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