111
|
1 /* Subroutines used for code generation on the EPIPHANY cpu.
|
|
2 Copyright (C) 1994-2017 Free Software Foundation, Inc.
|
|
3 Contributed by Embecosm on behalf of Adapteva, Inc.
|
|
4
|
|
5 This file is part of GCC.
|
|
6
|
|
7 GCC is free software; you can redistribute it and/or modify
|
|
8 it under the terms of the GNU General Public License as published by
|
|
9 the Free Software Foundation; either version 3, or (at your option)
|
|
10 any later version.
|
|
11
|
|
12 GCC is distributed in the hope that it will be useful,
|
|
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15 GNU General Public 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 "backend.h"
|
|
25 #include "target.h"
|
|
26 #include "rtl.h"
|
|
27 #include "tree.h"
|
|
28 #include "df.h"
|
|
29 #include "memmodel.h"
|
|
30 #include "tm_p.h"
|
|
31 #include "stringpool.h"
|
|
32 #include "attribs.h"
|
|
33 #include "optabs.h"
|
|
34 #include "emit-rtl.h"
|
|
35 #include "recog.h"
|
|
36 #include "diagnostic-core.h"
|
|
37 #include "alias.h"
|
|
38 #include "stor-layout.h"
|
|
39 #include "varasm.h"
|
|
40 #include "calls.h"
|
|
41 #include "output.h"
|
|
42 #include "insn-attr.h"
|
|
43 #include "explow.h"
|
|
44 #include "expr.h"
|
|
45 #include "tm-constrs.h"
|
|
46 #include "tree-pass.h" /* for current_pass */
|
|
47 #include "context.h"
|
|
48 #include "pass_manager.h"
|
|
49 #include "builtins.h"
|
|
50
|
|
51 /* Which cpu we're compiling for. */
|
|
52 int epiphany_cpu_type;
|
|
53
|
|
54 /* Name of mangle string to add to symbols to separate code compiled for each
|
|
55 cpu (or NULL). */
|
|
56 const char *epiphany_mangle_cpu;
|
|
57
|
|
58 /* Array of valid operand punctuation characters. */
|
|
59 char epiphany_punct_chars[256];
|
|
60
|
|
61 /* The rounding mode that we generally use for floating point. */
|
|
62 int epiphany_normal_fp_rounding;
|
|
63
|
|
64 /* The pass instance, for use in epiphany_optimize_mode_switching. */
|
|
65 static opt_pass *pass_mode_switch_use;
|
|
66
|
|
67 static void epiphany_init_reg_tables (void);
|
|
68 static int get_epiphany_condition_code (rtx);
|
|
69 static tree epiphany_handle_interrupt_attribute (tree *, tree, tree, int, bool *);
|
|
70 static tree epiphany_handle_forwarder_attribute (tree *, tree, tree, int,
|
|
71 bool *);
|
|
72 static bool epiphany_pass_by_reference (cumulative_args_t, machine_mode,
|
|
73 const_tree, bool);
|
|
74 static rtx_insn *frame_insn (rtx);
|
|
75
|
|
76 /* defines for the initialization of the GCC target structure. */
|
|
77 #define TARGET_ATTRIBUTE_TABLE epiphany_attribute_table
|
|
78
|
|
79 #define TARGET_PRINT_OPERAND epiphany_print_operand
|
|
80 #define TARGET_PRINT_OPERAND_ADDRESS epiphany_print_operand_address
|
|
81
|
|
82 #define TARGET_RTX_COSTS epiphany_rtx_costs
|
|
83 #define TARGET_ADDRESS_COST epiphany_address_cost
|
|
84 #define TARGET_MEMORY_MOVE_COST epiphany_memory_move_cost
|
|
85
|
|
86 #define TARGET_PROMOTE_FUNCTION_MODE epiphany_promote_function_mode
|
|
87 #define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
|
|
88
|
|
89 #define TARGET_RETURN_IN_MEMORY epiphany_return_in_memory
|
|
90 #define TARGET_PASS_BY_REFERENCE epiphany_pass_by_reference
|
|
91 #define TARGET_CALLEE_COPIES hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true
|
|
92 #define TARGET_FUNCTION_VALUE epiphany_function_value
|
|
93 #define TARGET_LIBCALL_VALUE epiphany_libcall_value
|
|
94 #define TARGET_FUNCTION_VALUE_REGNO_P epiphany_function_value_regno_p
|
|
95
|
|
96 #define TARGET_SETUP_INCOMING_VARARGS epiphany_setup_incoming_varargs
|
|
97
|
|
98 /* Using the simplistic varags handling forces us to do partial reg/stack
|
|
99 argument passing for types with larger size (> 4 bytes) than alignment. */
|
|
100 #define TARGET_ARG_PARTIAL_BYTES epiphany_arg_partial_bytes
|
|
101
|
|
102 #define TARGET_FUNCTION_OK_FOR_SIBCALL epiphany_function_ok_for_sibcall
|
|
103
|
|
104 #define TARGET_SCHED_ISSUE_RATE epiphany_issue_rate
|
|
105 #define TARGET_SCHED_ADJUST_COST epiphany_adjust_cost
|
|
106
|
|
107 #define TARGET_LRA_P hook_bool_void_false
|
|
108
|
|
109 #define TARGET_LEGITIMATE_ADDRESS_P epiphany_legitimate_address_p
|
|
110
|
|
111 #define TARGET_SECONDARY_RELOAD epiphany_secondary_reload
|
|
112
|
|
113 #define TARGET_OPTION_OVERRIDE epiphany_override_options
|
|
114
|
|
115 #define TARGET_CONDITIONAL_REGISTER_USAGE epiphany_conditional_register_usage
|
|
116
|
|
117 #define TARGET_FUNCTION_ARG epiphany_function_arg
|
|
118
|
|
119 #define TARGET_FUNCTION_ARG_ADVANCE epiphany_function_arg_advance
|
|
120
|
|
121 #define TARGET_FUNCTION_ARG_BOUNDARY epiphany_function_arg_boundary
|
|
122
|
|
123 #define TARGET_TRAMPOLINE_INIT epiphany_trampoline_init
|
|
124
|
|
125 /* Nonzero if the constant rtx value is a legitimate general operand.
|
|
126 We can handle any 32- or 64-bit constant. */
|
|
127 #define TARGET_LEGITIMATE_CONSTANT_P hook_bool_mode_rtx_true
|
|
128
|
|
129 #define TARGET_MIN_DIVISIONS_FOR_RECIP_MUL \
|
|
130 epiphany_min_divisions_for_recip_mul
|
|
131
|
|
132 #define TARGET_VECTORIZE_PREFERRED_SIMD_MODE epiphany_preferred_simd_mode
|
|
133
|
|
134 #define TARGET_VECTOR_MODE_SUPPORTED_P epiphany_vector_mode_supported_p
|
|
135
|
|
136 #define TARGET_VECTORIZE_VECTOR_ALIGNMENT_REACHABLE \
|
|
137 epiphany_vector_alignment_reachable
|
|
138
|
|
139 #define TARGET_VECTORIZE_SUPPORT_VECTOR_MISALIGNMENT \
|
|
140 epiphany_support_vector_misalignment
|
|
141
|
|
142 #define TARGET_ASM_CAN_OUTPUT_MI_THUNK \
|
|
143 hook_bool_const_tree_hwi_hwi_const_tree_true
|
|
144 #define TARGET_ASM_OUTPUT_MI_THUNK epiphany_output_mi_thunk
|
|
145
|
|
146 /* ??? we can use larger offsets for wider-mode sized accesses, but there
|
|
147 is no concept of anchors being dependent on the modes that they are used
|
|
148 for, so we can only use an offset range that would suit all modes. */
|
|
149 #define TARGET_MAX_ANCHOR_OFFSET (optimize_size ? 31 : 2047)
|
|
150 /* We further restrict the minimum to be a multiple of eight. */
|
|
151 #define TARGET_MIN_ANCHOR_OFFSET (optimize_size ? 0 : -2040)
|
|
152
|
|
153 /* Mode switching hooks. */
|
|
154
|
|
155 #define TARGET_MODE_EMIT emit_set_fp_mode
|
|
156
|
|
157 #define TARGET_MODE_NEEDED epiphany_mode_needed
|
|
158
|
|
159 #define TARGET_MODE_PRIORITY epiphany_mode_priority
|
|
160
|
|
161 #define TARGET_MODE_ENTRY epiphany_mode_entry
|
|
162
|
|
163 #define TARGET_MODE_EXIT epiphany_mode_exit
|
|
164
|
|
165 #define TARGET_MODE_AFTER epiphany_mode_after
|
|
166
|
|
167 #include "target-def.h"
|
|
168
|
|
169 #undef TARGET_ASM_ALIGNED_HI_OP
|
|
170 #define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
|
|
171 #undef TARGET_ASM_ALIGNED_SI_OP
|
|
172 #define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
|
|
173
|
|
174 #undef TARGET_HARD_REGNO_MODE_OK
|
|
175 #define TARGET_HARD_REGNO_MODE_OK epiphany_hard_regno_mode_ok
|
|
176
|
|
177 #undef TARGET_CONSTANT_ALIGNMENT
|
|
178 #define TARGET_CONSTANT_ALIGNMENT epiphany_constant_alignment
|
|
179
|
|
180 #undef TARGET_STARTING_FRAME_OFFSET
|
|
181 #define TARGET_STARTING_FRAME_OFFSET epiphany_starting_frame_offset
|
|
182
|
|
183 bool
|
|
184 epiphany_is_interrupt_p (tree decl)
|
|
185 {
|
|
186 tree attrs;
|
|
187
|
|
188 attrs = DECL_ATTRIBUTES (decl);
|
|
189 if (lookup_attribute ("interrupt", attrs))
|
|
190 return true;
|
|
191 else
|
|
192 return false;
|
|
193 }
|
|
194
|
|
195 /* Called from epiphany_override_options.
|
|
196 We use this to initialize various things. */
|
|
197
|
|
198 static void
|
|
199 epiphany_init (void)
|
|
200 {
|
|
201 /* N.B. this pass must not run before the first optimize_mode_switching
|
|
202 pass because of the side offect of epiphany_mode_needed on
|
|
203 MACHINE_FUNCTION(cfun)->unknown_mode_uses. But it must run before
|
|
204 pass_resolve_sw_modes. */
|
|
205 pass_mode_switch_use = make_pass_mode_switch_use (g);
|
|
206 struct register_pass_info insert_use_info
|
|
207 = { pass_mode_switch_use, "mode_sw",
|
|
208 1, PASS_POS_INSERT_AFTER
|
|
209 };
|
|
210 opt_pass *mode_sw2
|
|
211 = g->get_passes()->get_pass_mode_switching ()->clone ();
|
|
212 struct register_pass_info mode_sw2_info
|
|
213 = { mode_sw2, "mode_sw",
|
|
214 1, PASS_POS_INSERT_AFTER
|
|
215 };
|
|
216 opt_pass *mode_sw3 = make_pass_resolve_sw_modes (g);
|
|
217 struct register_pass_info mode_sw3_info
|
|
218 = { mode_sw3, "mode_sw",
|
|
219 1, PASS_POS_INSERT_AFTER
|
|
220 };
|
|
221 opt_pass *mode_sw4
|
|
222 = g->get_passes()->get_pass_split_all_insns ()->clone ();
|
|
223 struct register_pass_info mode_sw4_info
|
|
224 = { mode_sw4, "mode_sw",
|
|
225 1, PASS_POS_INSERT_AFTER
|
|
226 };
|
|
227 static const int num_modes[] = NUM_MODES_FOR_MODE_SWITCHING;
|
|
228 #define N_ENTITIES ARRAY_SIZE (num_modes)
|
|
229
|
|
230 epiphany_init_reg_tables ();
|
|
231
|
|
232 /* Initialize array for PRINT_OPERAND_PUNCT_VALID_P. */
|
|
233 memset (epiphany_punct_chars, 0, sizeof (epiphany_punct_chars));
|
|
234 epiphany_punct_chars['-'] = 1;
|
|
235
|
|
236 epiphany_normal_fp_rounding
|
|
237 = (epiphany_normal_fp_mode == FP_MODE_ROUND_TRUNC
|
|
238 ? FP_MODE_ROUND_TRUNC : FP_MODE_ROUND_NEAREST);
|
|
239 register_pass (&mode_sw4_info);
|
|
240 register_pass (&mode_sw2_info);
|
|
241 register_pass (&mode_sw3_info);
|
|
242 register_pass (&insert_use_info);
|
|
243 register_pass (&mode_sw2_info);
|
|
244 /* Verify that NUM_MODES_FOR_MODE_SWITCHING has one value per entity. */
|
|
245 gcc_assert (N_ENTITIES == EPIPHANY_MSW_ENTITY_NUM);
|
|
246
|
|
247 #if 1 /* As long as peep2_rescan is not implemented,
|
|
248 (see http://gcc.gnu.org/ml/gcc-patches/2011-10/msg02819.html,)
|
|
249 we need a second peephole2 pass to get reasonable code. */
|
|
250 {
|
|
251 opt_pass *extra_peephole2
|
|
252 = g->get_passes ()->get_pass_peephole2 ()->clone ();
|
|
253 struct register_pass_info peep2_2_info
|
|
254 = { extra_peephole2, "peephole2",
|
|
255 1, PASS_POS_INSERT_AFTER
|
|
256 };
|
|
257
|
|
258 register_pass (&peep2_2_info);
|
|
259 }
|
|
260 #endif
|
|
261 }
|
|
262
|
|
263 /* The condition codes of the EPIPHANY, and the inverse function. */
|
|
264 static const char *const epiphany_condition_codes[] =
|
|
265 { /* 0 1 2 3 4 5 6 7 8 9 */
|
|
266 "eq", "ne", "ltu", "gteu", "gt", "lte", "gte", "lt", "gtu", "lteu",
|
|
267 /* 10 11 12 13 */
|
|
268 "beq","bne","blt", "blte",
|
|
269 };
|
|
270
|
|
271 #define EPIPHANY_INVERSE_CONDITION_CODE(X) ((X) ^ 1)
|
|
272
|
|
273 /* Returns the index of the EPIPHANY condition code string in
|
|
274 `epiphany_condition_codes'. COMPARISON should be an rtx like
|
|
275 `(eq (...) (...))'. */
|
|
276
|
|
277 static int
|
|
278 get_epiphany_condition_code (rtx comparison)
|
|
279 {
|
|
280 switch (GET_MODE (XEXP (comparison, 0)))
|
|
281 {
|
|
282 case E_CCmode:
|
|
283 switch (GET_CODE (comparison))
|
|
284 {
|
|
285 case EQ : return 0;
|
|
286 case NE : return 1;
|
|
287 case LTU : return 2;
|
|
288 case GEU : return 3;
|
|
289 case GT : return 4;
|
|
290 case LE : return 5;
|
|
291 case GE : return 6;
|
|
292 case LT : return 7;
|
|
293 case GTU : return 8;
|
|
294 case LEU : return 9;
|
|
295
|
|
296 default : gcc_unreachable ();
|
|
297 }
|
|
298 case E_CC_N_NEmode:
|
|
299 switch (GET_CODE (comparison))
|
|
300 {
|
|
301 case EQ: return 6;
|
|
302 case NE: return 7;
|
|
303 default: gcc_unreachable ();
|
|
304 }
|
|
305 case E_CC_C_LTUmode:
|
|
306 switch (GET_CODE (comparison))
|
|
307 {
|
|
308 case GEU: return 2;
|
|
309 case LTU: return 3;
|
|
310 default: gcc_unreachable ();
|
|
311 }
|
|
312 case E_CC_C_GTUmode:
|
|
313 switch (GET_CODE (comparison))
|
|
314 {
|
|
315 case LEU: return 3;
|
|
316 case GTU: return 2;
|
|
317 default: gcc_unreachable ();
|
|
318 }
|
|
319 case E_CC_FPmode:
|
|
320 switch (GET_CODE (comparison))
|
|
321 {
|
|
322 case EQ: return 10;
|
|
323 case NE: return 11;
|
|
324 case LT: return 12;
|
|
325 case LE: return 13;
|
|
326 default: gcc_unreachable ();
|
|
327 }
|
|
328 case E_CC_FP_EQmode:
|
|
329 switch (GET_CODE (comparison))
|
|
330 {
|
|
331 case EQ: return 0;
|
|
332 case NE: return 1;
|
|
333 default: gcc_unreachable ();
|
|
334 }
|
|
335 case E_CC_FP_GTEmode:
|
|
336 switch (GET_CODE (comparison))
|
|
337 {
|
|
338 case EQ: return 0;
|
|
339 case NE: return 1;
|
|
340 case GT : return 4;
|
|
341 case GE : return 6;
|
|
342 case UNLE : return 5;
|
|
343 case UNLT : return 7;
|
|
344 default: gcc_unreachable ();
|
|
345 }
|
|
346 case E_CC_FP_ORDmode:
|
|
347 switch (GET_CODE (comparison))
|
|
348 {
|
|
349 case ORDERED: return 9;
|
|
350 case UNORDERED: return 8;
|
|
351 default: gcc_unreachable ();
|
|
352 }
|
|
353 case E_CC_FP_UNEQmode:
|
|
354 switch (GET_CODE (comparison))
|
|
355 {
|
|
356 case UNEQ: return 9;
|
|
357 case LTGT: return 8;
|
|
358 default: gcc_unreachable ();
|
|
359 }
|
|
360 default: gcc_unreachable ();
|
|
361 }
|
|
362 /*NOTREACHED*/
|
|
363 return (42);
|
|
364 }
|
|
365
|
|
366
|
|
367 /* Implement TARGET_HARD_REGNO_MODE_OK. */
|
|
368
|
|
369 static bool
|
|
370 epiphany_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
|
|
371 {
|
|
372 if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
|
|
373 return (regno & 1) == 0 && GPR_P (regno);
|
|
374 else
|
|
375 return true;
|
|
376 }
|
|
377
|
|
378 /* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE,
|
|
379 return the mode to be used for the comparison. */
|
|
380
|
|
381 machine_mode
|
|
382 epiphany_select_cc_mode (enum rtx_code op,
|
|
383 rtx x ATTRIBUTE_UNUSED,
|
|
384 rtx y ATTRIBUTE_UNUSED)
|
|
385 {
|
|
386 if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
|
|
387 {
|
|
388 if (TARGET_SOFT_CMPSF
|
|
389 || op == ORDERED || op == UNORDERED)
|
|
390 {
|
|
391 if (op == EQ || op == NE)
|
|
392 return CC_FP_EQmode;
|
|
393 if (op == ORDERED || op == UNORDERED)
|
|
394 return CC_FP_ORDmode;
|
|
395 if (op == UNEQ || op == LTGT)
|
|
396 return CC_FP_UNEQmode;
|
|
397 return CC_FP_GTEmode;
|
|
398 }
|
|
399 return CC_FPmode;
|
|
400 }
|
|
401 /* recognize combiner pattern ashlsi_btst:
|
|
402 (parallel [
|
|
403 (set (reg:N_NE 65 cc1)
|
|
404 (compare:N_NE (zero_extract:SI (reg/v:SI 75 [ a ])
|
|
405 (const_int 1 [0x1])
|
|
406 (const_int 0 [0x0]))
|
|
407 (const_int 0 [0x0])))
|
|
408 (clobber (scratch:SI)) */
|
|
409 else if ((op == EQ || op == NE)
|
|
410 && GET_CODE (x) == ZERO_EXTRACT
|
|
411 && XEXP (x, 1) == const1_rtx
|
|
412 && CONST_INT_P (XEXP (x, 2)))
|
|
413 return CC_N_NEmode;
|
|
414 else if ((op == GEU || op == LTU) && GET_CODE (x) == PLUS)
|
|
415 return CC_C_LTUmode;
|
|
416 else if ((op == LEU || op == GTU) && GET_CODE (x) == MINUS)
|
|
417 return CC_C_GTUmode;
|
|
418 else
|
|
419 return CCmode;
|
|
420 }
|
|
421
|
|
422 enum reg_class epiphany_regno_reg_class[FIRST_PSEUDO_REGISTER];
|
|
423
|
|
424 static void
|
|
425 epiphany_init_reg_tables (void)
|
|
426 {
|
|
427 int i;
|
|
428
|
|
429 for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
|
430 {
|
|
431 if (i == GPR_LR)
|
|
432 epiphany_regno_reg_class[i] = LR_REGS;
|
|
433 else if (i <= 7 && TARGET_PREFER_SHORT_INSN_REGS)
|
|
434 epiphany_regno_reg_class[i] = SHORT_INSN_REGS;
|
|
435 else if (call_used_regs[i]
|
|
436 && TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], i))
|
|
437 epiphany_regno_reg_class[i] = SIBCALL_REGS;
|
|
438 else if (i >= CORE_CONTROL_FIRST && i <= CORE_CONTROL_LAST)
|
|
439 epiphany_regno_reg_class[i] = CORE_CONTROL_REGS;
|
|
440 else if (i < (GPR_LAST+1)
|
|
441 || i == ARG_POINTER_REGNUM || i == FRAME_POINTER_REGNUM)
|
|
442 epiphany_regno_reg_class[i] = GENERAL_REGS;
|
|
443 else if (i == CC_REGNUM)
|
|
444 epiphany_regno_reg_class[i] = NO_REGS /* CC_REG: must be NO_REGS */;
|
|
445 else
|
|
446 epiphany_regno_reg_class[i] = NO_REGS;
|
|
447 }
|
|
448 }
|
|
449
|
|
450 /* EPIPHANY specific attribute support.
|
|
451
|
|
452 The EPIPHANY has these attributes:
|
|
453 interrupt - for interrupt functions.
|
|
454 short_call - the function is assumed to be reachable with the b / bl
|
|
455 instructions.
|
|
456 long_call - the function address is loaded into a register before use.
|
|
457 disinterrupt - functions which mask interrupts throughout.
|
|
458 They unmask them while calling an interruptible
|
|
459 function, though. */
|
|
460
|
|
461 static const struct attribute_spec epiphany_attribute_table[] =
|
|
462 {
|
|
463 /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
|
|
464 { "interrupt", 0, 9, true, false, false, epiphany_handle_interrupt_attribute, true },
|
|
465 { "forwarder_section", 1, 1, true, false, false, epiphany_handle_forwarder_attribute, false },
|
|
466 { "long_call", 0, 0, false, true, true, NULL, false },
|
|
467 { "short_call", 0, 0, false, true, true, NULL, false },
|
|
468 { "disinterrupt", 0, 0, false, true, true, NULL, true },
|
|
469 { NULL, 0, 0, false, false, false, NULL, false }
|
|
470 };
|
|
471
|
|
472 /* Handle an "interrupt" attribute; arguments as in
|
|
473 struct attribute_spec.handler. */
|
|
474 static tree
|
|
475 epiphany_handle_interrupt_attribute (tree *node, tree name, tree args,
|
|
476 int flags ATTRIBUTE_UNUSED,
|
|
477 bool *no_add_attrs)
|
|
478 {
|
|
479 tree value;
|
|
480
|
|
481 if (!args)
|
|
482 {
|
|
483 gcc_assert (DECL_P (*node));
|
|
484 tree t = TREE_TYPE (*node);
|
|
485 if (TREE_CODE (t) != FUNCTION_TYPE)
|
|
486 warning (OPT_Wattributes, "%qE attribute only applies to functions",
|
|
487 name);
|
|
488 /* Argument handling and the stack layout for interrupt handlers
|
|
489 don't mix. It makes no sense in the first place, so emit an
|
|
490 error for this. */
|
|
491 else if (TYPE_ARG_TYPES (t)
|
|
492 && TREE_VALUE (TYPE_ARG_TYPES (t)) != void_type_node)
|
|
493 error_at (DECL_SOURCE_LOCATION (*node),
|
|
494 "interrupt handlers cannot have arguments");
|
|
495 return NULL_TREE;
|
|
496 }
|
|
497
|
|
498 value = TREE_VALUE (args);
|
|
499
|
|
500 if (TREE_CODE (value) != STRING_CST)
|
|
501 {
|
|
502 warning (OPT_Wattributes,
|
|
503 "argument of %qE attribute is not a string constant", name);
|
|
504 *no_add_attrs = true;
|
|
505 }
|
|
506 else if (strcmp (TREE_STRING_POINTER (value), "reset")
|
|
507 && strcmp (TREE_STRING_POINTER (value), "software_exception")
|
|
508 && strcmp (TREE_STRING_POINTER (value), "page_miss")
|
|
509 && strcmp (TREE_STRING_POINTER (value), "timer0")
|
|
510 && strcmp (TREE_STRING_POINTER (value), "timer1")
|
|
511 && strcmp (TREE_STRING_POINTER (value), "message")
|
|
512 && strcmp (TREE_STRING_POINTER (value), "dma0")
|
|
513 && strcmp (TREE_STRING_POINTER (value), "dma1")
|
|
514 && strcmp (TREE_STRING_POINTER (value), "wand")
|
|
515 && strcmp (TREE_STRING_POINTER (value), "swi"))
|
|
516 {
|
|
517 warning (OPT_Wattributes,
|
|
518 "argument of %qE attribute is not \"reset\", \"software_exception\", \"page_miss\", \"timer0\", \"timer1\", \"message\", \"dma0\", \"dma1\", \"wand\" or \"swi\"",
|
|
519 name);
|
|
520 *no_add_attrs = true;
|
|
521 return NULL_TREE;
|
|
522 }
|
|
523
|
|
524 return epiphany_handle_interrupt_attribute (node, name, TREE_CHAIN (args),
|
|
525 flags, no_add_attrs);
|
|
526 }
|
|
527
|
|
528 /* Handle a "forwarder_section" attribute; arguments as in
|
|
529 struct attribute_spec.handler. */
|
|
530 static tree
|
|
531 epiphany_handle_forwarder_attribute (tree *node ATTRIBUTE_UNUSED,
|
|
532 tree name, tree args,
|
|
533 int flags ATTRIBUTE_UNUSED,
|
|
534 bool *no_add_attrs)
|
|
535 {
|
|
536 tree value;
|
|
537
|
|
538 value = TREE_VALUE (args);
|
|
539
|
|
540 if (TREE_CODE (value) != STRING_CST)
|
|
541 {
|
|
542 warning (OPT_Wattributes,
|
|
543 "argument of %qE attribute is not a string constant", name);
|
|
544 *no_add_attrs = true;
|
|
545 }
|
|
546 return NULL_TREE;
|
|
547 }
|
|
548
|
|
549
|
|
550 /* Misc. utilities. */
|
|
551
|
|
552 /* Generate a SYMBOL_REF for the special function NAME. When the address
|
|
553 can't be placed directly into a call instruction, and if possible, copy
|
|
554 it to a register so that cse / code hoisting is possible. */
|
|
555 rtx
|
|
556 sfunc_symbol (const char *name)
|
|
557 {
|
|
558 rtx sym = gen_rtx_SYMBOL_REF (Pmode, name);
|
|
559
|
|
560 /* These sfuncs should be hidden, and every dso should get a copy. */
|
|
561 SYMBOL_REF_FLAGS (sym) = SYMBOL_FLAG_FUNCTION | SYMBOL_FLAG_LOCAL;
|
|
562 if (TARGET_SHORT_CALLS)
|
|
563 ; /* Nothing to be done. */
|
|
564 else if (can_create_pseudo_p ())
|
|
565 sym = copy_to_mode_reg (Pmode, sym);
|
|
566 else /* We rely on reload to fix this up. */
|
|
567 gcc_assert (!reload_in_progress || reload_completed);
|
|
568 return sym;
|
|
569 }
|
|
570
|
|
571 /* X and Y are two things to compare using CODE in IN_MODE.
|
|
572 Emit the compare insn, construct the proper cc reg in the proper
|
|
573 mode, and return the rtx for the cc reg comparison in CMODE. */
|
|
574
|
|
575 rtx
|
|
576 gen_compare_reg (machine_mode cmode, enum rtx_code code,
|
|
577 machine_mode in_mode, rtx x, rtx y)
|
|
578 {
|
|
579 machine_mode mode = SELECT_CC_MODE (code, x, y);
|
|
580 rtx cc_reg, pat, clob0, clob1, clob2;
|
|
581
|
|
582 if (in_mode == VOIDmode)
|
|
583 in_mode = GET_MODE (x);
|
|
584 if (in_mode == VOIDmode)
|
|
585 in_mode = GET_MODE (y);
|
|
586
|
|
587 if (mode == CC_FPmode)
|
|
588 {
|
|
589 /* The epiphany has only EQ / NE / LT / LE conditions for
|
|
590 hardware floating point. */
|
|
591 if (code == GT || code == GE || code == UNLE || code == UNLT)
|
|
592 {
|
|
593 rtx tmp = x; x = y; y = tmp;
|
|
594 code = swap_condition (code);
|
|
595 }
|
|
596 cc_reg = gen_rtx_REG (mode, CCFP_REGNUM);
|
|
597 y = force_reg (in_mode, y);
|
|
598 }
|
|
599 else
|
|
600 {
|
|
601 if (mode == CC_FP_GTEmode
|
|
602 && (code == LE || code == LT || code == UNGT || code == UNGE))
|
|
603 {
|
|
604 if (flag_finite_math_only
|
|
605 && ((REG_P (x) && REGNO (x) == GPR_0)
|
|
606 || (REG_P (y) && REGNO (y) == GPR_1)))
|
|
607 switch (code)
|
|
608 {
|
|
609 case LE: code = UNLE; break;
|
|
610 case LT: code = UNLT; break;
|
|
611 case UNGT: code = GT; break;
|
|
612 case UNGE: code = GE; break;
|
|
613 default: gcc_unreachable ();
|
|
614 }
|
|
615 else
|
|
616 {
|
|
617 rtx tmp = x; x = y; y = tmp;
|
|
618 code = swap_condition (code);
|
|
619 }
|
|
620 }
|
|
621 cc_reg = gen_rtx_REG (mode, CC_REGNUM);
|
|
622 }
|
|
623 if ((mode == CC_FP_EQmode || mode == CC_FP_GTEmode
|
|
624 || mode == CC_FP_ORDmode || mode == CC_FP_UNEQmode)
|
|
625 /* mov<mode>cc might want to re-emit a comparison during ifcvt. */
|
|
626 && (!REG_P (x) || REGNO (x) != GPR_0
|
|
627 || !REG_P (y) || REGNO (y) != GPR_1))
|
|
628 {
|
|
629 rtx reg;
|
|
630
|
|
631 #if 0
|
|
632 /* ??? We should really do the r0/r1 clobber only during rtl expansion,
|
|
633 but just like the flag clobber of movsicc, we have to allow
|
|
634 this for ifcvt to work, on the assumption that we'll only want
|
|
635 to do this if these registers have been used before by the
|
|
636 pre-ifcvt code. */
|
|
637 gcc_assert (currently_expanding_to_rtl);
|
|
638 #endif
|
|
639 reg = gen_rtx_REG (in_mode, GPR_0);
|
|
640 if (reg_overlap_mentioned_p (reg, y))
|
|
641 return 0;
|
|
642 emit_move_insn (reg, x);
|
|
643 x = reg;
|
|
644 reg = gen_rtx_REG (in_mode, GPR_1);
|
|
645 emit_move_insn (reg, y);
|
|
646 y = reg;
|
|
647 }
|
|
648 else
|
|
649 x = force_reg (in_mode, x);
|
|
650
|
|
651 pat = gen_rtx_SET (cc_reg, gen_rtx_COMPARE (mode, x, y));
|
|
652 if (mode == CC_FP_EQmode || mode == CC_FP_GTEmode)
|
|
653 {
|
|
654 const char *name = mode == CC_FP_EQmode ? "__eqsf2" : "__gtesf2";
|
|
655 rtx use = gen_rtx_USE (VOIDmode, sfunc_symbol (name));
|
|
656
|
|
657 clob0 = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, GPR_IP));
|
|
658 clob1 = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, GPR_LR));
|
|
659 pat = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (4, pat, use, clob0, clob1));
|
|
660 }
|
|
661 else if (mode == CC_FP_ORDmode || mode == CC_FP_UNEQmode)
|
|
662 {
|
|
663 const char *name = mode == CC_FP_ORDmode ? "__ordsf2" : "__uneqsf2";
|
|
664 rtx use = gen_rtx_USE (VOIDmode, sfunc_symbol (name));
|
|
665
|
|
666 clob0 = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, GPR_IP));
|
|
667 clob1 = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, GPR_16));
|
|
668 clob2 = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, GPR_LR));
|
|
669 pat = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (5, pat, use,
|
|
670 clob0, clob1, clob2));
|
|
671 }
|
|
672 else
|
|
673 {
|
|
674 clob0 = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (in_mode));
|
|
675 pat = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, pat, clob0));
|
|
676 }
|
|
677 emit_insn (pat);
|
|
678 return gen_rtx_fmt_ee (code, cmode, cc_reg, const0_rtx);
|
|
679 }
|
|
680
|
|
681 /* The ROUND_ADVANCE* macros are local to this file. */
|
|
682 /* Round SIZE up to a word boundary. */
|
|
683 #define ROUND_ADVANCE(SIZE) \
|
|
684 (((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
|
|
685
|
|
686 /* Round arg MODE/TYPE up to the next word boundary. */
|
|
687 #define ROUND_ADVANCE_ARG(MODE, TYPE) \
|
|
688 ((MODE) == BLKmode \
|
|
689 ? ROUND_ADVANCE (int_size_in_bytes (TYPE)) \
|
|
690 : ROUND_ADVANCE (GET_MODE_SIZE (MODE)))
|
|
691
|
|
692 /* Round CUM up to the necessary point for argument MODE/TYPE. */
|
|
693 #define ROUND_ADVANCE_CUM(CUM, MODE, TYPE) \
|
|
694 (epiphany_function_arg_boundary ((MODE), (TYPE)) > BITS_PER_WORD \
|
|
695 ? (((CUM) + 1) & ~1) \
|
|
696 : (CUM))
|
|
697
|
|
698 static unsigned int
|
|
699 epiphany_function_arg_boundary (machine_mode mode, const_tree type)
|
|
700 {
|
|
701 if ((type ? TYPE_ALIGN (type) : GET_MODE_BITSIZE (mode)) <= PARM_BOUNDARY)
|
|
702 return PARM_BOUNDARY;
|
|
703 return 2 * PARM_BOUNDARY;
|
|
704 }
|
|
705
|
|
706 /* Do any needed setup for a variadic function. For the EPIPHANY, we
|
|
707 actually emit the code in epiphany_expand_prologue.
|
|
708
|
|
709 CUM has not been updated for the last named argument which has type TYPE
|
|
710 and mode MODE, and we rely on this fact. */
|
|
711
|
|
712
|
|
713 static void
|
|
714 epiphany_setup_incoming_varargs (cumulative_args_t cum, machine_mode mode,
|
|
715 tree type, int *pretend_size, int no_rtl)
|
|
716 {
|
|
717 int first_anon_arg;
|
|
718 CUMULATIVE_ARGS next_cum;
|
|
719 machine_function_t *mf = MACHINE_FUNCTION (cfun);
|
|
720
|
|
721 /* All BLKmode values are passed by reference. */
|
|
722 gcc_assert (mode != BLKmode);
|
|
723
|
|
724 next_cum = *get_cumulative_args (cum);
|
|
725 next_cum
|
|
726 = ROUND_ADVANCE_CUM (next_cum, mode, type) + ROUND_ADVANCE_ARG (mode, type);
|
|
727 first_anon_arg = next_cum;
|
|
728
|
|
729 if (first_anon_arg < MAX_EPIPHANY_PARM_REGS && !no_rtl)
|
|
730 {
|
|
731 /* Note that first_reg_offset < MAX_EPIPHANY_PARM_REGS. */
|
|
732 int first_reg_offset = first_anon_arg;
|
|
733
|
|
734 *pretend_size = ((MAX_EPIPHANY_PARM_REGS - first_reg_offset)
|
|
735 * UNITS_PER_WORD);
|
|
736 }
|
|
737 mf->args_parsed = 1;
|
|
738 mf->pretend_args_odd = ((*pretend_size & UNITS_PER_WORD) ? 1 : 0);
|
|
739 }
|
|
740
|
|
741 static int
|
|
742 epiphany_arg_partial_bytes (cumulative_args_t cum, machine_mode mode,
|
|
743 tree type, bool named ATTRIBUTE_UNUSED)
|
|
744 {
|
|
745 int words = 0, rounded_cum;
|
|
746
|
|
747 gcc_assert (!epiphany_pass_by_reference (cum, mode, type, /* named */ true));
|
|
748
|
|
749 rounded_cum = ROUND_ADVANCE_CUM (*get_cumulative_args (cum), mode, type);
|
|
750 if (rounded_cum < MAX_EPIPHANY_PARM_REGS)
|
|
751 {
|
|
752 words = MAX_EPIPHANY_PARM_REGS - rounded_cum;
|
|
753 if (words >= ROUND_ADVANCE_ARG (mode, type))
|
|
754 words = 0;
|
|
755 }
|
|
756 return words * UNITS_PER_WORD;
|
|
757 }
|
|
758
|
|
759 /* Cost functions. */
|
|
760
|
|
761 /* Compute a (partial) cost for rtx X. Return true if the complete
|
|
762 cost has been computed, and false if subexpressions should be
|
|
763 scanned. In either case, *TOTAL contains the cost result. */
|
|
764
|
|
765 static bool
|
|
766 epiphany_rtx_costs (rtx x, machine_mode mode, int outer_code,
|
|
767 int opno ATTRIBUTE_UNUSED,
|
|
768 int *total, bool speed ATTRIBUTE_UNUSED)
|
|
769 {
|
|
770 int code = GET_CODE (x);
|
|
771
|
|
772 switch (code)
|
|
773 {
|
|
774 /* Small integers in the right context are as cheap as registers. */
|
|
775 case CONST_INT:
|
|
776 if ((outer_code == PLUS || outer_code == MINUS)
|
|
777 && SIMM11 (INTVAL (x)))
|
|
778 {
|
|
779 *total = 0;
|
|
780 return true;
|
|
781 }
|
|
782 if (IMM16 (INTVAL (x)))
|
|
783 {
|
|
784 *total = outer_code == SET ? 0 : COSTS_N_INSNS (1);
|
|
785 return true;
|
|
786 }
|
|
787 /* FALLTHRU */
|
|
788
|
|
789 case CONST:
|
|
790 case LABEL_REF:
|
|
791 case SYMBOL_REF:
|
|
792 *total = COSTS_N_INSNS ((epiphany_small16 (x) ? 0 : 1)
|
|
793 + (outer_code == SET ? 0 : 1));
|
|
794 return true;
|
|
795
|
|
796 case CONST_DOUBLE:
|
|
797 {
|
|
798 rtx high, low;
|
|
799 split_double (x, &high, &low);
|
|
800 *total = COSTS_N_INSNS (!IMM16 (INTVAL (high))
|
|
801 + !IMM16 (INTVAL (low)));
|
|
802 return true;
|
|
803 }
|
|
804
|
|
805 case ASHIFT:
|
|
806 case ASHIFTRT:
|
|
807 case LSHIFTRT:
|
|
808 *total = COSTS_N_INSNS (1);
|
|
809 return true;
|
|
810
|
|
811 case COMPARE:
|
|
812 switch (mode)
|
|
813 {
|
|
814 /* There are a number of single-insn combiner patterns that use
|
|
815 the flag side effects of arithmetic. */
|
|
816 case E_CC_N_NEmode:
|
|
817 case E_CC_C_LTUmode:
|
|
818 case E_CC_C_GTUmode:
|
|
819 return true;
|
|
820 default:
|
|
821 return false;
|
|
822 }
|
|
823
|
|
824
|
|
825 case SET:
|
|
826 {
|
|
827 rtx src = SET_SRC (x);
|
|
828 if (BINARY_P (src))
|
|
829 *total = 0;
|
|
830 return false;
|
|
831 }
|
|
832
|
|
833 default:
|
|
834 return false;
|
|
835 }
|
|
836 }
|
|
837
|
|
838
|
|
839 /* Provide the costs of an addressing mode that contains ADDR.
|
|
840 If ADDR is not a valid address, its cost is irrelevant. */
|
|
841
|
|
842 static int
|
|
843 epiphany_address_cost (rtx addr, machine_mode mode,
|
|
844 addr_space_t as ATTRIBUTE_UNUSED, bool speed)
|
|
845 {
|
|
846 rtx reg;
|
|
847 rtx off = const0_rtx;
|
|
848 int i;
|
|
849
|
|
850 if (speed)
|
|
851 return 0;
|
|
852 /* Return 0 for addresses valid in short insns, 1 for addresses only valid
|
|
853 in long insns. */
|
|
854 switch (GET_CODE (addr))
|
|
855 {
|
|
856 case PLUS :
|
|
857 reg = XEXP (addr, 0);
|
|
858 off = XEXP (addr, 1);
|
|
859 break;
|
|
860 case POST_MODIFY:
|
|
861 reg = XEXP (addr, 0);
|
|
862 off = XEXP (addr, 1);
|
|
863 gcc_assert (GET_CODE (off) == PLUS && rtx_equal_p (reg, XEXP (off, 0)));
|
|
864 off = XEXP (off, 1);
|
|
865 if (satisfies_constraint_Rgs (reg) && satisfies_constraint_Rgs (off))
|
|
866 return 0;
|
|
867 return 1;
|
|
868 case REG:
|
|
869 default:
|
|
870 reg = addr;
|
|
871 break;
|
|
872 }
|
|
873 if (!satisfies_constraint_Rgs (reg))
|
|
874 return 1;
|
|
875 /* The offset range available for short instructions depends on the mode
|
|
876 of the memory access. */
|
|
877 /* First, make sure we have a valid integer. */
|
|
878 if (!satisfies_constraint_L (off))
|
|
879 return 1;
|
|
880 i = INTVAL (off);
|
|
881 switch (GET_MODE_SIZE (mode))
|
|
882 {
|
|
883 default:
|
|
884 case 4:
|
|
885 if (i & 1)
|
|
886 return 1;
|
|
887 i >>= 1;
|
|
888 /* Fall through. */
|
|
889 case 2:
|
|
890 if (i & 1)
|
|
891 return 1;
|
|
892 i >>= 1;
|
|
893 /* Fall through. */
|
|
894 case 1:
|
|
895 return i < -7 || i > 7;
|
|
896 }
|
|
897 }
|
|
898
|
|
899 /* Compute the cost of moving data between registers and memory.
|
|
900 For integer, load latency is twice as long as register-register moves,
|
|
901 but issue pich is the same. For floating point, load latency is three
|
|
902 times as much as a reg-reg move. */
|
|
903 static int
|
|
904 epiphany_memory_move_cost (machine_mode mode,
|
|
905 reg_class_t rclass ATTRIBUTE_UNUSED,
|
|
906 bool in ATTRIBUTE_UNUSED)
|
|
907 {
|
|
908 return GET_MODE_CLASS (mode) == MODE_INT ? 3 : 4;
|
|
909 }
|
|
910
|
|
911 /* Function prologue/epilogue handlers. */
|
|
912
|
|
913 /* EPIPHANY stack frames look like:
|
|
914
|
|
915 Before call After call
|
|
916 +-----------------------+ +-----------------------+
|
|
917 | | | |
|
|
918 high | local variables, | | local variables, |
|
|
919 mem | reg save area, etc. | | reg save area, etc. |
|
|
920 | | | |
|
|
921 +-----------------------+ +-----------------------+
|
|
922 | | | |
|
|
923 | arguments on stack. | | arguments on stack. |
|
|
924 | | | |
|
|
925 SP+8->+-----------------------+FP+8m->+-----------------------+
|
|
926 | 2 word save area for | | reg parm save area, |
|
|
927 | leaf funcs / flags | | only created for |
|
|
928 SP+0->+-----------------------+ | variable argument |
|
|
929 | functions |
|
|
930 FP+8n->+-----------------------+
|
|
931 | |
|
|
932 | register save area |
|
|
933 | |
|
|
934 +-----------------------+
|
|
935 | |
|
|
936 | local variables |
|
|
937 | |
|
|
938 FP+0->+-----------------------+
|
|
939 | |
|
|
940 | alloca allocations |
|
|
941 | |
|
|
942 +-----------------------+
|
|
943 | |
|
|
944 | arguments on stack |
|
|
945 | |
|
|
946 SP+8->+-----------------------+
|
|
947 low | 2 word save area for |
|
|
948 memory | leaf funcs / flags |
|
|
949 SP+0->+-----------------------+
|
|
950
|
|
951 Notes:
|
|
952 1) The "reg parm save area" does not exist for non variable argument fns.
|
|
953 The "reg parm save area" could be eliminated if we created our
|
|
954 own TARGET_GIMPLIFY_VA_ARG_EXPR, but that has tradeoffs as well
|
|
955 (so it's not done). */
|
|
956
|
|
957 /* Structure to be filled in by epiphany_compute_frame_size with register
|
|
958 save masks, and offsets for the current function. */
|
|
959 struct epiphany_frame_info
|
|
960 {
|
|
961 unsigned int total_size; /* # bytes that the entire frame takes up. */
|
|
962 unsigned int pretend_size; /* # bytes we push and pretend caller did. */
|
|
963 unsigned int args_size; /* # bytes that outgoing arguments take up. */
|
|
964 unsigned int reg_size; /* # bytes needed to store regs. */
|
|
965 unsigned int var_size; /* # bytes that variables take up. */
|
|
966 HARD_REG_SET gmask; /* Set of saved gp registers. */
|
|
967 int initialized; /* Nonzero if frame size already calculated. */
|
|
968 int stld_sz; /* Current load/store data size for offset
|
|
969 adjustment. */
|
|
970 int need_fp; /* value to override "frame_pointer_needed */
|
|
971 /* FIRST_SLOT is the slot that is saved first, at the very start of
|
|
972 the frame, with a POST_MODIFY to allocate the frame, if the size fits,
|
|
973 or at least the parm and register save areas, otherwise.
|
|
974 In the case of a large frame, LAST_SLOT is the slot that is saved last,
|
|
975 with a POST_MODIFY to allocate the rest of the frame. */
|
|
976 int first_slot, last_slot, first_slot_offset, last_slot_offset;
|
|
977 int first_slot_size;
|
|
978 int small_threshold;
|
|
979 };
|
|
980
|
|
981 /* Current frame information calculated by epiphany_compute_frame_size. */
|
|
982 static struct epiphany_frame_info current_frame_info;
|
|
983
|
|
984 /* Zero structure to initialize current_frame_info. */
|
|
985 static struct epiphany_frame_info zero_frame_info;
|
|
986
|
|
987 /* The usual; we set up our machine_function data. */
|
|
988 static struct machine_function *
|
|
989 epiphany_init_machine_status (void)
|
|
990 {
|
|
991 struct machine_function *machine;
|
|
992
|
|
993 /* Reset state info for each function. */
|
|
994 current_frame_info = zero_frame_info;
|
|
995
|
|
996 machine = ggc_cleared_alloc<machine_function_t> ();
|
|
997
|
|
998 return machine;
|
|
999 }
|
|
1000
|
|
1001 /* Implements INIT_EXPANDERS. We just set up to call the above
|
|
1002 * function. */
|
|
1003 void
|
|
1004 epiphany_init_expanders (void)
|
|
1005 {
|
|
1006 init_machine_status = epiphany_init_machine_status;
|
|
1007 }
|
|
1008
|
|
1009 /* Type of function DECL.
|
|
1010
|
|
1011 The result is cached. To reset the cache at the end of a function,
|
|
1012 call with DECL = NULL_TREE. */
|
|
1013
|
|
1014 static enum epiphany_function_type
|
|
1015 epiphany_compute_function_type (tree decl)
|
|
1016 {
|
|
1017 tree a;
|
|
1018 /* Cached value. */
|
|
1019 static enum epiphany_function_type fn_type = EPIPHANY_FUNCTION_UNKNOWN;
|
|
1020 /* Last function we were called for. */
|
|
1021 static tree last_fn = NULL_TREE;
|
|
1022
|
|
1023 /* Resetting the cached value? */
|
|
1024 if (decl == NULL_TREE)
|
|
1025 {
|
|
1026 fn_type = EPIPHANY_FUNCTION_UNKNOWN;
|
|
1027 last_fn = NULL_TREE;
|
|
1028 return fn_type;
|
|
1029 }
|
|
1030
|
|
1031 if (decl == last_fn && fn_type != EPIPHANY_FUNCTION_UNKNOWN)
|
|
1032 return fn_type;
|
|
1033
|
|
1034 /* Assume we have a normal function (not an interrupt handler). */
|
|
1035 fn_type = EPIPHANY_FUNCTION_NORMAL;
|
|
1036
|
|
1037 /* Now see if this is an interrupt handler. */
|
|
1038 for (a = DECL_ATTRIBUTES (decl);
|
|
1039 a;
|
|
1040 a = TREE_CHAIN (a))
|
|
1041 {
|
|
1042 tree name = TREE_PURPOSE (a);
|
|
1043
|
|
1044 if (name == get_identifier ("interrupt"))
|
|
1045 fn_type = EPIPHANY_FUNCTION_INTERRUPT;
|
|
1046 }
|
|
1047
|
|
1048 last_fn = decl;
|
|
1049 return fn_type;
|
|
1050 }
|
|
1051
|
|
1052 #define RETURN_ADDR_REGNUM GPR_LR
|
|
1053 #define FRAME_POINTER_MASK (1 << (FRAME_POINTER_REGNUM))
|
|
1054 #define RETURN_ADDR_MASK (1 << (RETURN_ADDR_REGNUM))
|
|
1055
|
|
1056 /* Tell prologue and epilogue if register REGNO should be saved / restored.
|
|
1057 The return address and frame pointer are treated separately.
|
|
1058 Don't consider them here. */
|
|
1059 #define MUST_SAVE_REGISTER(regno, interrupt_p) \
|
|
1060 ((df_regs_ever_live_p (regno) \
|
|
1061 || (interrupt_p && !crtl->is_leaf \
|
|
1062 && call_used_regs[regno] && !fixed_regs[regno])) \
|
|
1063 && (!call_used_regs[regno] || regno == GPR_LR \
|
|
1064 || (interrupt_p && regno != GPR_SP)))
|
|
1065
|
|
1066 #define MUST_SAVE_RETURN_ADDR 0
|
|
1067
|
|
1068 /* Return the bytes needed to compute the frame pointer from the current
|
|
1069 stack pointer.
|
|
1070
|
|
1071 SIZE is the size needed for local variables. */
|
|
1072
|
|
1073 static unsigned int
|
|
1074 epiphany_compute_frame_size (int size /* # of var. bytes allocated. */)
|
|
1075 {
|
|
1076 int regno;
|
|
1077 unsigned int total_size, var_size, args_size, pretend_size, reg_size;
|
|
1078 HARD_REG_SET gmask;
|
|
1079 enum epiphany_function_type fn_type;
|
|
1080 int interrupt_p;
|
|
1081 int first_slot, last_slot, first_slot_offset, last_slot_offset;
|
|
1082 int first_slot_size;
|
|
1083 int small_slots = 0;
|
|
1084
|
|
1085 var_size = size;
|
|
1086 args_size = crtl->outgoing_args_size;
|
|
1087 pretend_size = crtl->args.pretend_args_size;
|
|
1088 total_size = args_size + var_size;
|
|
1089 reg_size = 0;
|
|
1090 CLEAR_HARD_REG_SET (gmask);
|
|
1091 first_slot = -1;
|
|
1092 first_slot_offset = 0;
|
|
1093 last_slot = -1;
|
|
1094 last_slot_offset = 0;
|
|
1095 first_slot_size = UNITS_PER_WORD;
|
|
1096
|
|
1097 /* See if this is an interrupt handler. Call used registers must be saved
|
|
1098 for them too. */
|
|
1099 fn_type = epiphany_compute_function_type (current_function_decl);
|
|
1100 interrupt_p = EPIPHANY_INTERRUPT_P (fn_type);
|
|
1101
|
|
1102 /* Calculate space needed for registers. */
|
|
1103
|
|
1104 for (regno = MAX_EPIPHANY_PARM_REGS - 1; pretend_size > reg_size; regno--)
|
|
1105 {
|
|
1106 reg_size += UNITS_PER_WORD;
|
|
1107 SET_HARD_REG_BIT (gmask, regno);
|
|
1108 if (epiphany_stack_offset - reg_size == 0)
|
|
1109 first_slot = regno;
|
|
1110 }
|
|
1111
|
|
1112 if (interrupt_p)
|
|
1113 reg_size += 2 * UNITS_PER_WORD;
|
|
1114 else
|
|
1115 small_slots = epiphany_stack_offset / UNITS_PER_WORD;
|
|
1116
|
|
1117 if (frame_pointer_needed)
|
|
1118 {
|
|
1119 current_frame_info.need_fp = 1;
|
|
1120 if (!interrupt_p && first_slot < 0)
|
|
1121 first_slot = GPR_FP;
|
|
1122 }
|
|
1123 else
|
|
1124 current_frame_info.need_fp = 0;
|
|
1125 for (regno = 0; regno <= GPR_LAST; regno++)
|
|
1126 {
|
|
1127 if (MUST_SAVE_REGISTER (regno, interrupt_p))
|
|
1128 {
|
|
1129 gcc_assert (!TEST_HARD_REG_BIT (gmask, regno));
|
|
1130 reg_size += UNITS_PER_WORD;
|
|
1131 SET_HARD_REG_BIT (gmask, regno);
|
|
1132 /* FIXME: when optimizing for speed, take schedling into account
|
|
1133 when selecting these registers. */
|
|
1134 if (regno == first_slot)
|
|
1135 gcc_assert (regno == GPR_FP && frame_pointer_needed);
|
|
1136 else if (!interrupt_p && first_slot < 0)
|
|
1137 first_slot = regno;
|
|
1138 else if (last_slot < 0
|
|
1139 && (first_slot ^ regno) != 1
|
|
1140 && (!interrupt_p || regno > GPR_1))
|
|
1141 last_slot = regno;
|
|
1142 }
|
|
1143 }
|
|
1144 if (TEST_HARD_REG_BIT (gmask, GPR_LR))
|
|
1145 MACHINE_FUNCTION (cfun)->lr_clobbered = 1;
|
|
1146 /* ??? Could sometimes do better than that. */
|
|
1147 current_frame_info.small_threshold
|
|
1148 = (optimize >= 3 || interrupt_p ? 0
|
|
1149 : pretend_size ? small_slots
|
|
1150 : 4 + small_slots - (first_slot == GPR_FP));
|
|
1151
|
|
1152 /* If there might be variables with 64-bit alignment requirement, align the
|
|
1153 start of the variables. */
|
|
1154 if (var_size >= 2 * UNITS_PER_WORD
|
|
1155 /* We don't want to split a double reg save/restore across two unpaired
|
|
1156 stack slots when optimizing. This rounding could be avoided with
|
|
1157 more complex reordering of the register saves, but that would seem
|
|
1158 to be a lot of code complexity for little gain. */
|
|
1159 || (reg_size > 8 && optimize))
|
|
1160 reg_size = EPIPHANY_STACK_ALIGN (reg_size);
|
|
1161 if (((total_size + reg_size
|
|
1162 /* Reserve space for UNKNOWN_REGNUM. */
|
|
1163 + EPIPHANY_STACK_ALIGN (4))
|
|
1164 <= (unsigned) epiphany_stack_offset)
|
|
1165 && !interrupt_p
|
|
1166 && crtl->is_leaf && !frame_pointer_needed)
|
|
1167 {
|
|
1168 first_slot = -1;
|
|
1169 last_slot = -1;
|
|
1170 goto alloc_done;
|
|
1171 }
|
|
1172 else if (reg_size
|
|
1173 && !interrupt_p
|
|
1174 && reg_size < (unsigned HOST_WIDE_INT) epiphany_stack_offset)
|
|
1175 reg_size = epiphany_stack_offset;
|
|
1176 if (interrupt_p)
|
|
1177 {
|
|
1178 if (total_size + reg_size < 0x3fc)
|
|
1179 {
|
|
1180 first_slot_offset = EPIPHANY_STACK_ALIGN (total_size + reg_size);
|
|
1181 first_slot_offset += EPIPHANY_STACK_ALIGN (epiphany_stack_offset);
|
|
1182 last_slot = -1;
|
|
1183 }
|
|
1184 else
|
|
1185 {
|
|
1186 first_slot_offset = EPIPHANY_STACK_ALIGN (reg_size);
|
|
1187 last_slot_offset = EPIPHANY_STACK_ALIGN (total_size);
|
|
1188 last_slot_offset += EPIPHANY_STACK_ALIGN (epiphany_stack_offset);
|
|
1189 if (last_slot >= 0)
|
|
1190 CLEAR_HARD_REG_BIT (gmask, last_slot);
|
|
1191 }
|
|
1192 }
|
|
1193 else if (total_size + reg_size < 0x1ffc && first_slot >= 0)
|
|
1194 {
|
|
1195 first_slot_offset = EPIPHANY_STACK_ALIGN (total_size + reg_size);
|
|
1196 last_slot = -1;
|
|
1197 }
|
|
1198 else
|
|
1199 {
|
|
1200 if (total_size + reg_size <= (unsigned) epiphany_stack_offset)
|
|
1201 {
|
|
1202 gcc_assert (first_slot < 0);
|
|
1203 gcc_assert (reg_size == 0 || (int) reg_size == epiphany_stack_offset);
|
|
1204 last_slot_offset = EPIPHANY_STACK_ALIGN (total_size + reg_size);
|
|
1205 }
|
|
1206 else
|
|
1207 {
|
|
1208 first_slot_offset
|
|
1209 = (reg_size
|
|
1210 ? EPIPHANY_STACK_ALIGN (reg_size - epiphany_stack_offset) : 0);
|
|
1211 if (!first_slot_offset)
|
|
1212 {
|
|
1213 if (first_slot != GPR_FP || !current_frame_info.need_fp)
|
|
1214 last_slot = first_slot;
|
|
1215 first_slot = -1;
|
|
1216 }
|
|
1217 last_slot_offset = EPIPHANY_STACK_ALIGN (total_size);
|
|
1218 if (reg_size)
|
|
1219 last_slot_offset += EPIPHANY_STACK_ALIGN (epiphany_stack_offset);
|
|
1220 }
|
|
1221 if (last_slot >= 0)
|
|
1222 CLEAR_HARD_REG_BIT (gmask, last_slot);
|
|
1223 }
|
|
1224 alloc_done:
|
|
1225 if (first_slot >= 0)
|
|
1226 {
|
|
1227 CLEAR_HARD_REG_BIT (gmask, first_slot);
|
|
1228 if (TEST_HARD_REG_BIT (gmask, first_slot ^ 1)
|
|
1229 && epiphany_stack_offset - pretend_size >= 2 * UNITS_PER_WORD)
|
|
1230 {
|
|
1231 CLEAR_HARD_REG_BIT (gmask, first_slot ^ 1);
|
|
1232 first_slot_size = 2 * UNITS_PER_WORD;
|
|
1233 first_slot &= ~1;
|
|
1234 }
|
|
1235 }
|
|
1236 total_size = first_slot_offset + last_slot_offset;
|
|
1237
|
|
1238 /* Save computed information. */
|
|
1239 current_frame_info.total_size = total_size;
|
|
1240 current_frame_info.pretend_size = pretend_size;
|
|
1241 current_frame_info.var_size = var_size;
|
|
1242 current_frame_info.args_size = args_size;
|
|
1243 current_frame_info.reg_size = reg_size;
|
|
1244 COPY_HARD_REG_SET (current_frame_info.gmask, gmask);
|
|
1245 current_frame_info.first_slot = first_slot;
|
|
1246 current_frame_info.last_slot = last_slot;
|
|
1247 current_frame_info.first_slot_offset = first_slot_offset;
|
|
1248 current_frame_info.first_slot_size = first_slot_size;
|
|
1249 current_frame_info.last_slot_offset = last_slot_offset;
|
|
1250
|
|
1251 current_frame_info.initialized = reload_completed;
|
|
1252
|
|
1253 /* Ok, we're done. */
|
|
1254 return total_size;
|
|
1255 }
|
|
1256
|
|
1257 /* Print operand X (an rtx) in assembler syntax to file FILE.
|
|
1258 CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified.
|
|
1259 For `%' followed by punctuation, CODE is the punctuation and X is null. */
|
|
1260
|
|
1261 static void
|
|
1262 epiphany_print_operand (FILE *file, rtx x, int code)
|
|
1263 {
|
|
1264 switch (code)
|
|
1265 {
|
|
1266 case 'd':
|
|
1267 fputs (epiphany_condition_codes[get_epiphany_condition_code (x)], file);
|
|
1268 return;
|
|
1269 case 'D':
|
|
1270 fputs (epiphany_condition_codes[EPIPHANY_INVERSE_CONDITION_CODE
|
|
1271 (get_epiphany_condition_code (x))],
|
|
1272 file);
|
|
1273 return;
|
|
1274
|
|
1275 case 'X':
|
|
1276 current_frame_info.stld_sz = 8;
|
|
1277 break;
|
|
1278
|
|
1279 case 'C' :
|
|
1280 current_frame_info.stld_sz = 4;
|
|
1281 break;
|
|
1282
|
|
1283 case 'c' :
|
|
1284 current_frame_info.stld_sz = 2;
|
|
1285 break;
|
|
1286
|
|
1287 case 'f':
|
|
1288 fputs (REG_P (x) ? "jalr " : "bl ", file);
|
|
1289 break;
|
|
1290
|
|
1291 case '-':
|
|
1292 fprintf (file, "r%d", epiphany_m1reg);
|
|
1293 return;
|
|
1294
|
|
1295 case 0 :
|
|
1296 /* Do nothing special. */
|
|
1297 break;
|
|
1298 default :
|
|
1299 /* Unknown flag. */
|
|
1300 output_operand_lossage ("invalid operand output code");
|
|
1301 }
|
|
1302
|
|
1303 switch (GET_CODE (x))
|
|
1304 {
|
|
1305 rtx addr;
|
|
1306 rtx offset;
|
|
1307
|
|
1308 case REG :
|
|
1309 fputs (reg_names[REGNO (x)], file);
|
|
1310 break;
|
|
1311 case MEM :
|
|
1312 if (code == 0)
|
|
1313 current_frame_info.stld_sz = 1;
|
|
1314 fputc ('[', file);
|
|
1315 addr = XEXP (x, 0);
|
|
1316 switch (GET_CODE (addr))
|
|
1317 {
|
|
1318 case POST_INC:
|
|
1319 offset = GEN_INT (GET_MODE_SIZE (GET_MODE (x)));
|
|
1320 addr = XEXP (addr, 0);
|
|
1321 break;
|
|
1322 case POST_DEC:
|
|
1323 offset = GEN_INT (-GET_MODE_SIZE (GET_MODE (x)));
|
|
1324 addr = XEXP (addr, 0);
|
|
1325 break;
|
|
1326 case POST_MODIFY:
|
|
1327 offset = XEXP (XEXP (addr, 1), 1);
|
|
1328 addr = XEXP (addr, 0);
|
|
1329 break;
|
|
1330 default:
|
|
1331 offset = 0;
|
|
1332 break;
|
|
1333 }
|
|
1334 output_address (GET_MODE (x), addr);
|
|
1335 fputc (']', file);
|
|
1336 if (offset)
|
|
1337 {
|
|
1338 fputc (',', file);
|
|
1339 if (CONST_INT_P (offset)) switch (GET_MODE_SIZE (GET_MODE (x)))
|
|
1340 {
|
|
1341 default:
|
|
1342 gcc_unreachable ();
|
|
1343 case 8:
|
|
1344 offset = GEN_INT (INTVAL (offset) >> 3);
|
|
1345 break;
|
|
1346 case 4:
|
|
1347 offset = GEN_INT (INTVAL (offset) >> 2);
|
|
1348 break;
|
|
1349 case 2:
|
|
1350 offset = GEN_INT (INTVAL (offset) >> 1);
|
|
1351 break;
|
|
1352 case 1:
|
|
1353 break;
|
|
1354 }
|
|
1355 output_address (GET_MODE (x), offset);
|
|
1356 }
|
|
1357 break;
|
|
1358 case CONST_DOUBLE :
|
|
1359 /* We handle SFmode constants here as output_addr_const doesn't. */
|
|
1360 if (GET_MODE (x) == SFmode)
|
|
1361 {
|
|
1362 long l;
|
|
1363
|
|
1364 REAL_VALUE_TO_TARGET_SINGLE (*CONST_DOUBLE_REAL_VALUE (x), l);
|
|
1365 fprintf (file, "%s0x%08lx", IMMEDIATE_PREFIX, l);
|
|
1366 break;
|
|
1367 }
|
|
1368 /* FALLTHRU */
|
|
1369 /* Let output_addr_const deal with it. */
|
|
1370 case CONST_INT:
|
|
1371 fprintf(file,"%s",IMMEDIATE_PREFIX);
|
|
1372 if (code == 'C' || code == 'X')
|
|
1373 {
|
|
1374 fprintf (file, "%ld",
|
|
1375 (long) (INTVAL (x) / current_frame_info.stld_sz));
|
|
1376 break;
|
|
1377 }
|
|
1378 /* Fall through */
|
|
1379 default :
|
|
1380 output_addr_const (file, x);
|
|
1381 break;
|
|
1382 }
|
|
1383 }
|
|
1384
|
|
1385 /* Print a memory address as an operand to reference that memory location. */
|
|
1386
|
|
1387 static void
|
|
1388 epiphany_print_operand_address (FILE *file, machine_mode /*mode*/, rtx addr)
|
|
1389 {
|
|
1390 register rtx base, index = 0;
|
|
1391 int offset = 0;
|
|
1392
|
|
1393 switch (GET_CODE (addr))
|
|
1394 {
|
|
1395 case REG :
|
|
1396 fputs (reg_names[REGNO (addr)], file);
|
|
1397 break;
|
|
1398 case SYMBOL_REF :
|
|
1399 if (/*???*/ 0 && SYMBOL_REF_FUNCTION_P (addr))
|
|
1400 {
|
|
1401 output_addr_const (file, addr);
|
|
1402 }
|
|
1403 else
|
|
1404 {
|
|
1405 output_addr_const (file, addr);
|
|
1406 }
|
|
1407 break;
|
|
1408 case PLUS :
|
|
1409 if (GET_CODE (XEXP (addr, 0)) == CONST_INT)
|
|
1410 offset = INTVAL (XEXP (addr, 0)), base = XEXP (addr, 1);
|
|
1411 else if (GET_CODE (XEXP (addr, 1)) == CONST_INT)
|
|
1412 offset = INTVAL (XEXP (addr, 1)), base = XEXP (addr, 0);
|
|
1413 else
|
|
1414 base = XEXP (addr, 0), index = XEXP (addr, 1);
|
|
1415 gcc_assert (GET_CODE (base) == REG);
|
|
1416 fputs (reg_names[REGNO (base)], file);
|
|
1417 if (index == 0)
|
|
1418 {
|
|
1419 /*
|
|
1420 ** ++rk quirky method to scale offset for ld/str.......
|
|
1421 */
|
|
1422 fprintf (file, ",%s%d", IMMEDIATE_PREFIX,
|
|
1423 offset/current_frame_info.stld_sz);
|
|
1424 }
|
|
1425 else
|
|
1426 {
|
|
1427 switch (GET_CODE (index))
|
|
1428 {
|
|
1429 case REG:
|
|
1430 fprintf (file, ",%s", reg_names[REGNO (index)]);
|
|
1431 break;
|
|
1432 case SYMBOL_REF:
|
|
1433 fputc (',', file), output_addr_const (file, index);
|
|
1434 break;
|
|
1435 default:
|
|
1436 gcc_unreachable ();
|
|
1437 }
|
|
1438 }
|
|
1439 break;
|
|
1440 case PRE_INC: case PRE_DEC: case POST_INC: case POST_DEC: case POST_MODIFY:
|
|
1441 /* We shouldn't get here as we've lost the mode of the memory object
|
|
1442 (which says how much to inc/dec by.
|
|
1443 FIXME: We have the mode now, address printing can be moved into this
|
|
1444 function. */
|
|
1445 gcc_unreachable ();
|
|
1446 break;
|
|
1447 default:
|
|
1448 output_addr_const (file, addr);
|
|
1449 break;
|
|
1450 }
|
|
1451 }
|
|
1452
|
|
1453 void
|
|
1454 epiphany_final_prescan_insn (rtx_insn *insn ATTRIBUTE_UNUSED,
|
|
1455 rtx *opvec ATTRIBUTE_UNUSED,
|
|
1456 int noperands ATTRIBUTE_UNUSED)
|
|
1457 {
|
|
1458 int i = epiphany_n_nops;
|
|
1459 rtx pat ATTRIBUTE_UNUSED;
|
|
1460
|
|
1461 while (i--)
|
|
1462 fputs ("\tnop\n", asm_out_file);
|
|
1463 }
|
|
1464
|
|
1465
|
|
1466 /* Worker function for TARGET_RETURN_IN_MEMORY. */
|
|
1467
|
|
1468 static bool
|
|
1469 epiphany_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
|
|
1470 {
|
|
1471 HOST_WIDE_INT size = int_size_in_bytes (type);
|
|
1472
|
|
1473 if (AGGREGATE_TYPE_P (type)
|
|
1474 && (TYPE_MODE (type) == BLKmode || TYPE_NEEDS_CONSTRUCTING (type)))
|
|
1475 return true;
|
|
1476 return (size == -1 || size > 8);
|
|
1477 }
|
|
1478
|
|
1479 /* For EPIPHANY, All aggregates and arguments greater than 8 bytes are
|
|
1480 passed by reference. */
|
|
1481
|
|
1482 static bool
|
|
1483 epiphany_pass_by_reference (cumulative_args_t ca ATTRIBUTE_UNUSED,
|
|
1484 machine_mode mode, const_tree type,
|
|
1485 bool named ATTRIBUTE_UNUSED)
|
|
1486 {
|
|
1487 if (type)
|
|
1488 {
|
|
1489 if (AGGREGATE_TYPE_P (type)
|
|
1490 && (mode == BLKmode || TYPE_NEEDS_CONSTRUCTING (type)))
|
|
1491 return true;
|
|
1492 }
|
|
1493 return false;
|
|
1494 }
|
|
1495
|
|
1496
|
|
1497 static rtx
|
|
1498 epiphany_function_value (const_tree ret_type,
|
|
1499 const_tree fn_decl_or_type ATTRIBUTE_UNUSED,
|
|
1500 bool outgoing ATTRIBUTE_UNUSED)
|
|
1501 {
|
|
1502 machine_mode mode;
|
|
1503
|
|
1504 mode = TYPE_MODE (ret_type);
|
|
1505 /* We must change the mode like PROMOTE_MODE does.
|
|
1506 ??? PROMOTE_MODE is ignored for non-scalar types.
|
|
1507 The set of types tested here has to be kept in sync
|
|
1508 with the one in explow.c:promote_mode. */
|
|
1509 if (GET_MODE_CLASS (mode) == MODE_INT
|
|
1510 && GET_MODE_SIZE (mode) < 4
|
|
1511 && (TREE_CODE (ret_type) == INTEGER_TYPE
|
|
1512 || TREE_CODE (ret_type) == ENUMERAL_TYPE
|
|
1513 || TREE_CODE (ret_type) == BOOLEAN_TYPE
|
|
1514 || TREE_CODE (ret_type) == OFFSET_TYPE))
|
|
1515 mode = SImode;
|
|
1516 return gen_rtx_REG (mode, 0);
|
|
1517 }
|
|
1518
|
|
1519 static rtx
|
|
1520 epiphany_libcall_value (machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED)
|
|
1521 {
|
|
1522 return gen_rtx_REG (mode, 0);
|
|
1523 }
|
|
1524
|
|
1525 static bool
|
|
1526 epiphany_function_value_regno_p (const unsigned int regno ATTRIBUTE_UNUSED)
|
|
1527 {
|
|
1528 return regno == 0;
|
|
1529 }
|
|
1530
|
|
1531 /* Fix up invalid option settings. */
|
|
1532 static void
|
|
1533 epiphany_override_options (void)
|
|
1534 {
|
|
1535 if (epiphany_stack_offset < 4)
|
|
1536 error ("stack_offset must be at least 4");
|
|
1537 if (epiphany_stack_offset & 3)
|
|
1538 error ("stack_offset must be a multiple of 4");
|
|
1539 epiphany_stack_offset = (epiphany_stack_offset + 3) & -4;
|
|
1540 if (!TARGET_SOFT_CMPSF)
|
|
1541 flag_finite_math_only = 1;
|
|
1542
|
|
1543 /* This needs to be done at start up. It's convenient to do it here. */
|
|
1544 epiphany_init ();
|
|
1545 }
|
|
1546
|
|
1547 /* For a DImode load / store SET, make a SImode set for a
|
|
1548 REG_FRAME_RELATED_EXPR note, using OFFSET to create a high or lowpart
|
|
1549 subreg. */
|
|
1550 static rtx
|
|
1551 frame_subreg_note (rtx set, int offset)
|
|
1552 {
|
|
1553 rtx src = simplify_gen_subreg (SImode, SET_SRC (set), DImode, offset);
|
|
1554 rtx dst = simplify_gen_subreg (SImode, SET_DEST (set), DImode, offset);
|
|
1555
|
|
1556 set = gen_rtx_SET (dst ,src);
|
|
1557 RTX_FRAME_RELATED_P (set) = 1;
|
|
1558 return set;
|
|
1559 }
|
|
1560
|
|
1561 static rtx_insn *
|
|
1562 frame_insn (rtx x)
|
|
1563 {
|
|
1564 int i;
|
|
1565 rtx note = NULL_RTX;
|
|
1566 rtx_insn *insn;
|
|
1567
|
|
1568 if (GET_CODE (x) == PARALLEL)
|
|
1569 {
|
|
1570 rtx part = XVECEXP (x, 0, 0);
|
|
1571
|
|
1572 if (GET_MODE (SET_DEST (part)) == DImode)
|
|
1573 {
|
|
1574 note = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (XVECLEN (x, 0) + 1));
|
|
1575 XVECEXP (note, 0, 0) = frame_subreg_note (part, 0);
|
|
1576 XVECEXP (note, 0, 1) = frame_subreg_note (part, UNITS_PER_WORD);
|
|
1577 for (i = XVECLEN (x, 0) - 1; i >= 1; i--)
|
|
1578 {
|
|
1579 part = copy_rtx (XVECEXP (x, 0, i));
|
|
1580
|
|
1581 if (GET_CODE (part) == SET)
|
|
1582 RTX_FRAME_RELATED_P (part) = 1;
|
|
1583 XVECEXP (note, 0, i + 1) = part;
|
|
1584 }
|
|
1585 }
|
|
1586 else
|
|
1587 {
|
|
1588 for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
|
|
1589 {
|
|
1590 part = XVECEXP (x, 0, i);
|
|
1591
|
|
1592 if (GET_CODE (part) == SET)
|
|
1593 RTX_FRAME_RELATED_P (part) = 1;
|
|
1594 }
|
|
1595 }
|
|
1596 }
|
|
1597 else if (GET_CODE (x) == SET && GET_MODE (SET_DEST (x)) == DImode)
|
|
1598 note = gen_rtx_PARALLEL (VOIDmode,
|
|
1599 gen_rtvec (2, frame_subreg_note (x, 0),
|
|
1600 frame_subreg_note (x, UNITS_PER_WORD)));
|
|
1601 insn = emit_insn (x);
|
|
1602 RTX_FRAME_RELATED_P (insn) = 1;
|
|
1603 if (note)
|
|
1604 add_reg_note (insn, REG_FRAME_RELATED_EXPR, note);
|
|
1605 return insn;
|
|
1606 }
|
|
1607
|
|
1608 static rtx_insn *
|
|
1609 frame_move_insn (rtx to, rtx from)
|
|
1610 {
|
|
1611 return frame_insn (gen_rtx_SET (to, from));
|
|
1612 }
|
|
1613
|
|
1614 /* Generate a MEM referring to a varargs argument slot. */
|
|
1615
|
|
1616 static rtx
|
|
1617 gen_varargs_mem (machine_mode mode, rtx addr)
|
|
1618 {
|
|
1619 rtx mem = gen_rtx_MEM (mode, addr);
|
|
1620 MEM_NOTRAP_P (mem) = 1;
|
|
1621 set_mem_alias_set (mem, get_varargs_alias_set ());
|
|
1622 return mem;
|
|
1623 }
|
|
1624
|
|
1625 /* Emit instructions to save or restore registers in the range [MIN..LIMIT) .
|
|
1626 If EPILOGUE_P is 0, save; if it is one, restore.
|
|
1627 ADDR is the stack slot to save the first register to; subsequent
|
|
1628 registers are written to lower addresses.
|
|
1629 However, the order of register pairs can be reversed in order to
|
|
1630 use double-word load-store instructions. Likewise, an unpaired single
|
|
1631 word save slot can be skipped while double saves are carried out, and
|
|
1632 reused when a single register is to be saved. */
|
|
1633
|
|
1634 static void
|
|
1635 epiphany_emit_save_restore (int min, int limit, rtx addr, int epilogue_p)
|
|
1636 {
|
|
1637 int i;
|
|
1638 int stack_offset
|
|
1639 = current_frame_info.first_slot >= 0 ? epiphany_stack_offset : 0;
|
|
1640 rtx skipped_mem = NULL_RTX;
|
|
1641 int last_saved = limit - 1;
|
|
1642
|
|
1643 if (!optimize)
|
|
1644 while (last_saved >= 0
|
|
1645 && !TEST_HARD_REG_BIT (current_frame_info.gmask, last_saved))
|
|
1646 last_saved--;
|
|
1647 for (i = 0; i < limit; i++)
|
|
1648 {
|
|
1649 machine_mode mode = word_mode;
|
|
1650 rtx mem, reg;
|
|
1651 int n = i;
|
|
1652 rtx (*gen_mem) (machine_mode, rtx) = gen_frame_mem;
|
|
1653
|
|
1654 /* Make sure we push the arguments in the right order. */
|
|
1655 if (n < MAX_EPIPHANY_PARM_REGS && crtl->args.pretend_args_size)
|
|
1656 {
|
|
1657 n = MAX_EPIPHANY_PARM_REGS - 1 - n;
|
|
1658 gen_mem = gen_varargs_mem;
|
|
1659 }
|
|
1660 if (stack_offset == current_frame_info.first_slot_size
|
|
1661 && current_frame_info.first_slot >= 0)
|
|
1662 {
|
|
1663 if (current_frame_info.first_slot_size > UNITS_PER_WORD)
|
|
1664 {
|
|
1665 mode = DImode;
|
|
1666 addr = plus_constant (Pmode, addr,
|
|
1667 - (HOST_WIDE_INT) UNITS_PER_WORD);
|
|
1668 }
|
|
1669 if (i-- < min || !epilogue_p)
|
|
1670 goto next_slot;
|
|
1671 n = current_frame_info.first_slot;
|
|
1672 gen_mem = gen_frame_mem;
|
|
1673 }
|
|
1674 else if (n == UNKNOWN_REGNUM
|
|
1675 && stack_offset > current_frame_info.first_slot_size)
|
|
1676 {
|
|
1677 i--;
|
|
1678 goto next_slot;
|
|
1679 }
|
|
1680 else if (!TEST_HARD_REG_BIT (current_frame_info.gmask, n))
|
|
1681 continue;
|
|
1682 else if (i < min)
|
|
1683 goto next_slot;
|
|
1684
|
|
1685 /* Check for a register pair to save. */
|
|
1686 if (n == i
|
|
1687 && (n >= MAX_EPIPHANY_PARM_REGS || crtl->args.pretend_args_size == 0)
|
|
1688 && (n & 1) == 0 && n+1 < limit
|
|
1689 && TEST_HARD_REG_BIT (current_frame_info.gmask, n+1))
|
|
1690 {
|
|
1691 /* If it fits in the current stack slot pair, place it there. */
|
|
1692 if (GET_CODE (addr) == PLUS && (stack_offset & 7) == 0
|
|
1693 && stack_offset != 2 * UNITS_PER_WORD
|
|
1694 && (current_frame_info.last_slot < 0
|
|
1695 || INTVAL (XEXP (addr, 1)) != UNITS_PER_WORD)
|
|
1696 && (n+1 != last_saved || !skipped_mem))
|
|
1697 {
|
|
1698 mode = DImode;
|
|
1699 i++;
|
|
1700 addr = plus_constant (Pmode, addr,
|
|
1701 - (HOST_WIDE_INT) UNITS_PER_WORD);
|
|
1702 }
|
|
1703 /* If it fits in the following stack slot pair, that's fine, too. */
|
|
1704 else if (GET_CODE (addr) == PLUS && (stack_offset & 7) == 4
|
|
1705 && stack_offset != 2 * UNITS_PER_WORD
|
|
1706 && stack_offset != 3 * UNITS_PER_WORD
|
|
1707 && (current_frame_info.last_slot < 0
|
|
1708 || INTVAL (XEXP (addr, 1)) != 2 * UNITS_PER_WORD)
|
|
1709 && n + 1 != last_saved)
|
|
1710 {
|
|
1711 gcc_assert (!skipped_mem);
|
|
1712 stack_offset -= GET_MODE_SIZE (mode);
|
|
1713 skipped_mem = gen_mem (mode, addr);
|
|
1714 mode = DImode;
|
|
1715 i++;
|
|
1716 addr = plus_constant (Pmode, addr,
|
|
1717 - (HOST_WIDE_INT) 2 * UNITS_PER_WORD);
|
|
1718 }
|
|
1719 }
|
|
1720 reg = gen_rtx_REG (mode, n);
|
|
1721 if (mode != DImode && skipped_mem)
|
|
1722 mem = skipped_mem;
|
|
1723 else
|
|
1724 mem = gen_mem (mode, addr);
|
|
1725
|
|
1726 /* If we are loading / storing LR, note the offset that
|
|
1727 gen_reload_insi_ra requires. Since GPR_LR is even,
|
|
1728 we only need to test n, even if mode is DImode. */
|
|
1729 gcc_assert ((GPR_LR & 1) == 0);
|
|
1730 if (n == GPR_LR)
|
|
1731 {
|
|
1732 long lr_slot_offset = 0;
|
|
1733 rtx m_addr = XEXP (mem, 0);
|
|
1734
|
|
1735 if (GET_CODE (m_addr) == PLUS)
|
|
1736 lr_slot_offset = INTVAL (XEXP (m_addr, 1));
|
|
1737 if (frame_pointer_needed)
|
|
1738 lr_slot_offset += (current_frame_info.first_slot_offset
|
|
1739 - current_frame_info.total_size);
|
|
1740 if (MACHINE_FUNCTION (cfun)->lr_slot_known)
|
|
1741 gcc_assert (MACHINE_FUNCTION (cfun)->lr_slot_offset
|
|
1742 == lr_slot_offset);
|
|
1743 MACHINE_FUNCTION (cfun)->lr_slot_offset = lr_slot_offset;
|
|
1744 MACHINE_FUNCTION (cfun)->lr_slot_known = 1;
|
|
1745 }
|
|
1746
|
|
1747 if (!epilogue_p)
|
|
1748 frame_move_insn (mem, reg);
|
|
1749 else if (n >= MAX_EPIPHANY_PARM_REGS || !crtl->args.pretend_args_size)
|
|
1750 emit_move_insn (reg, mem);
|
|
1751 if (mem == skipped_mem)
|
|
1752 {
|
|
1753 skipped_mem = NULL_RTX;
|
|
1754 continue;
|
|
1755 }
|
|
1756 next_slot:
|
|
1757 addr = plus_constant (Pmode, addr, -(HOST_WIDE_INT) UNITS_PER_WORD);
|
|
1758 stack_offset -= GET_MODE_SIZE (mode);
|
|
1759 }
|
|
1760 }
|
|
1761
|
|
1762 void
|
|
1763 epiphany_expand_prologue (void)
|
|
1764 {
|
|
1765 int interrupt_p;
|
|
1766 enum epiphany_function_type fn_type;
|
|
1767 rtx addr, mem, off, reg;
|
|
1768
|
|
1769 if (!current_frame_info.initialized)
|
|
1770 epiphany_compute_frame_size (get_frame_size ());
|
|
1771
|
|
1772 /* It is debatable if we should adjust this by epiphany_stack_offset. */
|
|
1773 if (flag_stack_usage_info)
|
|
1774 current_function_static_stack_size = current_frame_info.total_size;
|
|
1775
|
|
1776 fn_type = epiphany_compute_function_type (current_function_decl);
|
|
1777 interrupt_p = EPIPHANY_INTERRUPT_P (fn_type);
|
|
1778
|
|
1779 if (interrupt_p)
|
|
1780 {
|
|
1781 addr = plus_constant (Pmode, stack_pointer_rtx,
|
|
1782 - (HOST_WIDE_INT) 2 * UNITS_PER_WORD);
|
|
1783 if (!lookup_attribute ("forwarder_section",
|
|
1784 DECL_ATTRIBUTES (current_function_decl))
|
|
1785 || !epiphany_is_long_call_p (XEXP (DECL_RTL (current_function_decl),
|
|
1786 0)))
|
|
1787 frame_move_insn (gen_frame_mem (DImode, addr),
|
|
1788 gen_rtx_REG (DImode, GPR_0));
|
|
1789 frame_move_insn (gen_rtx_REG (SImode, GPR_0),
|
|
1790 gen_rtx_REG (word_mode, STATUS_REGNUM));
|
|
1791 frame_move_insn (gen_rtx_REG (SImode, GPR_1),
|
|
1792 gen_rtx_REG (word_mode, IRET_REGNUM));
|
|
1793 mem = gen_frame_mem (BLKmode, stack_pointer_rtx);
|
|
1794 off = GEN_INT (-current_frame_info.first_slot_offset);
|
|
1795 frame_insn (gen_stack_adjust_add (off, mem));
|
|
1796 if (!epiphany_uninterruptible_p (current_function_decl))
|
|
1797 emit_insn (gen_gie ());
|
|
1798 addr = plus_constant (Pmode, stack_pointer_rtx,
|
|
1799 current_frame_info.first_slot_offset
|
|
1800 - (HOST_WIDE_INT) 3 * UNITS_PER_WORD);
|
|
1801 }
|
|
1802 else
|
|
1803 {
|
|
1804 addr = plus_constant (Pmode, stack_pointer_rtx,
|
|
1805 epiphany_stack_offset
|
|
1806 - (HOST_WIDE_INT) UNITS_PER_WORD);
|
|
1807 epiphany_emit_save_restore (0, current_frame_info.small_threshold,
|
|
1808 addr, 0);
|
|
1809 /* Allocate register save area; for small to medium size frames,
|
|
1810 allocate the entire frame; this is joint with one register save. */
|
|
1811 if (current_frame_info.first_slot >= 0)
|
|
1812 {
|
|
1813 machine_mode mode
|
|
1814 = (current_frame_info.first_slot_size == UNITS_PER_WORD
|
|
1815 ? word_mode : DImode);
|
|
1816
|
|
1817 off = GEN_INT (-current_frame_info.first_slot_offset);
|
|
1818 mem = gen_frame_mem (BLKmode,
|
|
1819 gen_rtx_PLUS (Pmode, stack_pointer_rtx, off));
|
|
1820 frame_insn (gen_stack_adjust_str
|
|
1821 (gen_frame_mem (mode, stack_pointer_rtx),
|
|
1822 gen_rtx_REG (mode, current_frame_info.first_slot),
|
|
1823 off, mem));
|
|
1824 addr = plus_constant (Pmode, addr,
|
|
1825 current_frame_info.first_slot_offset);
|
|
1826 }
|
|
1827 }
|
|
1828 epiphany_emit_save_restore (current_frame_info.small_threshold,
|
|
1829 FIRST_PSEUDO_REGISTER, addr, 0);
|
|
1830 if (current_frame_info.need_fp)
|
|
1831 frame_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
|
|
1832 /* For large frames, allocate bulk of frame. This is usually joint with one
|
|
1833 register save. */
|
|
1834 if (current_frame_info.last_slot >= 0)
|
|
1835 {
|
|
1836 rtx ip, mem2, note;
|
|
1837 rtx_insn *insn;
|
|
1838
|
|
1839 gcc_assert (current_frame_info.last_slot != GPR_FP
|
|
1840 || (!current_frame_info.need_fp
|
|
1841 && current_frame_info.first_slot < 0));
|
|
1842 off = GEN_INT (-current_frame_info.last_slot_offset);
|
|
1843 mem = gen_frame_mem (BLKmode,
|
|
1844 gen_rtx_PLUS (Pmode, stack_pointer_rtx, off));
|
|
1845 ip = gen_rtx_REG (Pmode, GPR_IP);
|
|
1846 frame_move_insn (ip, off);
|
|
1847 reg = gen_rtx_REG (word_mode, current_frame_info.last_slot),
|
|
1848 mem2 = gen_frame_mem (word_mode, stack_pointer_rtx),
|
|
1849 insn = frame_insn (gen_stack_adjust_str (mem2, reg, ip, mem));
|
|
1850 /* Instruction scheduling can separate the instruction setting IP from
|
|
1851 INSN so that dwarf2out_frame_debug_expr becomes confused what the
|
|
1852 temporary register is. Example: _gcov.o */
|
|
1853 note = gen_rtx_SET (stack_pointer_rtx,
|
|
1854 gen_rtx_PLUS (Pmode, stack_pointer_rtx, off));
|
|
1855 note = gen_rtx_PARALLEL (VOIDmode,
|
|
1856 gen_rtvec (2, gen_rtx_SET (mem2, reg), note));
|
|
1857 add_reg_note (insn, REG_FRAME_RELATED_EXPR, note);
|
|
1858 }
|
|
1859 /* If there is only one or no register to save, yet we have a large frame,
|
|
1860 use an add. */
|
|
1861 else if (current_frame_info.last_slot_offset)
|
|
1862 {
|
|
1863 mem = gen_frame_mem (BLKmode,
|
|
1864 plus_constant (Pmode, stack_pointer_rtx,
|
|
1865 current_frame_info.last_slot_offset));
|
|
1866 off = GEN_INT (-current_frame_info.last_slot_offset);
|
|
1867 if (!SIMM11 (INTVAL (off)))
|
|
1868 {
|
|
1869 reg = gen_rtx_REG (Pmode, GPR_IP);
|
|
1870 frame_move_insn (reg, off);
|
|
1871 off = reg;
|
|
1872 }
|
|
1873 frame_insn (gen_stack_adjust_add (off, mem));
|
|
1874 }
|
|
1875 }
|
|
1876
|
|
1877 void
|
|
1878 epiphany_expand_epilogue (int sibcall_p)
|
|
1879 {
|
|
1880 int interrupt_p;
|
|
1881 enum epiphany_function_type fn_type;
|
|
1882 rtx mem, addr, reg, off;
|
|
1883 HOST_WIDE_INT restore_offset;
|
|
1884
|
|
1885 fn_type = epiphany_compute_function_type( current_function_decl);
|
|
1886 interrupt_p = EPIPHANY_INTERRUPT_P (fn_type);
|
|
1887
|
|
1888 /* For variable frames, deallocate bulk of frame. */
|
|
1889 if (current_frame_info.need_fp)
|
|
1890 {
|
|
1891 mem = gen_frame_mem (BLKmode, stack_pointer_rtx);
|
|
1892 emit_insn (gen_stack_adjust_mov (mem));
|
|
1893 }
|
|
1894 /* Else for large static frames, deallocate bulk of frame. */
|
|
1895 else if (current_frame_info.last_slot_offset)
|
|
1896 {
|
|
1897 mem = gen_frame_mem (BLKmode, stack_pointer_rtx);
|
|
1898 reg = gen_rtx_REG (Pmode, GPR_IP);
|
|
1899 emit_move_insn (reg, GEN_INT (current_frame_info.last_slot_offset));
|
|
1900 emit_insn (gen_stack_adjust_add (reg, mem));
|
|
1901 }
|
|
1902 restore_offset = (interrupt_p
|
|
1903 ? - 3 * UNITS_PER_WORD
|
|
1904 : epiphany_stack_offset - (HOST_WIDE_INT) UNITS_PER_WORD);
|
|
1905 addr = plus_constant (Pmode, stack_pointer_rtx,
|
|
1906 (current_frame_info.first_slot_offset
|
|
1907 + restore_offset));
|
|
1908 epiphany_emit_save_restore (current_frame_info.small_threshold,
|
|
1909 FIRST_PSEUDO_REGISTER, addr, 1);
|
|
1910
|
|
1911 if (interrupt_p && !epiphany_uninterruptible_p (current_function_decl))
|
|
1912 emit_insn (gen_gid ());
|
|
1913
|
|
1914 off = GEN_INT (current_frame_info.first_slot_offset);
|
|
1915 mem = gen_frame_mem (BLKmode, stack_pointer_rtx);
|
|
1916 /* For large / variable size frames, deallocating the register save area is
|
|
1917 joint with one register restore; for medium size frames, we use a
|
|
1918 dummy post-increment load to dealloacte the whole frame. */
|
|
1919 if (!SIMM11 (INTVAL (off)) || current_frame_info.last_slot >= 0)
|
|
1920 {
|
|
1921 emit_insn (gen_stack_adjust_ldr
|
|
1922 (gen_rtx_REG (word_mode,
|
|
1923 (current_frame_info.last_slot >= 0
|
|
1924 ? current_frame_info.last_slot : GPR_IP)),
|
|
1925 gen_frame_mem (word_mode, stack_pointer_rtx),
|
|
1926 off,
|
|
1927 mem));
|
|
1928 }
|
|
1929 /* While for small frames, we deallocate the entire frame with one add. */
|
|
1930 else if (INTVAL (off))
|
|
1931 {
|
|
1932 emit_insn (gen_stack_adjust_add (off, mem));
|
|
1933 }
|
|
1934 if (interrupt_p)
|
|
1935 {
|
|
1936 emit_move_insn (gen_rtx_REG (word_mode, STATUS_REGNUM),
|
|
1937 gen_rtx_REG (SImode, GPR_0));
|
|
1938 emit_move_insn (gen_rtx_REG (word_mode, IRET_REGNUM),
|
|
1939 gen_rtx_REG (SImode, GPR_1));
|
|
1940 addr = plus_constant (Pmode, stack_pointer_rtx,
|
|
1941 - (HOST_WIDE_INT) 2 * UNITS_PER_WORD);
|
|
1942 emit_move_insn (gen_rtx_REG (DImode, GPR_0),
|
|
1943 gen_frame_mem (DImode, addr));
|
|
1944 }
|
|
1945 addr = plus_constant (Pmode, stack_pointer_rtx,
|
|
1946 epiphany_stack_offset - (HOST_WIDE_INT) UNITS_PER_WORD);
|
|
1947 epiphany_emit_save_restore (0, current_frame_info.small_threshold, addr, 1);
|
|
1948 if (!sibcall_p)
|
|
1949 {
|
|
1950 if (interrupt_p)
|
|
1951 emit_jump_insn (gen_return_internal_interrupt());
|
|
1952 else
|
|
1953 emit_jump_insn (gen_return_i ());
|
|
1954 }
|
|
1955 }
|
|
1956
|
|
1957 int
|
|
1958 epiphany_initial_elimination_offset (int from, int to)
|
|
1959 {
|
|
1960 epiphany_compute_frame_size (get_frame_size ());
|
|
1961 if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
|
|
1962 return current_frame_info.total_size - current_frame_info.reg_size;
|
|
1963 if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
|
|
1964 return current_frame_info.first_slot_offset - current_frame_info.reg_size;
|
|
1965 if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
|
|
1966 return (current_frame_info.total_size
|
|
1967 - ((current_frame_info.pretend_size + 4) & -8));
|
|
1968 if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
|
|
1969 return (current_frame_info.first_slot_offset
|
|
1970 - ((current_frame_info.pretend_size + 4) & -8));
|
|
1971 gcc_unreachable ();
|
|
1972 }
|
|
1973
|
|
1974 bool
|
|
1975 epiphany_regno_rename_ok (unsigned, unsigned dst)
|
|
1976 {
|
|
1977 enum epiphany_function_type fn_type;
|
|
1978
|
|
1979 fn_type = epiphany_compute_function_type (current_function_decl);
|
|
1980 if (!EPIPHANY_INTERRUPT_P (fn_type))
|
|
1981 return true;
|
|
1982 if (df_regs_ever_live_p (dst))
|
|
1983 return true;
|
|
1984 return false;
|
|
1985 }
|
|
1986
|
|
1987 static int
|
|
1988 epiphany_issue_rate (void)
|
|
1989 {
|
|
1990 return 2;
|
|
1991 }
|
|
1992
|
|
1993 /* Function to update the integer COST
|
|
1994 based on the relationship between INSN that is dependent on
|
|
1995 DEP_INSN through the dependence LINK. The default is to make no
|
|
1996 adjustment to COST. This can be used for example to specify to
|
|
1997 the scheduler that an output- or anti-dependence does not incur
|
|
1998 the same cost as a data-dependence. The return value should be
|
|
1999 the new value for COST. */
|
|
2000 static int
|
|
2001 epiphany_adjust_cost (rtx_insn *insn, int dep_type, rtx_insn *dep_insn,
|
|
2002 int cost, unsigned int)
|
|
2003 {
|
|
2004 if (dep_type == 0)
|
|
2005 {
|
|
2006 rtx dep_set;
|
|
2007
|
|
2008 if (recog_memoized (insn) < 0
|
|
2009 || recog_memoized (dep_insn) < 0)
|
|
2010 return cost;
|
|
2011
|
|
2012 dep_set = single_set (dep_insn);
|
|
2013
|
|
2014 /* The latency that we specify in the scheduling description refers
|
|
2015 to the actual output, not to an auto-increment register; for that,
|
|
2016 the latency is one. */
|
|
2017 if (dep_set && MEM_P (SET_SRC (dep_set)) && cost > 1)
|
|
2018 {
|
|
2019 rtx set = single_set (insn);
|
|
2020
|
|
2021 if (set
|
|
2022 && !reg_overlap_mentioned_p (SET_DEST (dep_set), SET_SRC (set))
|
|
2023 && (!MEM_P (SET_DEST (set))
|
|
2024 || !reg_overlap_mentioned_p (SET_DEST (dep_set),
|
|
2025 XEXP (SET_DEST (set), 0))))
|
|
2026 cost = 1;
|
|
2027 }
|
|
2028 }
|
|
2029 return cost;
|
|
2030 }
|
|
2031
|
|
2032 #define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_BASE_P (X)
|
|
2033
|
|
2034 #define RTX_OK_FOR_BASE_P(X) \
|
|
2035 (REG_P (X) && REG_OK_FOR_BASE_P (X))
|
|
2036
|
|
2037 #define RTX_OK_FOR_INDEX_P(MODE, X) \
|
|
2038 ((GET_MODE_CLASS (MODE) != MODE_VECTOR_INT \
|
|
2039 || epiphany_vect_align >= GET_MODE_SIZE (MODE)) \
|
|
2040 && (REG_P (X) && REG_OK_FOR_INDEX_P (X)))
|
|
2041
|
|
2042 #define LEGITIMATE_OFFSET_ADDRESS_P(MODE, X) \
|
|
2043 (GET_CODE (X) == PLUS \
|
|
2044 && RTX_OK_FOR_BASE_P (XEXP (X, 0)) \
|
|
2045 && (RTX_OK_FOR_INDEX_P (MODE, XEXP (X, 1)) \
|
|
2046 || RTX_OK_FOR_OFFSET_P (MODE, XEXP (X, 1))))
|
|
2047
|
|
2048 static bool
|
|
2049 epiphany_legitimate_address_p (machine_mode mode, rtx x, bool strict)
|
|
2050 {
|
|
2051 #define REG_OK_FOR_BASE_P(X) \
|
|
2052 (strict ? GPR_P (REGNO (X)) : GPR_AP_OR_PSEUDO_P (REGNO (X)))
|
|
2053 if (RTX_OK_FOR_BASE_P (x))
|
|
2054 return true;
|
|
2055 if (RTX_FRAME_OFFSET_P (x))
|
|
2056 return true;
|
|
2057 if (LEGITIMATE_OFFSET_ADDRESS_P (mode, x))
|
|
2058 return true;
|
|
2059 /* If this is a misaligned stack access, don't force it to reg+index. */
|
|
2060 if (GET_MODE_SIZE (mode) == 8
|
|
2061 && GET_CODE (x) == PLUS && XEXP (x, 0) == stack_pointer_rtx
|
|
2062 /* Decomposed to SImode; GET_MODE_SIZE (SImode) == 4 */
|
|
2063 && !(INTVAL (XEXP (x, 1)) & 3)
|
|
2064 && INTVAL (XEXP (x, 1)) >= -2047 * 4
|
|
2065 && INTVAL (XEXP (x, 1)) <= 2046 * 4)
|
|
2066 return true;
|
|
2067 if (TARGET_POST_INC
|
|
2068 && (GET_CODE (x) == POST_DEC || GET_CODE (x) == POST_INC)
|
|
2069 && RTX_OK_FOR_BASE_P (XEXP ((x), 0)))
|
|
2070 return true;
|
|
2071 if ((TARGET_POST_MODIFY || reload_completed)
|
|
2072 && GET_CODE (x) == POST_MODIFY
|
|
2073 && GET_CODE (XEXP ((x), 1)) == PLUS
|
|
2074 && rtx_equal_p (XEXP ((x), 0), XEXP (XEXP ((x), 1), 0))
|
|
2075 && LEGITIMATE_OFFSET_ADDRESS_P (mode, XEXP ((x), 1)))
|
|
2076 return true;
|
|
2077 if (mode == BLKmode)
|
|
2078 return epiphany_legitimate_address_p (SImode, x, strict);
|
|
2079 return false;
|
|
2080 }
|
|
2081
|
|
2082 static reg_class_t
|
|
2083 epiphany_secondary_reload (bool in_p, rtx x, reg_class_t rclass,
|
|
2084 machine_mode mode ATTRIBUTE_UNUSED,
|
|
2085 secondary_reload_info *sri)
|
|
2086 {
|
|
2087 /* This could give more reload inheritance, but we are missing some
|
|
2088 reload infrastructure. */
|
|
2089 if (0)
|
|
2090 if (in_p && GET_CODE (x) == UNSPEC
|
|
2091 && satisfies_constraint_Sra (x) && !satisfies_constraint_Rra (x))
|
|
2092 {
|
|
2093 gcc_assert (rclass == GENERAL_REGS);
|
|
2094 sri->icode = CODE_FOR_reload_insi_ra;
|
|
2095 return NO_REGS;
|
|
2096 }
|
|
2097 return NO_REGS;
|
|
2098 }
|
|
2099
|
|
2100 bool
|
|
2101 epiphany_is_long_call_p (rtx x)
|
|
2102 {
|
|
2103 tree decl = SYMBOL_REF_DECL (x);
|
|
2104 bool ret_val = !TARGET_SHORT_CALLS;
|
|
2105 tree attrs;
|
|
2106
|
|
2107 /* ??? Is it safe to default to ret_val if decl is NULL? We should
|
|
2108 probably encode information via encode_section_info, and also
|
|
2109 have (an) option(s) to take SYMBOL_FLAG_LOCAL and/or SYMBOL_FLAG_EXTERNAL
|
|
2110 into account. */
|
|
2111 if (decl)
|
|
2112 {
|
|
2113 attrs = TYPE_ATTRIBUTES (TREE_TYPE (decl));
|
|
2114 if (lookup_attribute ("long_call", attrs))
|
|
2115 ret_val = true;
|
|
2116 else if (lookup_attribute ("short_call", attrs))
|
|
2117 ret_val = false;
|
|
2118 }
|
|
2119 return ret_val;
|
|
2120 }
|
|
2121
|
|
2122 bool
|
|
2123 epiphany_small16 (rtx x)
|
|
2124 {
|
|
2125 rtx base = x;
|
|
2126 rtx offs ATTRIBUTE_UNUSED = const0_rtx;
|
|
2127
|
|
2128 if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS)
|
|
2129 {
|
|
2130 base = XEXP (XEXP (x, 0), 0);
|
|
2131 offs = XEXP (XEXP (x, 0), 1);
|
|
2132 }
|
|
2133 if (GET_CODE (base) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (base)
|
|
2134 && epiphany_is_long_call_p (base))
|
|
2135 return false;
|
|
2136 return TARGET_SMALL16 != 0;
|
|
2137 }
|
|
2138
|
|
2139 /* Return nonzero if it is ok to make a tail-call to DECL. */
|
|
2140 static bool
|
|
2141 epiphany_function_ok_for_sibcall (tree decl, tree exp)
|
|
2142 {
|
|
2143 bool cfun_interrupt_p, call_interrupt_p;
|
|
2144
|
|
2145 cfun_interrupt_p = EPIPHANY_INTERRUPT_P (epiphany_compute_function_type
|
|
2146 (current_function_decl));
|
|
2147 if (decl)
|
|
2148 call_interrupt_p = EPIPHANY_INTERRUPT_P (epiphany_compute_function_type (decl));
|
|
2149 else
|
|
2150 {
|
|
2151 tree fn_type = TREE_TYPE (CALL_EXPR_FN (exp));
|
|
2152
|
|
2153 gcc_assert (POINTER_TYPE_P (fn_type));
|
|
2154 fn_type = TREE_TYPE (fn_type);
|
|
2155 gcc_assert (TREE_CODE (fn_type) == FUNCTION_TYPE
|
|
2156 || TREE_CODE (fn_type) == METHOD_TYPE);
|
|
2157 call_interrupt_p
|
|
2158 = lookup_attribute ("interrupt", TYPE_ATTRIBUTES (fn_type)) != NULL;
|
|
2159 }
|
|
2160
|
|
2161 /* Don't tailcall from or to an ISR routine - although we could in
|
|
2162 principle tailcall from one ISR routine to another, we'd need to
|
|
2163 handle this in sibcall_epilogue to make it work. */
|
|
2164 if (cfun_interrupt_p || call_interrupt_p)
|
|
2165 return false;
|
|
2166
|
|
2167 /* Everything else is ok. */
|
|
2168 return true;
|
|
2169 }
|
|
2170
|
|
2171 /* T is a function declaration or the MEM_EXPR of a MEM passed to a call
|
|
2172 expander.
|
|
2173 Return true iff the type of T has the uninterruptible attribute.
|
|
2174 If T is NULL, return false. */
|
|
2175 bool
|
|
2176 epiphany_uninterruptible_p (tree t)
|
|
2177 {
|
|
2178 tree attrs;
|
|
2179
|
|
2180 if (t)
|
|
2181 {
|
|
2182 attrs = TYPE_ATTRIBUTES (TREE_TYPE (t));
|
|
2183 if (lookup_attribute ("disinterrupt", attrs))
|
|
2184 return true;
|
|
2185 }
|
|
2186 return false;
|
|
2187 }
|
|
2188
|
|
2189 bool
|
|
2190 epiphany_call_uninterruptible_p (rtx mem)
|
|
2191 {
|
|
2192 rtx addr = XEXP (mem, 0);
|
|
2193 tree t = NULL_TREE;
|
|
2194
|
|
2195 if (GET_CODE (addr) == SYMBOL_REF)
|
|
2196 t = SYMBOL_REF_DECL (addr);
|
|
2197 if (!t)
|
|
2198 t = MEM_EXPR (mem);
|
|
2199 return epiphany_uninterruptible_p (t);
|
|
2200 }
|
|
2201
|
|
2202 static machine_mode
|
|
2203 epiphany_promote_function_mode (const_tree type, machine_mode mode,
|
|
2204 int *punsignedp ATTRIBUTE_UNUSED,
|
|
2205 const_tree funtype ATTRIBUTE_UNUSED,
|
|
2206 int for_return ATTRIBUTE_UNUSED)
|
|
2207 {
|
|
2208 int dummy;
|
|
2209
|
|
2210 return promote_mode (type, mode, &dummy);
|
|
2211 }
|
|
2212
|
|
2213 static void
|
|
2214 epiphany_conditional_register_usage (void)
|
|
2215 {
|
|
2216 int i;
|
|
2217
|
|
2218 if (PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM)
|
|
2219 {
|
|
2220 fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
|
|
2221 call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
|
|
2222 }
|
|
2223 if (TARGET_HALF_REG_FILE)
|
|
2224 {
|
|
2225 for (i = 32; i <= 63; i++)
|
|
2226 {
|
|
2227 fixed_regs[i] = 1;
|
|
2228 call_used_regs[i] = 1;
|
|
2229 }
|
|
2230 }
|
|
2231 if (epiphany_m1reg >= 0)
|
|
2232 {
|
|
2233 fixed_regs[epiphany_m1reg] = 1;
|
|
2234 call_used_regs[epiphany_m1reg] = 1;
|
|
2235 }
|
|
2236 if (!TARGET_PREFER_SHORT_INSN_REGS)
|
|
2237 CLEAR_HARD_REG_SET (reg_class_contents[SHORT_INSN_REGS]);
|
|
2238 COPY_HARD_REG_SET (reg_class_contents[SIBCALL_REGS],
|
|
2239 reg_class_contents[GENERAL_REGS]);
|
|
2240 /* It would be simpler and quicker if we could just use
|
|
2241 AND_COMPL_HARD_REG_SET, alas, call_used_reg_set is yet uninitialized;
|
|
2242 it is set up later by our caller. */
|
|
2243 for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
|
2244 if (!call_used_regs[i])
|
|
2245 CLEAR_HARD_REG_BIT (reg_class_contents[SIBCALL_REGS], i);
|
|
2246 }
|
|
2247
|
|
2248 /* Determine where to put an argument to a function.
|
|
2249 Value is zero to push the argument on the stack,
|
|
2250 or a hard register in which to store the argument.
|
|
2251
|
|
2252 MODE is the argument's machine mode.
|
|
2253 TYPE is the data type of the argument (as a tree).
|
|
2254 This is null for libcalls where that information may
|
|
2255 not be available.
|
|
2256 CUM is a variable of type CUMULATIVE_ARGS which gives info about
|
|
2257 the preceding args and about the function being called.
|
|
2258 NAMED is nonzero if this argument is a named parameter
|
|
2259 (otherwise it is an extra parameter matching an ellipsis). */
|
|
2260 /* On the EPIPHANY the first MAX_EPIPHANY_PARM_REGS args are normally in
|
|
2261 registers and the rest are pushed. */
|
|
2262 static rtx
|
|
2263 epiphany_function_arg (cumulative_args_t cum_v, machine_mode mode,
|
|
2264 const_tree type, bool named ATTRIBUTE_UNUSED)
|
|
2265 {
|
|
2266 CUMULATIVE_ARGS cum = *get_cumulative_args (cum_v);
|
|
2267
|
|
2268 if (PASS_IN_REG_P (cum, mode, type))
|
|
2269 return gen_rtx_REG (mode, ROUND_ADVANCE_CUM (cum, mode, type));
|
|
2270 return 0;
|
|
2271 }
|
|
2272
|
|
2273 /* Update the data in CUM to advance over an argument
|
|
2274 of mode MODE and data type TYPE.
|
|
2275 (TYPE is null for libcalls where that information may not be available.) */
|
|
2276 static void
|
|
2277 epiphany_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
|
|
2278 const_tree type, bool named ATTRIBUTE_UNUSED)
|
|
2279 {
|
|
2280 CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
|
|
2281
|
|
2282 *cum = ROUND_ADVANCE_CUM (*cum, mode, type) + ROUND_ADVANCE_ARG (mode, type);
|
|
2283 }
|
|
2284
|
|
2285 /* Nested function support.
|
|
2286 An epiphany trampoline looks like this:
|
|
2287 mov r16,%low(fnaddr)
|
|
2288 movt r16,%high(fnaddr)
|
|
2289 mov ip,%low(cxt)
|
|
2290 movt ip,%high(cxt)
|
|
2291 jr r16 */
|
|
2292
|
|
2293 #define EPIPHANY_LOW_RTX(X) \
|
|
2294 (gen_rtx_IOR (SImode, \
|
|
2295 gen_rtx_ASHIFT (SImode, \
|
|
2296 gen_rtx_AND (SImode, (X), GEN_INT (0xff)), GEN_INT (5)), \
|
|
2297 gen_rtx_ASHIFT (SImode, \
|
|
2298 gen_rtx_AND (SImode, (X), GEN_INT (0xff00)), GEN_INT (12))))
|
|
2299 #define EPIPHANY_HIGH_RTX(X) \
|
|
2300 EPIPHANY_LOW_RTX (gen_rtx_LSHIFTRT (SImode, (X), GEN_INT (16)))
|
|
2301
|
|
2302 /* Emit RTL insns to initialize the variable parts of a trampoline.
|
|
2303 FNADDR is an RTX for the address of the function's pure code.
|
|
2304 CXT is an RTX for the static chain value for the function. */
|
|
2305 static void
|
|
2306 epiphany_trampoline_init (rtx tramp_mem, tree fndecl, rtx cxt)
|
|
2307 {
|
|
2308 rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
|
|
2309 rtx tramp = force_reg (Pmode, XEXP (tramp_mem, 0));
|
|
2310
|
|
2311 emit_move_insn (gen_rtx_MEM (SImode, plus_constant (Pmode, tramp, 0)),
|
|
2312 gen_rtx_IOR (SImode, GEN_INT (0x4002000b),
|
|
2313 EPIPHANY_LOW_RTX (fnaddr)));
|
|
2314 emit_move_insn (gen_rtx_MEM (SImode, plus_constant (Pmode, tramp, 4)),
|
|
2315 gen_rtx_IOR (SImode, GEN_INT (0x5002000b),
|
|
2316 EPIPHANY_HIGH_RTX (fnaddr)));
|
|
2317 emit_move_insn (gen_rtx_MEM (SImode, plus_constant (Pmode, tramp, 8)),
|
|
2318 gen_rtx_IOR (SImode, GEN_INT (0x2002800b),
|
|
2319 EPIPHANY_LOW_RTX (cxt)));
|
|
2320 emit_move_insn (gen_rtx_MEM (SImode, plus_constant (Pmode, tramp, 12)),
|
|
2321 gen_rtx_IOR (SImode, GEN_INT (0x3002800b),
|
|
2322 EPIPHANY_HIGH_RTX (cxt)));
|
|
2323 emit_move_insn (gen_rtx_MEM (SImode, plus_constant (Pmode, tramp, 16)),
|
|
2324 GEN_INT (0x0802014f));
|
|
2325 }
|
|
2326
|
|
2327 bool
|
|
2328 epiphany_optimize_mode_switching (int entity)
|
|
2329 {
|
|
2330 if (MACHINE_FUNCTION (cfun)->sw_entities_processed & (1 << entity))
|
|
2331 return false;
|
|
2332 switch (entity)
|
|
2333 {
|
|
2334 case EPIPHANY_MSW_ENTITY_AND:
|
|
2335 case EPIPHANY_MSW_ENTITY_OR:
|
|
2336 case EPIPHANY_MSW_ENTITY_CONFIG:
|
|
2337 return true;
|
|
2338 case EPIPHANY_MSW_ENTITY_NEAREST:
|
|
2339 case EPIPHANY_MSW_ENTITY_TRUNC:
|
|
2340 return optimize > 0;
|
|
2341 case EPIPHANY_MSW_ENTITY_ROUND_UNKNOWN:
|
|
2342 return MACHINE_FUNCTION (cfun)->unknown_mode_uses != 0;
|
|
2343 case EPIPHANY_MSW_ENTITY_ROUND_KNOWN:
|
|
2344 return (MACHINE_FUNCTION (cfun)->sw_entities_processed
|
|
2345 & (1 << EPIPHANY_MSW_ENTITY_ROUND_UNKNOWN)) != 0;
|
|
2346 case EPIPHANY_MSW_ENTITY_FPU_OMNIBUS:
|
|
2347 return optimize == 0 || current_pass == pass_mode_switch_use;
|
|
2348 }
|
|
2349 gcc_unreachable ();
|
|
2350 }
|
|
2351
|
|
2352 static int
|
|
2353 epiphany_mode_priority (int entity, int priority)
|
|
2354 {
|
|
2355 if (entity == EPIPHANY_MSW_ENTITY_AND || entity == EPIPHANY_MSW_ENTITY_OR
|
|
2356 || entity== EPIPHANY_MSW_ENTITY_CONFIG)
|
|
2357 return priority;
|
|
2358 if (priority > 3)
|
|
2359 switch (priority)
|
|
2360 {
|
|
2361 case 4: return FP_MODE_ROUND_UNKNOWN;
|
|
2362 case 5: return FP_MODE_NONE;
|
|
2363 default: gcc_unreachable ();
|
|
2364 }
|
|
2365 switch ((enum attr_fp_mode) epiphany_normal_fp_mode)
|
|
2366 {
|
|
2367 case FP_MODE_INT:
|
|
2368 switch (priority)
|
|
2369 {
|
|
2370 case 0: return FP_MODE_INT;
|
|
2371 case 1: return epiphany_normal_fp_rounding;
|
|
2372 case 2: return (epiphany_normal_fp_rounding == FP_MODE_ROUND_NEAREST
|
|
2373 ? FP_MODE_ROUND_TRUNC : FP_MODE_ROUND_NEAREST);
|
|
2374 case 3: return FP_MODE_CALLER;
|
|
2375 }
|
|
2376 case FP_MODE_ROUND_NEAREST:
|
|
2377 case FP_MODE_CALLER:
|
|
2378 switch (priority)
|
|
2379 {
|
|
2380 case 0: return FP_MODE_ROUND_NEAREST;
|
|
2381 case 1: return FP_MODE_ROUND_TRUNC;
|
|
2382 case 2: return FP_MODE_INT;
|
|
2383 case 3: return FP_MODE_CALLER;
|
|
2384 }
|
|
2385 case FP_MODE_ROUND_TRUNC:
|
|
2386 switch (priority)
|
|
2387 {
|
|
2388 case 0: return FP_MODE_ROUND_TRUNC;
|
|
2389 case 1: return FP_MODE_ROUND_NEAREST;
|
|
2390 case 2: return FP_MODE_INT;
|
|
2391 case 3: return FP_MODE_CALLER;
|
|
2392 }
|
|
2393 case FP_MODE_ROUND_UNKNOWN:
|
|
2394 case FP_MODE_NONE:
|
|
2395 gcc_unreachable ();
|
|
2396 }
|
|
2397 gcc_unreachable ();
|
|
2398 }
|
|
2399
|
|
2400 int
|
|
2401 epiphany_mode_needed (int entity, rtx_insn *insn)
|
|
2402 {
|
|
2403 enum attr_fp_mode mode;
|
|
2404
|
|
2405 if (recog_memoized (insn) < 0)
|
|
2406 {
|
|
2407 if (entity == EPIPHANY_MSW_ENTITY_AND
|
|
2408 || entity == EPIPHANY_MSW_ENTITY_OR
|
|
2409 || entity == EPIPHANY_MSW_ENTITY_CONFIG)
|
|
2410 return 2;
|
|
2411 return FP_MODE_NONE;
|
|
2412 }
|
|
2413 mode = get_attr_fp_mode (insn);
|
|
2414
|
|
2415 switch (entity)
|
|
2416 {
|
|
2417 case EPIPHANY_MSW_ENTITY_AND:
|
|
2418 return mode != FP_MODE_NONE && mode != FP_MODE_INT ? 1 : 2;
|
|
2419 case EPIPHANY_MSW_ENTITY_OR:
|
|
2420 return mode == FP_MODE_INT ? 1 : 2;
|
|
2421 case EPIPHANY_MSW_ENTITY_CONFIG:
|
|
2422 /* We must know/save config before we set it to something else.
|
|
2423 Where we need the original value, we are fine with having it
|
|
2424 just unchanged from the function start.
|
|
2425 Because of the nature of the mode switching optimization,
|
|
2426 a restore will be dominated by a clobber. */
|
|
2427 if (mode != FP_MODE_NONE && mode != FP_MODE_CALLER)
|
|
2428 return 1;
|
|
2429 /* A cpecial case are abnormal edges, which are deemed to clobber
|
|
2430 the mode as well. We need to pin this effect on a actually
|
|
2431 dominating insn, and one where the frame can be accessed, too, in
|
|
2432 case the pseudo used to save CONFIG doesn't get a hard register. */
|
|
2433 if (CALL_P (insn) && find_reg_note (insn, REG_EH_REGION, NULL_RTX))
|
|
2434 return 1;
|
|
2435 return 2;
|
|
2436 case EPIPHANY_MSW_ENTITY_ROUND_KNOWN:
|
|
2437 if (recog_memoized (insn) == CODE_FOR_set_fp_mode)
|
|
2438 mode = (enum attr_fp_mode) epiphany_mode_after (entity, mode, insn);
|
|
2439 /* Fall through. */
|
|
2440 case EPIPHANY_MSW_ENTITY_NEAREST:
|
|
2441 case EPIPHANY_MSW_ENTITY_TRUNC:
|
|
2442 if (mode == FP_MODE_ROUND_UNKNOWN)
|
|
2443 {
|
|
2444 MACHINE_FUNCTION (cfun)->unknown_mode_uses++;
|
|
2445 return FP_MODE_NONE;
|
|
2446 }
|
|
2447 return mode;
|
|
2448 case EPIPHANY_MSW_ENTITY_ROUND_UNKNOWN:
|
|
2449 if (mode == FP_MODE_ROUND_NEAREST || mode == FP_MODE_ROUND_TRUNC)
|
|
2450 return FP_MODE_ROUND_UNKNOWN;
|
|
2451 return mode;
|
|
2452 case EPIPHANY_MSW_ENTITY_FPU_OMNIBUS:
|
|
2453 if (mode == FP_MODE_ROUND_UNKNOWN)
|
|
2454 return epiphany_normal_fp_rounding;
|
|
2455 return mode;
|
|
2456 default:
|
|
2457 gcc_unreachable ();
|
|
2458 }
|
|
2459 }
|
|
2460
|
|
2461 static int
|
|
2462 epiphany_mode_entry_exit (int entity, bool exit)
|
|
2463 {
|
|
2464 int normal_mode = epiphany_normal_fp_mode ;
|
|
2465
|
|
2466 MACHINE_FUNCTION (cfun)->sw_entities_processed |= (1 << entity);
|
|
2467 if (epiphany_is_interrupt_p (current_function_decl))
|
|
2468 normal_mode = FP_MODE_CALLER;
|
|
2469 switch (entity)
|
|
2470 {
|
|
2471 case EPIPHANY_MSW_ENTITY_AND:
|
|
2472 if (exit)
|
|
2473 return normal_mode != FP_MODE_INT ? 1 : 2;
|
|
2474 return 0;
|
|
2475 case EPIPHANY_MSW_ENTITY_OR:
|
|
2476 if (exit)
|
|
2477 return normal_mode == FP_MODE_INT ? 1 : 2;
|
|
2478 return 0;
|
|
2479 case EPIPHANY_MSW_ENTITY_CONFIG:
|
|
2480 if (exit)
|
|
2481 return 2;
|
|
2482 return normal_mode == FP_MODE_CALLER ? 0 : 1;
|
|
2483 case EPIPHANY_MSW_ENTITY_ROUND_UNKNOWN:
|
|
2484 if (normal_mode == FP_MODE_ROUND_NEAREST
|
|
2485 || normal_mode == FP_MODE_ROUND_TRUNC)
|
|
2486 return FP_MODE_ROUND_UNKNOWN;
|
|
2487 /* Fall through. */
|
|
2488 case EPIPHANY_MSW_ENTITY_NEAREST:
|
|
2489 case EPIPHANY_MSW_ENTITY_TRUNC:
|
|
2490 case EPIPHANY_MSW_ENTITY_ROUND_KNOWN:
|
|
2491 case EPIPHANY_MSW_ENTITY_FPU_OMNIBUS:
|
|
2492 return normal_mode;
|
|
2493 default:
|
|
2494 gcc_unreachable ();
|
|
2495 }
|
|
2496 }
|
|
2497
|
|
2498 int
|
|
2499 epiphany_mode_after (int entity, int last_mode, rtx_insn *insn)
|
|
2500 {
|
|
2501 /* We have too few call-saved registers to hope to keep the masks across
|
|
2502 calls. */
|
|
2503 if (entity == EPIPHANY_MSW_ENTITY_AND || entity == EPIPHANY_MSW_ENTITY_OR)
|
|
2504 {
|
|
2505 if (CALL_P (insn))
|
|
2506 return 0;
|
|
2507 return last_mode;
|
|
2508 }
|
|
2509 /* If there is an abnormal edge, we don't want the config register to
|
|
2510 be 'saved' again at the destination.
|
|
2511 The frame pointer adjustment is inside a PARALLEL because of the
|
|
2512 flags clobber. */
|
|
2513 if (entity == EPIPHANY_MSW_ENTITY_CONFIG && NONJUMP_INSN_P (insn)
|
|
2514 && GET_CODE (PATTERN (insn)) == PARALLEL
|
|
2515 && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET
|
|
2516 && SET_DEST (XVECEXP (PATTERN (insn), 0, 0)) == frame_pointer_rtx)
|
|
2517 {
|
|
2518 gcc_assert (cfun->has_nonlocal_label);
|
|
2519 return 1;
|
|
2520 }
|
|
2521 if (recog_memoized (insn) < 0)
|
|
2522 return last_mode;
|
|
2523 if (get_attr_fp_mode (insn) == FP_MODE_ROUND_UNKNOWN
|
|
2524 && last_mode != FP_MODE_ROUND_NEAREST && last_mode != FP_MODE_ROUND_TRUNC)
|
|
2525 {
|
|
2526 if (entity == EPIPHANY_MSW_ENTITY_NEAREST)
|
|
2527 return FP_MODE_ROUND_NEAREST;
|
|
2528 if (entity == EPIPHANY_MSW_ENTITY_TRUNC)
|
|
2529 return FP_MODE_ROUND_TRUNC;
|
|
2530 }
|
|
2531 if (recog_memoized (insn) == CODE_FOR_set_fp_mode)
|
|
2532 {
|
|
2533 rtx src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
|
|
2534 int fp_mode;
|
|
2535
|
|
2536 if (REG_P (src))
|
|
2537 return FP_MODE_CALLER;
|
|
2538 fp_mode = INTVAL (XVECEXP (XEXP (src, 0), 0, 0));
|
|
2539 if (entity == EPIPHANY_MSW_ENTITY_ROUND_UNKNOWN
|
|
2540 && (fp_mode == FP_MODE_ROUND_NEAREST
|
|
2541 || fp_mode == EPIPHANY_MSW_ENTITY_TRUNC))
|
|
2542 return FP_MODE_ROUND_UNKNOWN;
|
|
2543 return fp_mode;
|
|
2544 }
|
|
2545 return last_mode;
|
|
2546 }
|
|
2547
|
|
2548 static int
|
|
2549 epiphany_mode_entry (int entity)
|
|
2550 {
|
|
2551 return epiphany_mode_entry_exit (entity, false);
|
|
2552 }
|
|
2553
|
|
2554 static int
|
|
2555 epiphany_mode_exit (int entity)
|
|
2556 {
|
|
2557 return epiphany_mode_entry_exit (entity, true);
|
|
2558 }
|
|
2559
|
|
2560 void
|
|
2561 emit_set_fp_mode (int entity, int mode, int prev_mode ATTRIBUTE_UNUSED,
|
|
2562 HARD_REG_SET regs_live ATTRIBUTE_UNUSED)
|
|
2563 {
|
|
2564 rtx save_cc, cc_reg, mask, src, src2;
|
|
2565 enum attr_fp_mode fp_mode;
|
|
2566
|
|
2567 if (!MACHINE_FUNCTION (cfun)->and_mask)
|
|
2568 {
|
|
2569 MACHINE_FUNCTION (cfun)->and_mask = gen_reg_rtx (SImode);
|
|
2570 MACHINE_FUNCTION (cfun)->or_mask = gen_reg_rtx (SImode);
|
|
2571 }
|
|
2572 if (entity == EPIPHANY_MSW_ENTITY_AND)
|
|
2573 {
|
|
2574 gcc_assert (mode >= 0 && mode <= 2);
|
|
2575 if (mode == 1)
|
|
2576 emit_move_insn (MACHINE_FUNCTION (cfun)->and_mask,
|
|
2577 gen_int_mode (0xfff1fffe, SImode));
|
|
2578 return;
|
|
2579 }
|
|
2580 else if (entity == EPIPHANY_MSW_ENTITY_OR)
|
|
2581 {
|
|
2582 gcc_assert (mode >= 0 && mode <= 2);
|
|
2583 if (mode == 1)
|
|
2584 emit_move_insn (MACHINE_FUNCTION (cfun)->or_mask, GEN_INT(0x00080000));
|
|
2585 return;
|
|
2586 }
|
|
2587 else if (entity == EPIPHANY_MSW_ENTITY_CONFIG)
|
|
2588 {
|
|
2589 /* Mode switching optimization is done after emit_initial_value_sets,
|
|
2590 so we have to take care of CONFIG_REGNUM here. */
|
|
2591 gcc_assert (mode >= 0 && mode <= 2);
|
|
2592 rtx save = get_hard_reg_initial_val (SImode, CONFIG_REGNUM);
|
|
2593 if (mode == 1)
|
|
2594 emit_insn (gen_save_config (save));
|
|
2595 return;
|
|
2596 }
|
|
2597 fp_mode = (enum attr_fp_mode) mode;
|
|
2598 src = NULL_RTX;
|
|
2599
|
|
2600 switch (fp_mode)
|
|
2601 {
|
|
2602 case FP_MODE_CALLER:
|
|
2603 /* The EPIPHANY_MSW_ENTITY_CONFIG processing must come later
|
|
2604 so that the config save gets inserted before the first use. */
|
|
2605 gcc_assert (entity > EPIPHANY_MSW_ENTITY_CONFIG);
|
|
2606 src = get_hard_reg_initial_val (SImode, CONFIG_REGNUM);
|
|
2607 mask = MACHINE_FUNCTION (cfun)->and_mask;
|
|
2608 break;
|
|
2609 case FP_MODE_ROUND_UNKNOWN:
|
|
2610 MACHINE_FUNCTION (cfun)->unknown_mode_sets++;
|
|
2611 mask = MACHINE_FUNCTION (cfun)->and_mask;
|
|
2612 break;
|
|
2613 case FP_MODE_ROUND_NEAREST:
|
|
2614 if (entity == EPIPHANY_MSW_ENTITY_TRUNC)
|
|
2615 return;
|
|
2616 mask = MACHINE_FUNCTION (cfun)->and_mask;
|
|
2617 break;
|
|
2618 case FP_MODE_ROUND_TRUNC:
|
|
2619 if (entity == EPIPHANY_MSW_ENTITY_NEAREST)
|
|
2620 return;
|
|
2621 mask = MACHINE_FUNCTION (cfun)->and_mask;
|
|
2622 break;
|
|
2623 case FP_MODE_INT:
|
|
2624 mask = MACHINE_FUNCTION (cfun)->or_mask;
|
|
2625 break;
|
|
2626 case FP_MODE_NONE:
|
|
2627 default:
|
|
2628 gcc_unreachable ();
|
|
2629 }
|
|
2630 save_cc = gen_reg_rtx (CCmode);
|
|
2631 cc_reg = gen_rtx_REG (CCmode, CC_REGNUM);
|
|
2632 emit_move_insn (save_cc, cc_reg);
|
|
2633 mask = force_reg (SImode, mask);
|
|
2634 if (!src)
|
|
2635 {
|
|
2636 rtvec v = gen_rtvec (1, GEN_INT (fp_mode));
|
|
2637
|
|
2638 src = gen_rtx_CONST (SImode, gen_rtx_UNSPEC (SImode, v, UNSPEC_FP_MODE));
|
|
2639 }
|
|
2640 if (entity == EPIPHANY_MSW_ENTITY_ROUND_KNOWN
|
|
2641 || entity == EPIPHANY_MSW_ENTITY_FPU_OMNIBUS)
|
|
2642 src2 = copy_rtx (src);
|
|
2643 else
|
|
2644 {
|
|
2645 rtvec v = gen_rtvec (1, GEN_INT (FP_MODE_ROUND_UNKNOWN));
|
|
2646
|
|
2647 src2 = gen_rtx_CONST (SImode, gen_rtx_UNSPEC (SImode, v, UNSPEC_FP_MODE));
|
|
2648 }
|
|
2649 emit_insn (gen_set_fp_mode (src, src2, mask));
|
|
2650 emit_move_insn (cc_reg, save_cc);
|
|
2651 }
|
|
2652
|
|
2653 void
|
|
2654 epiphany_expand_set_fp_mode (rtx *operands)
|
|
2655 {
|
|
2656 rtx ctrl = gen_rtx_REG (SImode, CONFIG_REGNUM);
|
|
2657 rtx src = operands[0];
|
|
2658 rtx mask_reg = operands[2];
|
|
2659 rtx scratch = operands[3];
|
|
2660 enum attr_fp_mode fp_mode;
|
|
2661
|
|
2662
|
|
2663 gcc_assert (rtx_equal_p (src, operands[1])
|
|
2664 /* Sometimes reload gets silly and reloads the same pseudo
|
|
2665 into different registers. */
|
|
2666 || (REG_P (src) && REG_P (operands[1])));
|
|
2667
|
|
2668 if (!epiphany_uninterruptible_p (current_function_decl))
|
|
2669 emit_insn (gen_gid ());
|
|
2670 emit_move_insn (scratch, ctrl);
|
|
2671
|
|
2672 if (GET_CODE (src) == REG)
|
|
2673 {
|
|
2674 /* FP_MODE_CALLER */
|
|
2675 emit_insn (gen_xorsi3 (scratch, scratch, src));
|
|
2676 emit_insn (gen_andsi3 (scratch, scratch, mask_reg));
|
|
2677 emit_insn (gen_xorsi3 (scratch, scratch, src));
|
|
2678 }
|
|
2679 else
|
|
2680 {
|
|
2681 gcc_assert (GET_CODE (src) == CONST);
|
|
2682 src = XEXP (src, 0);
|
|
2683 fp_mode = (enum attr_fp_mode) INTVAL (XVECEXP (src, 0, 0));
|
|
2684 switch (fp_mode)
|
|
2685 {
|
|
2686 case FP_MODE_ROUND_NEAREST:
|
|
2687 emit_insn (gen_andsi3 (scratch, scratch, mask_reg));
|
|
2688 break;
|
|
2689 case FP_MODE_ROUND_TRUNC:
|
|
2690 emit_insn (gen_andsi3 (scratch, scratch, mask_reg));
|
|
2691 emit_insn (gen_add2_insn (scratch, const1_rtx));
|
|
2692 break;
|
|
2693 case FP_MODE_INT:
|
|
2694 emit_insn (gen_iorsi3 (scratch, scratch, mask_reg));
|
|
2695 break;
|
|
2696 case FP_MODE_CALLER:
|
|
2697 case FP_MODE_ROUND_UNKNOWN:
|
|
2698 case FP_MODE_NONE:
|
|
2699 gcc_unreachable ();
|
|
2700 }
|
|
2701 }
|
|
2702 emit_move_insn (ctrl, scratch);
|
|
2703 if (!epiphany_uninterruptible_p (current_function_decl))
|
|
2704 emit_insn (gen_gie ());
|
|
2705 }
|
|
2706
|
|
2707 void
|
|
2708 epiphany_insert_mode_switch_use (rtx_insn *insn,
|
|
2709 int entity ATTRIBUTE_UNUSED,
|
|
2710 int mode ATTRIBUTE_UNUSED)
|
|
2711 {
|
|
2712 rtx pat = PATTERN (insn);
|
|
2713 rtvec v;
|
|
2714 int len, i;
|
|
2715 rtx near = gen_rtx_REG (SImode, FP_NEAREST_REGNUM);
|
|
2716 rtx trunc = gen_rtx_REG (SImode, FP_TRUNCATE_REGNUM);
|
|
2717
|
|
2718 if (entity != EPIPHANY_MSW_ENTITY_FPU_OMNIBUS)
|
|
2719 return;
|
|
2720 switch ((enum attr_fp_mode) get_attr_fp_mode (insn))
|
|
2721 {
|
|
2722 case FP_MODE_ROUND_NEAREST:
|
|
2723 near = gen_rtx_USE (VOIDmode, near);
|
|
2724 trunc = gen_rtx_CLOBBER (VOIDmode, trunc);
|
|
2725 break;
|
|
2726 case FP_MODE_ROUND_TRUNC:
|
|
2727 near = gen_rtx_CLOBBER (VOIDmode, near);
|
|
2728 trunc = gen_rtx_USE (VOIDmode, trunc);
|
|
2729 break;
|
|
2730 case FP_MODE_ROUND_UNKNOWN:
|
|
2731 near = gen_rtx_USE (VOIDmode, gen_rtx_REG (SImode, FP_ANYFP_REGNUM));
|
|
2732 trunc = copy_rtx (near);
|
|
2733 /* Fall through. */
|
|
2734 case FP_MODE_INT:
|
|
2735 case FP_MODE_CALLER:
|
|
2736 near = gen_rtx_USE (VOIDmode, near);
|
|
2737 trunc = gen_rtx_USE (VOIDmode, trunc);
|
|
2738 break;
|
|
2739 case FP_MODE_NONE:
|
|
2740 gcc_unreachable ();
|
|
2741 }
|
|
2742 gcc_assert (GET_CODE (pat) == PARALLEL);
|
|
2743 len = XVECLEN (pat, 0);
|
|
2744 v = rtvec_alloc (len + 2);
|
|
2745 for (i = 0; i < len; i++)
|
|
2746 RTVEC_ELT (v, i) = XVECEXP (pat, 0, i);
|
|
2747 RTVEC_ELT (v, len) = near;
|
|
2748 RTVEC_ELT (v, len + 1) = trunc;
|
|
2749 pat = gen_rtx_PARALLEL (VOIDmode, v);
|
|
2750 PATTERN (insn) = pat;
|
|
2751 MACHINE_FUNCTION (cfun)->control_use_inserted = true;
|
|
2752 }
|
|
2753
|
|
2754 bool
|
|
2755 epiphany_epilogue_uses (int regno)
|
|
2756 {
|
|
2757 if (regno == GPR_LR)
|
|
2758 return true;
|
|
2759 if (reload_completed && epiphany_is_interrupt_p (current_function_decl))
|
|
2760 {
|
|
2761 if (fixed_regs[regno]
|
|
2762 && regno != STATUS_REGNUM && regno != IRET_REGNUM
|
|
2763 && regno != FP_NEAREST_REGNUM && regno != FP_TRUNCATE_REGNUM)
|
|
2764 return false;
|
|
2765 return true;
|
|
2766 }
|
|
2767 if (regno == FP_NEAREST_REGNUM
|
|
2768 && epiphany_normal_fp_mode != FP_MODE_ROUND_TRUNC)
|
|
2769 return true;
|
|
2770 if (regno == FP_TRUNCATE_REGNUM
|
|
2771 && epiphany_normal_fp_mode != FP_MODE_ROUND_NEAREST)
|
|
2772 return true;
|
|
2773 return false;
|
|
2774 }
|
|
2775
|
|
2776 static unsigned int
|
|
2777 epiphany_min_divisions_for_recip_mul (machine_mode mode)
|
|
2778 {
|
|
2779 if (flag_reciprocal_math && mode == SFmode)
|
|
2780 /* We'll expand into a multiply-by-reciprocal anyway, so we might a well do
|
|
2781 it already at the tree level and expose it to further optimizations. */
|
|
2782 return 1;
|
|
2783 return default_min_divisions_for_recip_mul (mode);
|
|
2784 }
|
|
2785
|
|
2786 static machine_mode
|
|
2787 epiphany_preferred_simd_mode (scalar_mode mode ATTRIBUTE_UNUSED)
|
|
2788 {
|
|
2789 return TARGET_VECT_DOUBLE ? DImode : SImode;
|
|
2790 }
|
|
2791
|
|
2792 static bool
|
|
2793 epiphany_vector_mode_supported_p (machine_mode mode)
|
|
2794 {
|
|
2795 if (mode == V2SFmode)
|
|
2796 return true;
|
|
2797 if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT
|
|
2798 && (GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8))
|
|
2799 return true;
|
|
2800 return false;
|
|
2801 }
|
|
2802
|
|
2803 static bool
|
|
2804 epiphany_vector_alignment_reachable (const_tree type, bool is_packed)
|
|
2805 {
|
|
2806 /* Vectors which aren't in packed structures will not be less aligned than
|
|
2807 the natural alignment of their element type, so this is safe. */
|
|
2808 if (TYPE_ALIGN_UNIT (type) == 4)
|
|
2809 return !is_packed;
|
|
2810
|
|
2811 return default_builtin_vector_alignment_reachable (type, is_packed);
|
|
2812 }
|
|
2813
|
|
2814 static bool
|
|
2815 epiphany_support_vector_misalignment (machine_mode mode, const_tree type,
|
|
2816 int misalignment, bool is_packed)
|
|
2817 {
|
|
2818 if (GET_MODE_SIZE (mode) == 8 && misalignment % 4 == 0)
|
|
2819 return true;
|
|
2820 return default_builtin_support_vector_misalignment (mode, type, misalignment,
|
|
2821 is_packed);
|
|
2822 }
|
|
2823
|
|
2824 /* STRUCTURE_SIZE_BOUNDARY seems a bit crude in how it enlarges small
|
|
2825 structs. Make structs double-word-aligned it they are a double word or
|
|
2826 (potentially) larger; failing that, do the same for a size of 32 bits. */
|
|
2827 unsigned
|
|
2828 epiphany_special_round_type_align (tree type, unsigned computed,
|
|
2829 unsigned specified)
|
|
2830 {
|
|
2831 unsigned align = MAX (computed, specified);
|
|
2832 tree field;
|
|
2833 HOST_WIDE_INT total, max;
|
|
2834 unsigned try_align = FASTEST_ALIGNMENT;
|
|
2835
|
|
2836 if (maximum_field_alignment && try_align > maximum_field_alignment)
|
|
2837 try_align = maximum_field_alignment;
|
|
2838 if (align >= try_align)
|
|
2839 return align;
|
|
2840 for (max = 0, field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
|
|
2841 {
|
|
2842 tree offset, size;
|
|
2843
|
|
2844 if (TREE_CODE (field) != FIELD_DECL
|
|
2845 || TREE_TYPE (field) == error_mark_node)
|
|
2846 continue;
|
|
2847 offset = bit_position (field);
|
|
2848 size = DECL_SIZE (field);
|
|
2849 if (!tree_fits_uhwi_p (offset) || !tree_fits_uhwi_p (size)
|
|
2850 || tree_to_uhwi (offset) >= try_align
|
|
2851 || tree_to_uhwi (size) >= try_align)
|
|
2852 return try_align;
|
|
2853 total = tree_to_uhwi (offset) + tree_to_uhwi (size);
|
|
2854 if (total > max)
|
|
2855 max = total;
|
|
2856 }
|
|
2857 if (max >= (HOST_WIDE_INT) try_align)
|
|
2858 align = try_align;
|
|
2859 else if (try_align > 32 && max >= 32)
|
|
2860 align = max > 32 ? 64 : 32;
|
|
2861 return align;
|
|
2862 }
|
|
2863
|
|
2864 /* Upping the alignment of arrays in structs is not only a performance
|
|
2865 enhancement, it also helps preserve assumptions about how
|
|
2866 arrays-at-the-end-of-structs work, like for struct gcov_fn_info in
|
|
2867 libgcov.c . */
|
|
2868 unsigned
|
|
2869 epiphany_adjust_field_align (tree type, unsigned computed)
|
|
2870 {
|
|
2871 if (computed == 32
|
|
2872 && TREE_CODE (type) == ARRAY_TYPE)
|
|
2873 {
|
|
2874 tree elmsz = TYPE_SIZE (TREE_TYPE (type));
|
|
2875
|
|
2876 if (!tree_fits_uhwi_p (elmsz) || tree_to_uhwi (elmsz) >= 32)
|
|
2877 return 64;
|
|
2878 }
|
|
2879 return computed;
|
|
2880 }
|
|
2881
|
|
2882 /* Output code to add DELTA to the first argument, and then jump
|
|
2883 to FUNCTION. Used for C++ multiple inheritance. */
|
|
2884 static void
|
|
2885 epiphany_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
|
|
2886 HOST_WIDE_INT delta,
|
|
2887 HOST_WIDE_INT vcall_offset,
|
|
2888 tree function)
|
|
2889 {
|
|
2890 int this_regno
|
|
2891 = aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function) ? 1 : 0;
|
|
2892 const char *this_name = reg_names[this_regno];
|
|
2893 const char *fname;
|
|
2894
|
|
2895 /* We use IP and R16 as a scratch registers. */
|
|
2896 gcc_assert (call_used_regs [GPR_IP]);
|
|
2897 gcc_assert (call_used_regs [GPR_16]);
|
|
2898
|
|
2899 /* Add DELTA. When possible use a plain add, otherwise load it into
|
|
2900 a register first. */
|
|
2901 if (delta == 0)
|
|
2902 ; /* Done. */
|
|
2903 else if (SIMM11 (delta))
|
|
2904 asm_fprintf (file, "\tadd\t%s,%s,%d\n", this_name, this_name, (int) delta);
|
|
2905 else if (delta < 0 && delta >= -0xffff)
|
|
2906 {
|
|
2907 asm_fprintf (file, "\tmov\tip,%d\n", (int) -delta);
|
|
2908 asm_fprintf (file, "\tsub\t%s,%s,ip\n", this_name, this_name);
|
|
2909 }
|
|
2910 else
|
|
2911 {
|
|
2912 asm_fprintf (file, "\tmov\tip,%%low(%ld)\n", (long) delta);
|
|
2913 if (delta & ~0xffff)
|
|
2914 asm_fprintf (file, "\tmovt\tip,%%high(%ld)\n", (long) delta);
|
|
2915 asm_fprintf (file, "\tadd\t%s,%s,ip\n", this_name, this_name);
|
|
2916 }
|
|
2917
|
|
2918 /* If needed, add *(*THIS + VCALL_OFFSET) to THIS. */
|
|
2919 if (vcall_offset != 0)
|
|
2920 {
|
|
2921 /* ldr ip,[this] --> temp = *this
|
|
2922 ldr ip,[ip,vcall_offset] > temp = *(*this + vcall_offset)
|
|
2923 add this,this,ip --> this+ = *(*this + vcall_offset) */
|
|
2924 asm_fprintf (file, "\tldr\tip, [%s]\n", this_name);
|
|
2925 if (vcall_offset < -0x7ff * 4 || vcall_offset > 0x7ff * 4
|
|
2926 || (vcall_offset & 3) != 0)
|
|
2927 {
|
|
2928 asm_fprintf (file, "\tmov\tr16, %%low(%ld)\n", (long) vcall_offset);
|
|
2929 asm_fprintf (file, "\tmovt\tr16, %%high(%ld)\n", (long) vcall_offset);
|
|
2930 asm_fprintf (file, "\tldr\tip, [ip,r16]\n");
|
|
2931 }
|
|
2932 else
|
|
2933 asm_fprintf (file, "\tldr\tip, [ip,%d]\n", (int) vcall_offset / 4);
|
|
2934 asm_fprintf (file, "\tadd\t%s, %s, ip\n", this_name, this_name);
|
|
2935 }
|
|
2936
|
|
2937 fname = XSTR (XEXP (DECL_RTL (function), 0), 0);
|
|
2938 if (epiphany_is_long_call_p (XEXP (DECL_RTL (function), 0)))
|
|
2939 {
|
|
2940 fputs ("\tmov\tip,%low(", file);
|
|
2941 assemble_name (file, fname);
|
|
2942 fputs (")\n\tmovt\tip,%high(", file);
|
|
2943 assemble_name (file, fname);
|
|
2944 fputs (")\n\tjr ip\n", file);
|
|
2945 }
|
|
2946 else
|
|
2947 {
|
|
2948 fputs ("\tb\t", file);
|
|
2949 assemble_name (file, fname);
|
|
2950 fputc ('\n', file);
|
|
2951 }
|
|
2952 }
|
|
2953
|
|
2954 void
|
|
2955 epiphany_start_function (FILE *file, const char *name, tree decl)
|
|
2956 {
|
|
2957 /* If the function doesn't fit into the on-chip memory, it will have a
|
|
2958 section attribute - or lack of it - that denotes it goes somewhere else.
|
|
2959 But the architecture spec says that an interrupt vector still has to
|
|
2960 point to on-chip memory. So we must place a jump there to get to the
|
|
2961 actual function implementation. The forwarder_section attribute
|
|
2962 specifies the section where this jump goes.
|
|
2963 This mechanism can also be useful to have a shortcall destination for
|
|
2964 a function that is actually placed much farther away. */
|
|
2965 tree attrs, int_attr, int_names, int_name, forwarder_attr;
|
|
2966
|
|
2967 attrs = DECL_ATTRIBUTES (decl);
|
|
2968 int_attr = lookup_attribute ("interrupt", attrs);
|
|
2969 if (int_attr)
|
|
2970 for (int_names = TREE_VALUE (int_attr); int_names;
|
|
2971 int_names = TREE_CHAIN (int_names))
|
|
2972 {
|
|
2973 char buf[99];
|
|
2974
|
|
2975 int_name = TREE_VALUE (int_names);
|
|
2976 sprintf (buf, "ivt_entry_%.80s", TREE_STRING_POINTER (int_name));
|
|
2977 switch_to_section (get_section (buf, SECTION_CODE, decl));
|
|
2978 fputs ("\tb\t", file);
|
|
2979 assemble_name (file, name);
|
|
2980 fputc ('\n', file);
|
|
2981 }
|
|
2982 forwarder_attr = lookup_attribute ("forwarder_section", attrs);
|
|
2983 if (forwarder_attr)
|
|
2984 {
|
|
2985 const char *prefix = "__forwarder_dst_";
|
|
2986 char *dst_name = (char *) alloca (strlen (prefix) + strlen (name) + 1);
|
|
2987
|
|
2988 strcpy (dst_name, prefix);
|
|
2989 strcat (dst_name, name);
|
|
2990 forwarder_attr = TREE_VALUE (TREE_VALUE (forwarder_attr));
|
|
2991 switch_to_section (get_section (TREE_STRING_POINTER (forwarder_attr),
|
|
2992 SECTION_CODE, decl));
|
|
2993 ASM_OUTPUT_FUNCTION_LABEL (file, name, decl);
|
|
2994 if (epiphany_is_long_call_p (XEXP (DECL_RTL (decl), 0)))
|
|
2995 {
|
|
2996 int tmp = GPR_0;
|
|
2997
|
|
2998 if (int_attr)
|
|
2999 fputs ("\tstrd r0,[sp,-1]\n", file);
|
|
3000 else
|
|
3001 tmp = GPR_16;
|
|
3002 gcc_assert (call_used_regs[tmp]);
|
|
3003 fprintf (file, "\tmov r%d,%%low(", tmp);
|
|
3004 assemble_name (file, dst_name);
|
|
3005 fprintf (file, ")\n"
|
|
3006 "\tmovt r%d,%%high(", tmp);
|
|
3007 assemble_name (file, dst_name);
|
|
3008 fprintf (file, ")\n"
|
|
3009 "\tjr r%d\n", tmp);
|
|
3010 }
|
|
3011 else
|
|
3012 {
|
|
3013 fputs ("\tb\t", file);
|
|
3014 assemble_name (file, dst_name);
|
|
3015 fputc ('\n', file);
|
|
3016 }
|
|
3017 name = dst_name;
|
|
3018 }
|
|
3019 switch_to_section (function_section (decl));
|
|
3020 ASM_OUTPUT_FUNCTION_LABEL (file, name, decl);
|
|
3021 }
|
|
3022
|
|
3023
|
|
3024 /* Implement TARGET_CONSTANT_ALIGNMENT. */
|
|
3025
|
|
3026 static HOST_WIDE_INT
|
|
3027 epiphany_constant_alignment (const_tree exp, HOST_WIDE_INT align)
|
|
3028 {
|
|
3029 if (TREE_CODE (exp) == STRING_CST)
|
|
3030 return MAX (align, FASTEST_ALIGNMENT);
|
|
3031 return align;
|
|
3032 }
|
|
3033
|
|
3034 /* Implement TARGET_STARTING_FRAME_OFFSET. */
|
|
3035
|
|
3036 static HOST_WIDE_INT
|
|
3037 epiphany_starting_frame_offset (void)
|
|
3038 {
|
|
3039 return epiphany_stack_offset;
|
|
3040 }
|
|
3041
|
|
3042 struct gcc_target targetm = TARGET_INITIALIZER;
|