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