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