1/* Output routines for Sunplus S+CORE processor
2   Copyright (C) 2005 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 2, 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 COPYING.  If not, write to
19   the Free Software Foundation, 51 Franklin Street, Fifth Floor,
20   Boston, MA 02110-1301, USA.  */
21
22#include "config.h"
23#include "system.h"
24#include "coretypes.h"
25#include "tm.h"
26#include <signal.h>
27#include "rtl.h"
28#include "regs.h"
29#include "hard-reg-set.h"
30#include "real.h"
31#include "insn-config.h"
32#include "conditions.h"
33#include "insn-attr.h"
34#include "recog.h"
35#include "toplev.h"
36#include "output.h"
37#include "tree.h"
38#include "function.h"
39#include "expr.h"
40#include "optabs.h"
41#include "flags.h"
42#include "reload.h"
43#include "tm_p.h"
44#include "ggc.h"
45#include "gstab.h"
46#include "hashtab.h"
47#include "debug.h"
48#include "target.h"
49#include "target-def.h"
50#include "integrate.h"
51#include "langhooks.h"
52#include "cfglayout.h"
53#include "score-mdaux.h"
54
55#define GR_REG_CLASS_P(C)        ((C) == G16_REGS || (C) == G32_REGS)
56#define SP_REG_CLASS_P(C) \
57  ((C) == CN_REG || (C) == LC_REG || (C) == SC_REG || (C) == SP_REGS)
58#define CP_REG_CLASS_P(C) \
59  ((C) == CP1_REGS || (C) == CP2_REGS || (C) == CP3_REGS || (C) == CPA_REGS)
60#define CE_REG_CLASS_P(C) \
61  ((C) == HI_REG || (C) == LO_REG || (C) == CE_REGS)
62
63static int score_arg_partial_bytes (const CUMULATIVE_ARGS *,
64                                    enum machine_mode, tree, int);
65
66static int score_symbol_insns (enum score_symbol_type);
67
68static int score_address_insns (rtx, enum machine_mode);
69
70static bool score_rtx_costs (rtx, enum rtx_code, enum rtx_code, int *);
71
72static int score_address_cost (rtx);
73
74#undef  TARGET_ASM_FILE_START
75#define TARGET_ASM_FILE_START           th_asm_file_start
76
77#undef  TARGET_ASM_FILE_END
78#define TARGET_ASM_FILE_END             th_asm_file_end
79
80#undef  TARGET_ASM_FUNCTION_PROLOGUE
81#define TARGET_ASM_FUNCTION_PROLOGUE    th_function_prologue
82
83#undef  TARGET_ASM_FUNCTION_EPILOGUE
84#define TARGET_ASM_FUNCTION_EPILOGUE    th_function_epilogue
85
86#undef  TARGET_SCHED_ISSUE_RATE
87#define TARGET_SCHED_ISSUE_RATE         th_issue_rate
88
89#undef TARGET_ASM_SELECT_RTX_SECTION
90#define TARGET_ASM_SELECT_RTX_SECTION   th_select_rtx_section
91
92#undef  TARGET_IN_SMALL_DATA_P
93#define TARGET_IN_SMALL_DATA_P          th_in_small_data_p
94
95#undef  TARGET_FUNCTION_OK_FOR_SIBCALL
96#define TARGET_FUNCTION_OK_FOR_SIBCALL  th_function_ok_for_sibcall
97
98#undef TARGET_STRICT_ARGUMENT_NAMING
99#define TARGET_STRICT_ARGUMENT_NAMING   th_strict_argument_naming
100
101#undef TARGET_ASM_OUTPUT_MI_THUNK
102#define TARGET_ASM_OUTPUT_MI_THUNK      th_output_mi_thunk
103
104#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
105#define TARGET_ASM_CAN_OUTPUT_MI_THUNK  hook_bool_tree_hwi_hwi_tree_true
106
107#undef TARGET_PROMOTE_FUNCTION_ARGS
108#define TARGET_PROMOTE_FUNCTION_ARGS    hook_bool_tree_true
109
110#undef TARGET_PROMOTE_FUNCTION_RETURN
111#define TARGET_PROMOTE_FUNCTION_RETURN  hook_bool_tree_true
112
113#undef TARGET_PROMOTE_PROTOTYPES
114#define TARGET_PROMOTE_PROTOTYPES       hook_bool_tree_true
115
116#undef TARGET_MUST_PASS_IN_STACK
117#define TARGET_MUST_PASS_IN_STACK       must_pass_in_stack_var_size
118
119#undef TARGET_ARG_PARTIAL_BYTES
120#define TARGET_ARG_PARTIAL_BYTES        score_arg_partial_bytes
121
122#undef TARGET_PASS_BY_REFERENCE
123#define TARGET_PASS_BY_REFERENCE        score_pass_by_reference
124
125#undef TARGET_RETURN_IN_MEMORY
126#define TARGET_RETURN_IN_MEMORY         score_return_in_memory
127
128#undef TARGET_RTX_COSTS
129#define TARGET_RTX_COSTS                score_rtx_costs
130
131#undef TARGET_ADDRESS_COST
132#define TARGET_ADDRESS_COST             score_address_cost
133
134#undef TARGET_DEFAULT_TARGET_FLAGS
135#define TARGET_DEFAULT_TARGET_FLAGS     TARGET_DEFAULT
136
137/* Implement TARGET_RETURN_IN_MEMORY.  In S+core,
138   small structures are returned in a register.
139   Objects with varying size must still be returned in memory.  */
140static bool
141score_return_in_memory (tree type, tree fndecl ATTRIBUTE_UNUSED)
142{
143  return ((TYPE_MODE (type) == BLKmode)
144          || (int_size_in_bytes (type) > 2 * UNITS_PER_WORD)
145          || (int_size_in_bytes (type) == -1));
146}
147
148/* Return nonzero when an argument must be passed by reference.  */
149static bool
150score_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
151                         enum machine_mode mode, tree type,
152                         bool named ATTRIBUTE_UNUSED)
153{
154  /* If we have a variable-sized parameter, we have no choice.  */
155  return targetm.calls.must_pass_in_stack (mode, type);
156}
157
158/* Return a legitimate address for REG + OFFSET.  */
159static rtx
160score_add_offset (rtx temp, rtx reg, HOST_WIDE_INT offset)
161{
162  if (!IMM_IN_RANGE (offset, 15, 1))
163    {
164      reg = expand_simple_binop (GET_MODE (reg), PLUS,
165                                 gen_int_mode (offset & 0xffffc000,
166                                               GET_MODE (reg)),
167                                 reg, NULL, 0, OPTAB_WIDEN);
168      offset &= 0x3fff;
169    }
170
171  return plus_constant (reg, offset);
172}
173
174/* Implement TARGET_ASM_OUTPUT_MI_THUNK.  Generate rtl rather than asm text
175   in order to avoid duplicating too much logic from elsewhere.  */
176static void
177th_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
178                    HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
179                    tree function)
180{
181  rtx this, temp1, temp2, insn, fnaddr;
182
183  /* Pretend to be a post-reload pass while generating rtl.  */
184  no_new_pseudos = 1;
185  reload_completed = 1;
186  reset_block_changes ();
187
188  /* We need two temporary registers in some cases.  */
189  temp1 = gen_rtx_REG (Pmode, 8);
190  temp2 = gen_rtx_REG (Pmode, 9);
191
192  /* Find out which register contains the "this" pointer.  */
193  if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
194    this = gen_rtx_REG (Pmode, ARG_REG_FIRST + 1);
195  else
196    this = gen_rtx_REG (Pmode, ARG_REG_FIRST);
197
198  /* Add DELTA to THIS.  */
199  if (delta != 0)
200    {
201      rtx offset = GEN_INT (delta);
202      if (!CONST_OK_FOR_LETTER_P (delta, 'L'))
203        {
204          emit_move_insn (temp1, offset);
205          offset = temp1;
206        }
207      emit_insn (gen_add3_insn (this, this, offset));
208    }
209
210  /* If needed, add *(*THIS + VCALL_OFFSET) to THIS.  */
211  if (vcall_offset != 0)
212    {
213      rtx addr;
214
215      /* Set TEMP1 to *THIS.  */
216      emit_move_insn (temp1, gen_rtx_MEM (Pmode, this));
217
218      /* Set ADDR to a legitimate address for *THIS + VCALL_OFFSET.  */
219      addr = score_add_offset (temp2, temp1, vcall_offset);
220
221      /* Load the offset and add it to THIS.  */
222      emit_move_insn (temp1, gen_rtx_MEM (Pmode, addr));
223      emit_insn (gen_add3_insn (this, this, temp1));
224    }
225
226  /* Jump to the target function.  */
227  fnaddr = XEXP (DECL_RTL (function), 0);
228  insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx));
229  SIBLING_CALL_P (insn) = 1;
230
231  /* Run just enough of rest_of_compilation.  This sequence was
232     "borrowed" from alpha.c.  */
233  insn = get_insns ();
234  insn_locators_initialize ();
235  split_all_insns_noflow ();
236  shorten_branches (insn);
237  final_start_function (insn, file, 1);
238  final (insn, file, 1);
239  final_end_function ();
240
241  /* Clean up the vars set above.  Note that final_end_function resets
242     the global pointer for us.  */
243  reload_completed = 0;
244  no_new_pseudos = 0;
245}
246
247/* Implement TARGET_STRICT_ARGUMENT_NAMING.  */
248static bool
249th_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
250{
251  return true;
252}
253
254/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL.  */
255static bool
256th_function_ok_for_sibcall (ATTRIBUTE_UNUSED tree decl,
257                            ATTRIBUTE_UNUSED tree exp)
258{
259  return true;
260}
261
262struct score_arg_info
263{
264  /* The argument's size, in bytes.  */
265  unsigned int num_bytes;
266
267  /* The number of words passed in registers, rounded up.  */
268  unsigned int reg_words;
269
270  /* The offset of the first register from GP_ARG_FIRST or FP_ARG_FIRST,
271     or ARG_REG_NUM if the argument is passed entirely on the stack.  */
272  unsigned int reg_offset;
273
274  /* The number of words that must be passed on the stack, rounded up.  */
275  unsigned int stack_words;
276
277  /* The offset from the start of the stack overflow area of the argument's
278     first stack word.  Only meaningful when STACK_WORDS is nonzero.  */
279  unsigned int stack_offset;
280};
281
282/* Fill INFO with information about a single argument.  CUM is the
283   cumulative state for earlier arguments.  MODE is the mode of this
284   argument and TYPE is its type (if known).  NAMED is true if this
285   is a named (fixed) argument rather than a variable one.  */
286static void
287classify_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
288              tree type, int named, struct score_arg_info *info)
289{
290  int even_reg_p;
291  unsigned int num_words, max_regs;
292
293  even_reg_p = 0;
294  if (GET_MODE_CLASS (mode) == MODE_INT
295      || GET_MODE_CLASS (mode) == MODE_FLOAT)
296    even_reg_p = (GET_MODE_SIZE (mode) > UNITS_PER_WORD);
297  else
298    if (type != NULL_TREE && TYPE_ALIGN (type) > BITS_PER_WORD && named)
299      even_reg_p = 1;
300
301  if (TARGET_MUST_PASS_IN_STACK (mode, type))
302    info->reg_offset = ARG_REG_NUM;
303  else
304    {
305      info->reg_offset = cum->num_gprs;
306      if (even_reg_p)
307        info->reg_offset += info->reg_offset & 1;
308    }
309
310  if (mode == BLKmode)
311    info->num_bytes = int_size_in_bytes (type);
312  else
313    info->num_bytes = GET_MODE_SIZE (mode);
314
315  num_words = (info->num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
316  max_regs = ARG_REG_NUM - info->reg_offset;
317
318  /* Partition the argument between registers and stack.  */
319  info->reg_words = MIN (num_words, max_regs);
320  info->stack_words = num_words - info->reg_words;
321
322  /* The alignment applied to registers is also applied to stack arguments.  */
323  if (info->stack_words)
324    {
325      info->stack_offset = cum->stack_words;
326      if (even_reg_p)
327        info->stack_offset += info->stack_offset & 1;
328    }
329}
330
331/* Set up the stack and frame (if desired) for the function.  */
332static void
333th_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
334{
335  const char *fnname;
336  struct score_frame_info *f = mda_cached_frame ();
337  HOST_WIDE_INT tsize = f->total_size;
338
339  fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
340  if (!flag_inhibit_size_directive)
341    {
342      fputs ("\t.ent\t", file);
343      assemble_name (file, fnname);
344      fputs ("\n", file);
345    }
346  assemble_name (file, fnname);
347  fputs (":\n", file);
348
349  if (!flag_inhibit_size_directive)
350    {
351      fprintf (file,
352               "\t.frame\t%s," HOST_WIDE_INT_PRINT_DEC ",%s, %d\t\t"
353               "# vars= " HOST_WIDE_INT_PRINT_DEC ", regs= %d"
354               ", args= " HOST_WIDE_INT_PRINT_DEC
355               ", gp= " HOST_WIDE_INT_PRINT_DEC "\n",
356               (reg_names[(frame_pointer_needed)
357                ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM]),
358               tsize,
359               reg_names[RA_REGNUM],
360               current_function_is_leaf ? 1 : 0,
361               f->var_size,
362               f->num_gp,
363               f->args_size,
364               f->cprestore_size);
365
366      fprintf(file, "\t.mask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
367              f->mask,
368              (f->gp_sp_offset - f->total_size));
369    }
370}
371
372/* Do any necessary cleanup after a function to restore stack, frame,
373   and regs.  */
374static void
375th_function_epilogue (FILE *file,
376                      HOST_WIDE_INT size ATTRIBUTE_UNUSED)
377{
378  if (!flag_inhibit_size_directive)
379    {
380      const char *fnname;
381      fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
382      fputs ("\t.end\t", file);
383      assemble_name (file, fnname);
384      fputs ("\n", file);
385    }
386}
387
388/* Implement TARGET_SCHED_ISSUE_RATE.  */
389static int
390th_issue_rate (void)
391{
392  return 1;
393}
394
395/* Returns true if X contains a SYMBOL_REF.  */
396static bool
397symbolic_expression_p (rtx x)
398{
399  if (GET_CODE (x) == SYMBOL_REF)
400    return true;
401
402  if (GET_CODE (x) == CONST)
403    return symbolic_expression_p (XEXP (x, 0));
404
405  if (UNARY_P (x))
406    return symbolic_expression_p (XEXP (x, 0));
407
408  if (ARITHMETIC_P (x))
409    return (symbolic_expression_p (XEXP (x, 0))
410            || symbolic_expression_p (XEXP (x, 1)));
411
412  return false;
413}
414
415/* Choose the section to use for the constant rtx expression X that has
416   mode MODE.  */
417static section *
418th_select_rtx_section (enum machine_mode mode, rtx x,
419                       unsigned HOST_WIDE_INT align)
420{
421  if (GET_MODE_SIZE (mode) <= SCORE_SDATA_MAX)
422    return get_named_section (0, ".sdata", 0);
423  else if (flag_pic && symbolic_expression_p (x))
424    return get_named_section (0, ".data.rel.ro", 3);
425  else
426    return mergeable_constant_section (mode, align, 0);
427}
428
429/* Implement TARGET_IN_SMALL_DATA_P.  */
430static bool
431th_in_small_data_p (tree decl)
432{
433  HOST_WIDE_INT size;
434
435  if (TREE_CODE (decl) == STRING_CST
436      || TREE_CODE (decl) == FUNCTION_DECL)
437    return false;
438
439  if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl) != 0)
440    {
441      const char *name;
442      name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
443      if (strcmp (name, ".sdata") != 0
444          && strcmp (name, ".sbss") != 0)
445        return true;
446      if (!DECL_EXTERNAL (decl))
447        return false;
448    }
449  size = int_size_in_bytes (TREE_TYPE (decl));
450  return (size > 0 && size <= SCORE_SDATA_MAX);
451}
452
453/* Implement TARGET_ASM_FILE_START.  */
454static void
455th_asm_file_start (void)
456{
457  default_file_start ();
458  fprintf (asm_out_file, ASM_COMMENT_START
459           "GCC for S+core %s \n", SCORE_GCC_VERSION);
460
461  if (flag_pic)
462    fprintf (asm_out_file, "\t.set pic\n");
463}
464
465/* Implement TARGET_ASM_FILE_END.  When using assembler macros, emit
466   .externs for any small-data variables that turned out to be external.  */
467struct extern_list *extern_head = 0;
468
469static void
470th_asm_file_end (void)
471{
472  tree name_tree;
473  struct extern_list *p;
474  if (extern_head)
475    {
476      fputs ("\n", asm_out_file);
477      for (p = extern_head; p != 0; p = p->next)
478        {
479          name_tree = get_identifier (p->name);
480          if (!TREE_ASM_WRITTEN (name_tree)
481              && TREE_SYMBOL_REFERENCED (name_tree))
482            {
483              TREE_ASM_WRITTEN (name_tree) = 1;
484              fputs ("\t.extern\t", asm_out_file);
485              assemble_name (asm_out_file, p->name);
486              fprintf (asm_out_file, ", %d\n", p->size);
487            }
488        }
489    }
490}
491
492static unsigned int sdata_max;
493
494int
495score_sdata_max (void)
496{
497  return sdata_max;
498}
499
500/* default 0 = NO_REGS  */
501enum reg_class score_char_to_class[256];
502
503/* Implement OVERRIDE_OPTIONS macro.  */
504void
505score_override_options (void)
506{
507  flag_pic = false;
508  if (!flag_pic)
509    sdata_max = g_switch_set ? g_switch_value : DEFAULT_SDATA_MAX;
510  else
511    {
512      sdata_max = 0;
513      if (g_switch_set && (g_switch_value != 0))
514        warning (0, "-fPIC and -G are incompatible");
515    }
516
517  score_char_to_class['d'] = G32_REGS;
518  score_char_to_class['e'] = G16_REGS;
519  score_char_to_class['t'] = T32_REGS;
520
521  score_char_to_class['h'] = HI_REG;
522  score_char_to_class['l'] = LO_REG;
523  score_char_to_class['x'] = CE_REGS;
524
525  score_char_to_class['q'] = CN_REG;
526  score_char_to_class['y'] = LC_REG;
527  score_char_to_class['z'] = SC_REG;
528  score_char_to_class['a'] = SP_REGS;
529
530  score_char_to_class['c'] = CR_REGS;
531
532  score_char_to_class['b'] = CP1_REGS;
533  score_char_to_class['f'] = CP2_REGS;
534  score_char_to_class['i'] = CP3_REGS;
535  score_char_to_class['j'] = CPA_REGS;
536}
537
538/* Implement REGNO_REG_CLASS macro.  */
539int
540score_reg_class (int regno)
541{
542  int c;
543  gcc_assert (regno >= 0 && regno < FIRST_PSEUDO_REGISTER);
544
545  if (regno == FRAME_POINTER_REGNUM
546      || regno == ARG_POINTER_REGNUM)
547    return ALL_REGS;
548
549  for (c = 0; c < N_REG_CLASSES; c++)
550    if (TEST_HARD_REG_BIT (reg_class_contents[c], regno))
551      return c;
552
553  return NO_REGS;
554}
555
556/* Implement PREFERRED_RELOAD_CLASS macro.  */
557enum reg_class
558score_preferred_reload_class (rtx x ATTRIBUTE_UNUSED, enum reg_class class)
559{
560  if (reg_class_subset_p (G16_REGS, class))
561    return G16_REGS;
562  if (reg_class_subset_p (G32_REGS, class))
563    return G32_REGS;
564  return class;
565}
566
567/* Implement SECONDARY_INPUT_RELOAD_CLASS
568   and SECONDARY_OUTPUT_RELOAD_CLASS macro.  */
569enum reg_class
570score_secondary_reload_class (enum reg_class class,
571                              enum machine_mode mode ATTRIBUTE_UNUSED,
572                              rtx x)
573{
574  int regno = -1;
575  if (GET_CODE (x) == REG || GET_CODE(x) == SUBREG)
576    regno = true_regnum (x);
577
578  if (!GR_REG_CLASS_P (class))
579    return GP_REG_P (regno) ? NO_REGS : G32_REGS;
580  return NO_REGS;
581}
582
583/* Implement CONST_OK_FOR_LETTER_P macro.  */
584/* imm constraints
585   I        imm16 << 16
586   J        uimm5
587   K        uimm16
588   L        simm16
589   M        uimm14
590   N        simm14  */
591int
592score_const_ok_for_letter_p (HOST_WIDE_INT value, char c)
593{
594  switch (c)
595    {
596    case 'I': return ((value & 0xffff) == 0);
597    case 'J': return IMM_IN_RANGE (value, 5, 0);
598    case 'K': return IMM_IN_RANGE (value, 16, 0);
599    case 'L': return IMM_IN_RANGE (value, 16, 1);
600    case 'M': return IMM_IN_RANGE (value, 14, 0);
601    case 'N': return IMM_IN_RANGE (value, 14, 1);
602    default : return 0;
603    }
604}
605
606/* Implement EXTRA_CONSTRAINT macro.  */
607/* Z        symbol_ref  */
608int
609score_extra_constraint (rtx op, char c)
610{
611  switch (c)
612    {
613    case 'Z':
614      return GET_CODE (op) == SYMBOL_REF;
615    default:
616      gcc_unreachable ();
617    }
618}
619
620/* Return truth value on whether or not a given hard register
621   can support a given mode.  */
622int
623score_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
624{
625  int size = GET_MODE_SIZE (mode);
626  enum mode_class class = GET_MODE_CLASS (mode);
627
628  if (class == MODE_CC)
629    return regno == CC_REGNUM;
630  else if (regno == FRAME_POINTER_REGNUM
631           || regno == ARG_POINTER_REGNUM)
632    return class == MODE_INT;
633  else if (GP_REG_P (regno))
634    /* ((regno <= (GP_REG_LAST- HARD_REGNO_NREGS (dummy, mode)) + 1)  */
635    return !(regno & 1) || (size <= UNITS_PER_WORD);
636  else if (CE_REG_P (regno))
637    return (class == MODE_INT
638            && ((size <= UNITS_PER_WORD)
639                || (regno == CE_REG_FIRST && size == 2 * UNITS_PER_WORD)));
640  else
641    return (class == MODE_INT) && (size <= UNITS_PER_WORD);
642}
643
644/* Implement INITIAL_ELIMINATION_OFFSET.  FROM is either the frame
645   pointer or argument pointer.  TO is either the stack pointer or
646   hard frame pointer.  */
647HOST_WIDE_INT
648score_initial_elimination_offset (int from,
649                                  int to ATTRIBUTE_UNUSED)
650{
651  struct score_frame_info *f = mda_compute_frame_size (get_frame_size ());
652  switch (from)
653    {
654    case ARG_POINTER_REGNUM:
655      return f->total_size;
656    case FRAME_POINTER_REGNUM:
657      return 0;
658    default:
659      gcc_unreachable ();
660    }
661}
662
663/* Argument support functions.  */
664
665/* Initialize CUMULATIVE_ARGS for a function.  */
666void
667score_init_cumulative_args (CUMULATIVE_ARGS *cum,
668                            tree fntype ATTRIBUTE_UNUSED,
669                            rtx libname ATTRIBUTE_UNUSED)
670{
671  memset (cum, 0, sizeof (CUMULATIVE_ARGS));
672}
673
674/* Implement FUNCTION_ARG_ADVANCE macro.  */
675void
676score_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
677                            tree type, int named)
678{
679  struct score_arg_info info;
680  classify_arg (cum, mode, type, named, &info);
681  cum->num_gprs = info.reg_offset + info.reg_words;
682  if (info.stack_words > 0)
683    cum->stack_words = info.stack_offset + info.stack_words;
684  cum->arg_number++;
685}
686
687/* Implement TARGET_ARG_PARTIAL_BYTES macro.  */
688static int
689score_arg_partial_bytes (const CUMULATIVE_ARGS *cum,
690                         enum machine_mode mode, tree type, int named)
691{
692  struct score_arg_info info;
693  classify_arg (cum, mode, type, named, &info);
694  return info.stack_words > 0 ? info.reg_words * UNITS_PER_WORD : 0;
695}
696
697/* Implement FUNCTION_ARG macro.  */
698rtx
699score_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
700                    tree type, int named)
701{
702  struct score_arg_info info;
703
704  if (mode == VOIDmode || !named)
705    return 0;
706
707  classify_arg (cum, mode, type, named, &info);
708
709  if (info.reg_offset == ARG_REG_NUM)
710    return 0;
711
712  if (!info.stack_words)
713    return gen_rtx_REG (mode, ARG_REG_FIRST + info.reg_offset);
714  else
715    {
716      rtx ret = gen_rtx_PARALLEL (mode, rtvec_alloc (info.reg_words));
717      unsigned int i, part_offset = 0;
718      for (i = 0; i < info.reg_words; i++)
719        {
720          rtx reg;
721          reg = gen_rtx_REG (SImode, ARG_REG_FIRST + info.reg_offset + i);
722          XVECEXP (ret, 0, i) = gen_rtx_EXPR_LIST (SImode, reg,
723                                                   GEN_INT (part_offset));
724          part_offset += UNITS_PER_WORD;
725        }
726      return ret;
727    }
728}
729
730/* Implement FUNCTION_VALUE and LIBCALL_VALUE.  For normal calls,
731   VALTYPE is the return type and MODE is VOIDmode.  For libcalls,
732   VALTYPE is null and MODE is the mode of the return value.  */
733rtx
734score_function_value (tree valtype, tree func ATTRIBUTE_UNUSED,
735                      enum machine_mode mode)
736{
737  if (valtype)
738    {
739      int unsignedp;
740      mode = TYPE_MODE (valtype);
741      unsignedp = TYPE_UNSIGNED (valtype);
742      mode = promote_mode (valtype, mode, &unsignedp, 1);
743    }
744  return gen_rtx_REG (mode, RT_REGNUM);
745}
746
747/* Implement INITIALIZE_TRAMPOLINE macro.  */
748void
749score_initialize_trampoline (rtx ADDR, rtx FUNC, rtx CHAIN)
750{
751#define FFCACHE          "_flush_cache"
752#define CODE_SIZE        (TRAMPOLINE_INSNS * UNITS_PER_WORD)
753
754  unsigned int tramp[TRAMPOLINE_INSNS] = {
755    0x8103bc56,                         /* mv      r8, r3          */
756    0x9000bc05,                         /* bl      0x0x8           */
757    0xc1238000 | (CODE_SIZE - 8),       /* lw      r9, &func       */
758    0xc0038000
759    | (STATIC_CHAIN_REGNUM << 21)
760    | (CODE_SIZE - 4),                  /* lw  static chain reg, &chain */
761    0x8068bc56,                         /* mv      r3, r8          */
762    0x8009bc08,                         /* br      r9              */
763    0x0,
764    0x0,
765    };
766  rtx pfunc, pchain;
767  int i;
768
769  for (i = 0; i < TRAMPOLINE_INSNS; i++)
770    emit_move_insn (gen_rtx_MEM (ptr_mode, plus_constant (ADDR, i << 2)),
771                    GEN_INT (tramp[i]));
772
773  pfunc = plus_constant (ADDR, CODE_SIZE);
774  pchain = plus_constant (ADDR, CODE_SIZE + GET_MODE_SIZE (ptr_mode));
775
776  emit_move_insn (gen_rtx_MEM (ptr_mode, pfunc), FUNC);
777  emit_move_insn (gen_rtx_MEM (ptr_mode, pchain), CHAIN);
778  emit_library_call (gen_rtx_SYMBOL_REF (Pmode, FFCACHE),
779                     0, VOIDmode, 2,
780                     ADDR, Pmode,
781                     GEN_INT (TRAMPOLINE_SIZE), SImode);
782#undef FFCACHE
783#undef CODE_SIZE
784}
785
786/* This function is used to implement REG_MODE_OK_FOR_BASE_P macro.  */
787int
788score_regno_mode_ok_for_base_p (int regno, int strict)
789{
790  if (regno >= FIRST_PSEUDO_REGISTER)
791    {
792      if (!strict)
793        return 1;
794      regno = reg_renumber[regno];
795    }
796  if (regno == ARG_POINTER_REGNUM
797      || regno == FRAME_POINTER_REGNUM)
798    return 1;
799  return GP_REG_P (regno);
800}
801
802/* Implement GO_IF_LEGITIMATE_ADDRESS macro.  */
803int
804score_address_p (enum machine_mode mode, rtx x, int strict)
805{
806  struct score_address_info addr;
807
808  return mda_classify_address (&addr, mode, x, strict);
809}
810
811/* Copy VALUE to a register and return that register.  If new psuedos
812   are allowed, copy it into a new register, otherwise use DEST.  */
813static rtx
814score_force_temporary (rtx dest, rtx value)
815{
816  if (!no_new_pseudos)
817    return force_reg (Pmode, value);
818  else
819    {
820      emit_move_insn (copy_rtx (dest), value);
821      return dest;
822    }
823}
824
825/* Return a LO_SUM expression for ADDR.  TEMP is as for score_force_temporary
826   and is used to load the high part into a register.  */
827static rtx
828score_split_symbol (rtx temp, rtx addr)
829{
830  rtx high = score_force_temporary (temp,
831                                    gen_rtx_HIGH (Pmode, copy_rtx (addr)));
832  return gen_rtx_LO_SUM (Pmode, high, addr);
833}
834
835/* This function is used to implement LEGITIMIZE_ADDRESS.  If *XLOC can
836   be legitimized in a way that the generic machinery might not expect,
837   put the new address in *XLOC and return true.  */
838int
839score_legitimize_address (rtx *xloc)
840{
841  enum score_symbol_type symbol_type;
842
843  if (mda_symbolic_constant_p (*xloc, &symbol_type)
844      && symbol_type == SYMBOL_GENERAL)
845    {
846      *xloc = score_split_symbol (0, *xloc);
847      return 1;
848    }
849
850  if (GET_CODE (*xloc) == PLUS
851      && GET_CODE (XEXP (*xloc, 1)) == CONST_INT)
852    {
853      rtx reg = XEXP (*xloc, 0);
854      if (!mda_valid_base_register_p (reg, 0))
855        reg = copy_to_mode_reg (Pmode, reg);
856      *xloc = score_add_offset (NULL, reg, INTVAL (XEXP (*xloc, 1)));
857      return 1;
858    }
859  return 0;
860}
861
862/* Return a number assessing the cost of moving a register in class
863   FROM to class TO. */
864int
865score_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
866                          enum reg_class from, enum reg_class to)
867{
868  if (GR_REG_CLASS_P (from))
869    {
870      if (GR_REG_CLASS_P (to))
871        return 2;
872      else if (SP_REG_CLASS_P (to))
873        return 4;
874      else if (CP_REG_CLASS_P (to))
875        return 5;
876      else if (CE_REG_CLASS_P (to))
877        return 6;
878    }
879  if (GR_REG_CLASS_P (to))
880    {
881      if (GR_REG_CLASS_P (from))
882        return 2;
883      else if (SP_REG_CLASS_P (from))
884        return 4;
885      else if (CP_REG_CLASS_P (from))
886        return 5;
887      else if (CE_REG_CLASS_P (from))
888        return 6;
889    }
890  return 12;
891}
892
893/* Return the number of instructions needed to load a symbol of the
894   given type into a register.  */
895static int
896score_symbol_insns (enum score_symbol_type type)
897{
898  switch (type)
899    {
900    case SYMBOL_GENERAL:
901      return 2;
902
903    case SYMBOL_SMALL_DATA:
904      return 1;
905    }
906
907  gcc_unreachable ();
908}
909
910/* Return the number of instructions needed to load or store a value
911   of mode MODE at X.  Return 0 if X isn't valid for MODE.  */
912static int
913score_address_insns (rtx x, enum machine_mode mode)
914{
915  struct score_address_info addr;
916  int factor;
917
918  if (mode == BLKmode)
919    factor = 1;
920  else
921    factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
922
923  if (mda_classify_address (&addr, mode, x, false))
924    switch (addr.type)
925      {
926      case ADD_REG:
927      case ADD_CONST_INT:
928        return factor;
929
930      case ADD_SYMBOLIC:
931        return factor * score_symbol_insns (addr.symbol_type);
932      }
933  return 0;
934}
935
936/* Implement TARGET_RTX_COSTS macro.  */
937static bool
938score_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
939                 int *total)
940{
941  enum machine_mode mode = GET_MODE (x);
942
943  switch (code)
944    {
945    case CONST_INT:
946      if (outer_code == SET)
947        {
948          if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
949              || CONST_OK_FOR_LETTER_P (INTVAL (x), 'L'))
950            *total = COSTS_N_INSNS (1);
951          else
952            *total = COSTS_N_INSNS (2);
953        }
954      else if (outer_code == PLUS || outer_code == MINUS)
955        {
956          if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'N'))
957            *total = 0;
958          else if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
959                   || CONST_OK_FOR_LETTER_P (INTVAL (x), 'L'))
960            *total = 1;
961          else
962            *total = COSTS_N_INSNS (2);
963        }
964      else if (outer_code == AND || outer_code == IOR)
965        {
966          if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'M'))
967            *total = 0;
968          else if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
969                   || CONST_OK_FOR_LETTER_P (INTVAL (x), 'K'))
970            *total = 1;
971          else
972            *total = COSTS_N_INSNS (2);
973        }
974      else
975        {
976          *total = 0;
977        }
978      return true;
979
980    case CONST:
981    case SYMBOL_REF:
982    case LABEL_REF:
983    case CONST_DOUBLE:
984      *total = COSTS_N_INSNS (2);
985      return true;
986
987    case MEM:
988      {
989        /* If the address is legitimate, return the number of
990           instructions it needs, otherwise use the default handling.  */
991        int n = score_address_insns (XEXP (x, 0), GET_MODE (x));
992        if (n > 0)
993          {
994            *total = COSTS_N_INSNS (n + 1);
995            return true;
996          }
997        return false;
998      }
999
1000    case FFS:
1001      *total = COSTS_N_INSNS (6);
1002      return true;
1003
1004    case NOT:
1005      *total = COSTS_N_INSNS (1);
1006      return true;
1007
1008    case AND:
1009    case IOR:
1010    case XOR:
1011      if (mode == DImode)
1012        {
1013          *total = COSTS_N_INSNS (2);
1014          return true;
1015        }
1016      return false;
1017
1018    case ASHIFT:
1019    case ASHIFTRT:
1020    case LSHIFTRT:
1021      if (mode == DImode)
1022        {
1023          *total = COSTS_N_INSNS ((GET_CODE (XEXP (x, 1)) == CONST_INT)
1024                                  ? 4 : 12);
1025          return true;
1026        }
1027      return false;
1028
1029    case ABS:
1030      *total = COSTS_N_INSNS (4);
1031      return true;
1032
1033    case PLUS:
1034    case MINUS:
1035      if (mode == DImode)
1036        {
1037          *total = COSTS_N_INSNS (4);
1038          return true;
1039        }
1040      *total = COSTS_N_INSNS (1);
1041      return true;
1042
1043    case NEG:
1044      if (mode == DImode)
1045        {
1046          *total = COSTS_N_INSNS (4);
1047          return true;
1048        }
1049      return false;
1050
1051    case MULT:
1052      *total = optimize_size ? COSTS_N_INSNS (2) : COSTS_N_INSNS (12);
1053      return true;
1054
1055    case DIV:
1056    case MOD:
1057    case UDIV:
1058    case UMOD:
1059      *total = optimize_size ? COSTS_N_INSNS (2) : COSTS_N_INSNS (33);
1060      return true;
1061
1062    case SIGN_EXTEND:
1063    case ZERO_EXTEND:
1064      switch (GET_MODE (XEXP (x, 0)))
1065        {
1066        case QImode:
1067        case HImode:
1068          if (GET_CODE (XEXP (x, 0)) == MEM)
1069            {
1070              *total = COSTS_N_INSNS (2);
1071
1072              if (!TARGET_LITTLE_ENDIAN &&
1073                  side_effects_p (XEXP (XEXP (x, 0), 0)))
1074                *total = 100;
1075            }
1076          else
1077            *total = COSTS_N_INSNS (1);
1078          break;
1079
1080        default:
1081          *total = COSTS_N_INSNS (1);
1082          break;
1083        }
1084      return true;
1085
1086    default:
1087      return false;
1088    }
1089}
1090
1091/* Implement TARGET_ADDRESS_COST macro.  */
1092int
1093score_address_cost (rtx addr)
1094{
1095  return score_address_insns (addr, SImode);
1096}
1097
1098/* Implement ASM_OUTPUT_EXTERNAL macro.  */
1099int
1100score_output_external (FILE *file ATTRIBUTE_UNUSED,
1101                       tree decl, const char *name)
1102{
1103  register struct extern_list *p;
1104
1105  if (th_in_small_data_p (decl))
1106    {
1107      p = (struct extern_list *) ggc_alloc (sizeof (struct extern_list));
1108      p->next = extern_head;
1109      p->name = name;
1110      p->size = int_size_in_bytes (TREE_TYPE (decl));
1111      extern_head = p;
1112    }
1113  return 0;
1114}
1115
1116/* Output format asm string.  */
1117void
1118score_declare_object (FILE *stream, const char *name,
1119                      const char *directive, const char *fmt, ...)
1120{
1121  va_list ap;
1122  fputs (directive, stream);
1123  assemble_name (stream, name);
1124  va_start (ap, fmt);
1125  vfprintf (stream, fmt, ap);
1126  va_end (ap);
1127}
1128
1129/* Implement RETURN_ADDR_RTX.  Note, we do not support moving
1130   back to a previous frame.  */
1131rtx
1132score_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
1133{
1134  if (count != 0)
1135    return const0_rtx;
1136  return get_hard_reg_initial_val (Pmode, RA_REGNUM);
1137}
1138
1139/* Implement PRINT_OPERAND macro.  */
1140/* Score-specific operand codes:
1141   '['        print .set nor1 directive
1142   ']'        print .set r1 directive
1143   'U'        print hi part of a CONST_INT rtx
1144   'E'        print log2(v)
1145   'F'        print log2(~v)
1146   'D'        print SFmode const double
1147   'S'        selectively print "!" if operand is 15bit instruction accessible
1148   'V'        print "v!" if operand is 15bit instruction accessible, or "lfh!"
1149   'L'        low  part of DImode reg operand
1150   'H'        high part of DImode reg operand
1151   'C'        print part of opcode for a branch condition.  */
1152void
1153score_print_operand (FILE *file, rtx op, int c)
1154{
1155  enum rtx_code code = -1;
1156  if (!PRINT_OPERAND_PUNCT_VALID_P (c))
1157    code = GET_CODE (op);
1158
1159  if (c == '[')
1160    {
1161      fprintf (file, ".set r1\n");
1162    }
1163  else if (c == ']')
1164    {
1165      fprintf (file, "\n\t.set nor1");
1166    }
1167  else if (c == 'U')
1168    {
1169      gcc_assert (code == CONST_INT);
1170      fprintf (file, HOST_WIDE_INT_PRINT_HEX,
1171               (INTVAL (op) >> 16) & 0xffff);
1172    }
1173  else if (c == 'D')
1174    {
1175      if (GET_CODE (op) == CONST_DOUBLE)
1176        {
1177          rtx temp = gen_lowpart (SImode, op);
1178          gcc_assert (GET_MODE (op) == SFmode);
1179          fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (temp) & 0xffffffff);
1180        }
1181      else
1182        output_addr_const (file, op);
1183    }
1184  else if (c == 'S')
1185    {
1186      gcc_assert (code == REG);
1187      if (G16_REG_P (REGNO (op)))
1188        fprintf (file, "!");
1189    }
1190  else if (c == 'V')
1191    {
1192      gcc_assert (code == REG);
1193      fprintf (file, G16_REG_P (REGNO (op)) ? "v!" : "lfh!");
1194    }
1195  else if (c == 'C')
1196    {
1197      enum machine_mode mode = GET_MODE (XEXP (op, 0));
1198
1199      switch (code)
1200        {
1201        case EQ: fputs ("eq", file); break;
1202        case NE: fputs ("ne", file); break;
1203        case GT: fputs ("gt", file); break;
1204        case GE: fputs (mode != CCmode ? "pl" : "ge", file); break;
1205        case LT: fputs (mode != CCmode ? "mi" : "lt", file); break;
1206        case LE: fputs ("le", file); break;
1207        case GTU: fputs ("gtu", file); break;
1208        case GEU: fputs ("cs", file); break;
1209        case LTU: fputs ("cc", file); break;
1210        case LEU: fputs ("leu", file); break;
1211        default:
1212          output_operand_lossage ("invalid operand for code: '%c'", code);
1213        }
1214    }
1215  else if (c == 'E')
1216    {
1217      unsigned HOST_WIDE_INT i;
1218      unsigned HOST_WIDE_INT pow2mask = 1;
1219      unsigned HOST_WIDE_INT val;
1220
1221      val = INTVAL (op);
1222      for (i = 0; i < 32; i++)
1223        {
1224          if (val == pow2mask)
1225            break;
1226          pow2mask <<= 1;
1227        }
1228      gcc_assert (i < 32);
1229      fprintf (file, HOST_WIDE_INT_PRINT_HEX, i);
1230    }
1231  else if (c == 'F')
1232    {
1233      unsigned HOST_WIDE_INT i;
1234      unsigned HOST_WIDE_INT pow2mask = 1;
1235      unsigned HOST_WIDE_INT val;
1236
1237      val = ~INTVAL (op);
1238      for (i = 0; i < 32; i++)
1239        {
1240          if (val == pow2mask)
1241            break;
1242          pow2mask <<= 1;
1243        }
1244      gcc_assert (i < 32);
1245      fprintf (file, HOST_WIDE_INT_PRINT_HEX, i);
1246    }
1247  else if (code == REG)
1248    {
1249      int regnum = REGNO (op);
1250      if ((c == 'H' && !WORDS_BIG_ENDIAN)
1251          || (c == 'L' && WORDS_BIG_ENDIAN))
1252        regnum ++;
1253      fprintf (file, "%s", reg_names[regnum]);
1254    }
1255  else
1256    {
1257      switch (code)
1258        {
1259        case MEM:
1260          score_print_operand_address (file, op);
1261          break;
1262        default:
1263          output_addr_const (file, op);
1264        }
1265    }
1266}
1267
1268/* Implement PRINT_OPERAND_ADDRESS macro.  */
1269void
1270score_print_operand_address (FILE *file, rtx x)
1271{
1272  struct score_address_info addr;
1273  enum rtx_code code = GET_CODE (x);
1274  enum machine_mode mode = GET_MODE (x);
1275
1276  if (code == MEM)
1277    x = XEXP (x, 0);
1278
1279  if (mda_classify_address (&addr, mode, x, true))
1280    {
1281      switch (addr.type)
1282        {
1283        case ADD_REG:
1284          {
1285            switch (addr.code)
1286              {
1287              case PRE_DEC:
1288                fprintf (file, "[%s,-%ld]+", reg_names[REGNO (addr.reg)],
1289                         INTVAL (addr.offset));
1290                break;
1291              case POST_DEC:
1292                fprintf (file, "[%s]+,-%ld", reg_names[REGNO (addr.reg)],
1293                         INTVAL (addr.offset));
1294                break;
1295              case PRE_INC:
1296                fprintf (file, "[%s, %ld]+", reg_names[REGNO (addr.reg)],
1297                         INTVAL (addr.offset));
1298                break;
1299              case POST_INC:
1300                fprintf (file, "[%s]+, %ld", reg_names[REGNO (addr.reg)],
1301                         INTVAL (addr.offset));
1302                break;
1303              default:
1304                fprintf (file, "[%s,%ld]", reg_names[REGNO (addr.reg)],
1305                         INTVAL (addr.offset));
1306                break;
1307              }
1308          }
1309          return;
1310        case ADD_CONST_INT:
1311        case ADD_SYMBOLIC:
1312          output_addr_const (file, x);
1313          return;
1314        }
1315    }
1316  print_rtl (stderr, x);
1317  gcc_unreachable ();
1318}
1319
1320/* Implement SELECT_CC_MODE macro.  */
1321enum machine_mode
1322score_select_cc_mode (enum rtx_code op, rtx x, rtx y)
1323{
1324  if ((op == EQ || op == NE || op == LT || op == GE)
1325      && y == const0_rtx
1326      && GET_MODE (x) == SImode)
1327    {
1328      switch (GET_CODE (x))
1329        {
1330        case PLUS:
1331        case MINUS:
1332        case NEG:
1333        case AND:
1334        case IOR:
1335        case XOR:
1336        case NOT:
1337        case ASHIFT:
1338        case LSHIFTRT:
1339        case ASHIFTRT:
1340          return CC_NZmode;
1341
1342        case SIGN_EXTEND:
1343        case ZERO_EXTEND:
1344        case ROTATE:
1345        case ROTATERT:
1346          return (op == LT || op == GE) ? CC_Nmode : CCmode;
1347
1348        default:
1349          return CCmode;
1350        }
1351    }
1352
1353  if ((op == EQ || op == NE)
1354      && (GET_CODE (y) == NEG)
1355      && register_operand (XEXP (y, 0), SImode)
1356      && register_operand (x, SImode))
1357    {
1358      return CC_NZmode;
1359    }
1360
1361  return CCmode;
1362}
1363
1364struct gcc_target targetm = TARGET_INITIALIZER;
1365