]>
Commit | Line | Data |
---|---|---|
c6243b4c | 1 | /* Xstormy16 target functions. |
7adcbafe | 2 | Copyright (C) 1997-2022 Free Software Foundation, Inc. |
4b58290f GK |
3 | Contributed by Red Hat, Inc. |
4 | ||
5ab9749e | 5 | This file is part of GCC. |
4b58290f | 6 | |
5ab9749e NC |
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. | |
4b58290f | 11 | |
5ab9749e NC |
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. | |
4b58290f | 16 | |
5ab9749e NC |
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/>. */ | |
4b58290f | 20 | |
8fcc61f8 RS |
21 | #define IN_TARGET_CODE 1 |
22 | ||
4b58290f GK |
23 | #include "config.h" |
24 | #include "system.h" | |
4977bab6 | 25 | #include "coretypes.h" |
c7131fb2 | 26 | #include "backend.h" |
e11c4407 AM |
27 | #include "target.h" |
28 | #include "rtl.h" | |
c7131fb2 | 29 | #include "tree.h" |
314e6352 ML |
30 | #include "stringpool.h" |
31 | #include "attribs.h" | |
c7131fb2 | 32 | #include "gimple.h" |
c7131fb2 | 33 | #include "df.h" |
4d0cdd0c | 34 | #include "memmodel.h" |
e11c4407 AM |
35 | #include "tm_p.h" |
36 | #include "stringpool.h" | |
37 | #include "optabs.h" | |
38 | #include "emit-rtl.h" | |
4b58290f | 39 | #include "recog.h" |
718f9c0f | 40 | #include "diagnostic-core.h" |
e11c4407 | 41 | #include "output.h" |
40e23961 | 42 | #include "fold-const.h" |
d8a2d370 DN |
43 | #include "stor-layout.h" |
44 | #include "varasm.h" | |
45 | #include "calls.h" | |
36566b39 | 46 | #include "explow.h" |
4b58290f | 47 | #include "expr.h" |
f1e639b1 | 48 | #include "langhooks.h" |
60393bbc | 49 | #include "cfgrtl.h" |
45b0be94 | 50 | #include "gimplify.h" |
6b1ce545 | 51 | #include "reload.h" |
9b2b7279 | 52 | #include "builtins.h" |
4b58290f | 53 | |
994c5d85 | 54 | /* This file should be included last. */ |
d58627a0 RS |
55 | #include "target-def.h" |
56 | ||
51c16b7e SB |
57 | static rtx emit_addhi3_postreload (rtx, rtx, rtx); |
58 | static void xstormy16_asm_out_constructor (rtx, int); | |
59 | static void xstormy16_asm_out_destructor (rtx, int); | |
60 | static void xstormy16_asm_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, | |
61 | HOST_WIDE_INT, tree); | |
4b58290f | 62 | |
51c16b7e | 63 | static void xstormy16_init_builtins (void); |
ef4bddc2 | 64 | static rtx xstormy16_expand_builtin (tree, rtx, rtx, machine_mode, int); |
ef4bddc2 | 65 | static int xstormy16_address_cost (rtx, machine_mode, addr_space_t, bool); |
586de218 | 66 | static bool xstormy16_return_in_memory (const_tree, const_tree); |
3d4b192a | 67 | |
d6b5193b RS |
68 | static GTY(()) section *bss100_section; |
69 | ||
3c50106f RH |
70 | /* Compute a (partial) cost for rtx X. Return true if the complete |
71 | cost has been computed, and false if subexpressions should be | |
72 | scanned. In either case, *TOTAL contains the cost result. */ | |
73 | ||
74 | static bool | |
e548c9df AM |
75 | xstormy16_rtx_costs (rtx x, machine_mode mode ATTRIBUTE_UNUSED, |
76 | int outer_code ATTRIBUTE_UNUSED, | |
68f932c4 RS |
77 | int opno ATTRIBUTE_UNUSED, int *total, |
78 | bool speed ATTRIBUTE_UNUSED) | |
3c50106f | 79 | { |
e548c9df AM |
80 | int code = GET_CODE (x); |
81 | ||
3c50106f RH |
82 | switch (code) |
83 | { | |
84 | case CONST_INT: | |
85 | if (INTVAL (x) < 16 && INTVAL (x) >= 0) | |
86 | *total = COSTS_N_INSNS (1) / 2; | |
87 | else if (INTVAL (x) < 256 && INTVAL (x) >= 0) | |
88 | *total = COSTS_N_INSNS (1); | |
89 | else | |
90 | *total = COSTS_N_INSNS (2); | |
91 | return true; | |
92 | ||
93 | case CONST_DOUBLE: | |
94 | case CONST: | |
95 | case SYMBOL_REF: | |
96 | case LABEL_REF: | |
5ab9749e | 97 | *total = COSTS_N_INSNS (2); |
3c50106f RH |
98 | return true; |
99 | ||
100 | case MULT: | |
101 | *total = COSTS_N_INSNS (35 + 6); | |
102 | return true; | |
103 | case DIV: | |
104 | *total = COSTS_N_INSNS (51 - 6); | |
105 | return true; | |
106 | ||
107 | default: | |
108 | return false; | |
109 | } | |
110 | } | |
111 | ||
dcefdf67 | 112 | static int |
ef4bddc2 | 113 | xstormy16_address_cost (rtx x, machine_mode mode ATTRIBUTE_UNUSED, |
b413068c OE |
114 | addr_space_t as ATTRIBUTE_UNUSED, |
115 | bool speed ATTRIBUTE_UNUSED) | |
dcefdf67 | 116 | { |
a21eaf5e | 117 | return (CONST_INT_P (x) ? 2 |
dcefdf67 RH |
118 | : GET_CODE (x) == PLUS ? 7 |
119 | : 5); | |
120 | } | |
3c50106f | 121 | |
6b1ce545 AS |
122 | /* Worker function for TARGET_MEMORY_MOVE_COST. */ |
123 | ||
124 | static int | |
ef4bddc2 | 125 | xstormy16_memory_move_cost (machine_mode mode, reg_class_t rclass, |
6b1ce545 AS |
126 | bool in) |
127 | { | |
128 | return (5 + memory_move_secondary_cost (mode, rclass, in)); | |
129 | } | |
130 | ||
4b58290f GK |
131 | /* Branches are handled as follows: |
132 | ||
133 | 1. HImode compare-and-branches. The machine supports these | |
134 | natively, so the appropriate pattern is emitted directly. | |
135 | ||
136 | 2. SImode EQ and NE. These are emitted as pairs of HImode | |
5ab9749e | 137 | compare-and-branches. |
4b58290f GK |
138 | |
139 | 3. SImode LT, GE, LTU and GEU. These are emitted as a sequence | |
140 | of a SImode subtract followed by a branch (not a compare-and-branch), | |
141 | like this: | |
142 | sub | |
143 | sbc | |
144 | blt | |
145 | ||
146 | 4. SImode GT, LE, GTU, LEU. These are emitted as a sequence like: | |
147 | sub | |
148 | sbc | |
149 | blt | |
150 | or | |
5ab9749e | 151 | bne. */ |
4b58290f GK |
152 | |
153 | /* Emit a branch of kind CODE to location LOC. */ | |
154 | ||
155 | void | |
f90b7a5a | 156 | xstormy16_emit_cbranch (enum rtx_code code, rtx op0, rtx op1, rtx loc) |
4b58290f | 157 | { |
4b58290f GK |
158 | rtx condition_rtx, loc_ref, branch, cy_clobber; |
159 | rtvec vec; | |
ef4bddc2 | 160 | machine_mode mode; |
5ab9749e | 161 | |
4b58290f | 162 | mode = GET_MODE (op0); |
4718bfd8 | 163 | gcc_assert (mode == HImode || mode == SImode); |
4b58290f GK |
164 | |
165 | if (mode == SImode | |
166 | && (code == GT || code == LE || code == GTU || code == LEU)) | |
167 | { | |
168 | int unsigned_p = (code == GTU || code == LEU); | |
169 | int gt_p = (code == GT || code == GTU); | |
cd4c46f3 | 170 | rtx lab = NULL_RTX; |
5ab9749e | 171 | |
4b58290f GK |
172 | if (gt_p) |
173 | lab = gen_label_rtx (); | |
f90b7a5a | 174 | xstormy16_emit_cbranch (unsigned_p ? LTU : LT, op0, op1, gt_p ? lab : loc); |
4b58290f GK |
175 | /* This should be generated as a comparison against the temporary |
176 | created by the previous insn, but reload can't handle that. */ | |
f90b7a5a | 177 | xstormy16_emit_cbranch (gt_p ? NE : EQ, op0, op1, loc); |
4b58290f GK |
178 | if (gt_p) |
179 | emit_label (lab); | |
180 | return; | |
181 | } | |
5ab9749e | 182 | else if (mode == SImode |
4b58290f GK |
183 | && (code == NE || code == EQ) |
184 | && op1 != const0_rtx) | |
185 | { | |
f90b7a5a | 186 | rtx op0_word, op1_word; |
cd4c46f3 | 187 | rtx lab = NULL_RTX; |
4b58290f GK |
188 | int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD; |
189 | int i; | |
5ab9749e | 190 | |
4b58290f GK |
191 | if (code == EQ) |
192 | lab = gen_label_rtx (); | |
5ab9749e | 193 | |
4b58290f GK |
194 | for (i = 0; i < num_words - 1; i++) |
195 | { | |
f90b7a5a PB |
196 | op0_word = simplify_gen_subreg (word_mode, op0, mode, |
197 | i * UNITS_PER_WORD); | |
198 | op1_word = simplify_gen_subreg (word_mode, op1, mode, | |
199 | i * UNITS_PER_WORD); | |
200 | xstormy16_emit_cbranch (NE, op0_word, op1_word, code == EQ ? lab : loc); | |
4b58290f | 201 | } |
f90b7a5a PB |
202 | op0_word = simplify_gen_subreg (word_mode, op0, mode, |
203 | i * UNITS_PER_WORD); | |
204 | op1_word = simplify_gen_subreg (word_mode, op1, mode, | |
205 | i * UNITS_PER_WORD); | |
206 | xstormy16_emit_cbranch (code, op0_word, op1_word, loc); | |
4b58290f GK |
207 | |
208 | if (code == EQ) | |
209 | emit_label (lab); | |
210 | return; | |
211 | } | |
212 | ||
213 | /* We can't allow reload to try to generate any reload after a branch, | |
214 | so when some register must match we must make the temporary ourselves. */ | |
215 | if (mode != HImode) | |
216 | { | |
217 | rtx tmp; | |
218 | tmp = gen_reg_rtx (mode); | |
219 | emit_move_insn (tmp, op0); | |
220 | op0 = tmp; | |
221 | } | |
222 | ||
1c563bed | 223 | condition_rtx = gen_rtx_fmt_ee (code, mode, op0, op1); |
4b58290f | 224 | loc_ref = gen_rtx_LABEL_REF (VOIDmode, loc); |
f7df4a84 | 225 | branch = gen_rtx_SET (pc_rtx, |
4b58290f GK |
226 | gen_rtx_IF_THEN_ELSE (VOIDmode, condition_rtx, |
227 | loc_ref, pc_rtx)); | |
228 | ||
b72bbbcb | 229 | cy_clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (BImode, CARRY_REGNUM)); |
4b58290f GK |
230 | |
231 | if (mode == HImode) | |
232 | vec = gen_rtvec (2, branch, cy_clobber); | |
233 | else if (code == NE || code == EQ) | |
234 | vec = gen_rtvec (2, branch, gen_rtx_CLOBBER (VOIDmode, op0)); | |
235 | else | |
236 | { | |
237 | rtx sub; | |
238 | #if 0 | |
f7df4a84 | 239 | sub = gen_rtx_SET (op0, gen_rtx_MINUS (SImode, op0, op1)); |
4b58290f GK |
240 | #else |
241 | sub = gen_rtx_CLOBBER (SImode, op0); | |
242 | #endif | |
243 | vec = gen_rtvec (3, branch, sub, cy_clobber); | |
244 | } | |
245 | ||
246 | emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec)); | |
247 | } | |
248 | ||
249 | /* Take a SImode conditional branch, one of GT/LE/GTU/LEU, and split | |
250 | the arithmetic operation. Most of the work is done by | |
c6243b4c | 251 | xstormy16_expand_arith. */ |
4b58290f GK |
252 | |
253 | void | |
ef4bddc2 | 254 | xstormy16_split_cbranch (machine_mode mode, rtx label, rtx comparison, |
d40ba0b6 | 255 | rtx dest) |
4b58290f GK |
256 | { |
257 | rtx op0 = XEXP (comparison, 0); | |
258 | rtx op1 = XEXP (comparison, 1); | |
b32d5189 | 259 | rtx_insn *seq, *last_insn; |
4b58290f | 260 | rtx compare; |
5ab9749e | 261 | |
4b58290f | 262 | start_sequence (); |
d40ba0b6 | 263 | xstormy16_expand_arith (mode, COMPARE, dest, op0, op1); |
2f937369 | 264 | seq = get_insns (); |
4b58290f | 265 | end_sequence (); |
2f937369 | 266 | |
4718bfd8 | 267 | gcc_assert (INSN_P (seq)); |
2f937369 DM |
268 | |
269 | last_insn = seq; | |
270 | while (NEXT_INSN (last_insn) != NULL_RTX) | |
271 | last_insn = NEXT_INSN (last_insn); | |
272 | ||
273 | compare = SET_SRC (XVECEXP (PATTERN (last_insn), 0, 0)); | |
4b58290f GK |
274 | PUT_CODE (XEXP (compare, 0), GET_CODE (comparison)); |
275 | XEXP (compare, 1) = gen_rtx_LABEL_REF (VOIDmode, label); | |
276 | emit_insn (seq); | |
277 | } | |
278 | ||
279 | ||
280 | /* Return the string to output a conditional branch to LABEL, which is | |
281 | the operand number of the label. | |
282 | ||
283 | OP is the conditional expression, or NULL for branch-always. | |
284 | ||
5e7a8ee0 | 285 | REVERSED is nonzero if we should reverse the sense of the comparison. |
4b58290f GK |
286 | |
287 | INSN is the insn. */ | |
288 | ||
289 | char * | |
84034c69 DM |
290 | xstormy16_output_cbranch_hi (rtx op, const char *label, int reversed, |
291 | rtx_insn *insn) | |
4b58290f GK |
292 | { |
293 | static char string[64]; | |
294 | int need_longbranch = (op != NULL_RTX | |
295 | ? get_attr_length (insn) == 8 | |
296 | : get_attr_length (insn) == 4); | |
297 | int really_reversed = reversed ^ need_longbranch; | |
298 | const char *ccode; | |
0a2aaacc | 299 | const char *templ; |
4b58290f GK |
300 | const char *operands; |
301 | enum rtx_code code; | |
5ab9749e | 302 | |
4b58290f GK |
303 | if (! op) |
304 | { | |
305 | if (need_longbranch) | |
306 | ccode = "jmpf"; | |
307 | else | |
308 | ccode = "br"; | |
309 | sprintf (string, "%s %s", ccode, label); | |
310 | return string; | |
311 | } | |
312 | ||
313 | code = GET_CODE (op); | |
314 | ||
a21eaf5e | 315 | if (! REG_P (XEXP (op, 0))) |
4b58290f GK |
316 | { |
317 | code = swap_condition (code); | |
318 | operands = "%3,%2"; | |
319 | } | |
320 | else | |
321 | operands = "%2,%3"; | |
322 | ||
323 | /* Work out which way this really branches. */ | |
324 | if (really_reversed) | |
325 | code = reverse_condition (code); | |
326 | ||
327 | switch (code) | |
328 | { | |
329 | case EQ: ccode = "z"; break; | |
330 | case NE: ccode = "nz"; break; | |
331 | case GE: ccode = "ge"; break; | |
332 | case LT: ccode = "lt"; break; | |
333 | case GT: ccode = "gt"; break; | |
334 | case LE: ccode = "le"; break; | |
335 | case GEU: ccode = "nc"; break; | |
336 | case LTU: ccode = "c"; break; | |
337 | case GTU: ccode = "hi"; break; | |
338 | case LEU: ccode = "ls"; break; | |
5ab9749e | 339 | |
4b58290f | 340 | default: |
4718bfd8 | 341 | gcc_unreachable (); |
4b58290f GK |
342 | } |
343 | ||
344 | if (need_longbranch) | |
0a2aaacc | 345 | templ = "b%s %s,.+8 | jmpf %s"; |
4b58290f | 346 | else |
0a2aaacc KG |
347 | templ = "b%s %s,%s"; |
348 | sprintf (string, templ, ccode, operands, label); | |
5ab9749e | 349 | |
4b58290f GK |
350 | return string; |
351 | } | |
352 | ||
353 | /* Return the string to output a conditional branch to LABEL, which is | |
354 | the operand number of the label, but suitable for the tail of a | |
355 | SImode branch. | |
356 | ||
357 | OP is the conditional expression (OP is never NULL_RTX). | |
358 | ||
5e7a8ee0 | 359 | REVERSED is nonzero if we should reverse the sense of the comparison. |
4b58290f GK |
360 | |
361 | INSN is the insn. */ | |
362 | ||
363 | char * | |
84034c69 DM |
364 | xstormy16_output_cbranch_si (rtx op, const char *label, int reversed, |
365 | rtx_insn *insn) | |
4b58290f GK |
366 | { |
367 | static char string[64]; | |
368 | int need_longbranch = get_attr_length (insn) >= 8; | |
369 | int really_reversed = reversed ^ need_longbranch; | |
370 | const char *ccode; | |
0a2aaacc | 371 | const char *templ; |
4b58290f GK |
372 | char prevop[16]; |
373 | enum rtx_code code; | |
5ab9749e | 374 | |
4b58290f GK |
375 | code = GET_CODE (op); |
376 | ||
377 | /* Work out which way this really branches. */ | |
378 | if (really_reversed) | |
379 | code = reverse_condition (code); | |
380 | ||
381 | switch (code) | |
382 | { | |
383 | case EQ: ccode = "z"; break; | |
384 | case NE: ccode = "nz"; break; | |
385 | case GE: ccode = "ge"; break; | |
386 | case LT: ccode = "lt"; break; | |
387 | case GEU: ccode = "nc"; break; | |
388 | case LTU: ccode = "c"; break; | |
389 | ||
390 | /* The missing codes above should never be generated. */ | |
391 | default: | |
4718bfd8 | 392 | gcc_unreachable (); |
4b58290f GK |
393 | } |
394 | ||
395 | switch (code) | |
396 | { | |
397 | case EQ: case NE: | |
398 | { | |
399 | int regnum; | |
5ab9749e | 400 | |
a21eaf5e | 401 | gcc_assert (REG_P (XEXP (op, 0))); |
5ab9749e | 402 | |
4b58290f GK |
403 | regnum = REGNO (XEXP (op, 0)); |
404 | sprintf (prevop, "or %s,%s", reg_names[regnum], reg_names[regnum+1]); | |
405 | } | |
406 | break; | |
407 | ||
408 | case GE: case LT: case GEU: case LTU: | |
409 | strcpy (prevop, "sbc %2,%3"); | |
410 | break; | |
411 | ||
412 | default: | |
4718bfd8 | 413 | gcc_unreachable (); |
4b58290f GK |
414 | } |
415 | ||
416 | if (need_longbranch) | |
0a2aaacc | 417 | templ = "%s | b%s .+6 | jmpf %s"; |
4b58290f | 418 | else |
0a2aaacc KG |
419 | templ = "%s | b%s %s"; |
420 | sprintf (string, templ, prevop, ccode, label); | |
5ab9749e | 421 | |
4b58290f GK |
422 | return string; |
423 | } | |
424 | \f | |
425 | /* Many machines have some registers that cannot be copied directly to or from | |
426 | memory or even from other types of registers. An example is the `MQ' | |
427 | register, which on most machines, can only be copied to or from general | |
428 | registers, but not memory. Some machines allow copying all registers to and | |
429 | from memory, but require a scratch register for stores to some memory | |
430 | locations (e.g., those with symbolic address on the RT, and those with | |
981f6289 | 431 | certain symbolic address on the SPARC when compiling PIC). In some cases, |
4b58290f GK |
432 | both an intermediate and a scratch register are required. |
433 | ||
434 | You should define these macros to indicate to the reload phase that it may | |
435 | need to allocate at least one register for a reload in addition to the | |
436 | register to contain the data. Specifically, if copying X to a register | |
0a2aaacc | 437 | RCLASS in MODE requires an intermediate register, you should define |
4b58290f GK |
438 | `SECONDARY_INPUT_RELOAD_CLASS' to return the largest register class all of |
439 | whose registers can be used as intermediate registers or scratch registers. | |
440 | ||
0a2aaacc | 441 | If copying a register RCLASS in MODE to X requires an intermediate or scratch |
4b58290f GK |
442 | register, `SECONDARY_OUTPUT_RELOAD_CLASS' should be defined to return the |
443 | largest register class required. If the requirements for input and output | |
444 | reloads are the same, the macro `SECONDARY_RELOAD_CLASS' should be used | |
445 | instead of defining both macros identically. | |
446 | ||
447 | The values returned by these macros are often `GENERAL_REGS'. Return | |
448 | `NO_REGS' if no spare register is needed; i.e., if X can be directly copied | |
0a2aaacc | 449 | to or from a register of RCLASS in MODE without requiring a scratch register. |
4b58290f GK |
450 | Do not define this macro if it would always return `NO_REGS'. |
451 | ||
452 | If a scratch register is required (either with or without an intermediate | |
453 | register), you should define patterns for `reload_inM' or `reload_outM', as | |
454 | required.. These patterns, which will normally be implemented with a | |
455 | `define_expand', should be similar to the `movM' patterns, except that | |
456 | operand 2 is the scratch register. | |
457 | ||
458 | Define constraints for the reload register and scratch register that contain | |
459 | a single register class. If the original reload register (whose class is | |
0a2aaacc | 460 | RCLASS) can meet the constraint given in the pattern, the value returned by |
4b58290f GK |
461 | these macros is used for the class of the scratch register. Otherwise, two |
462 | additional reload registers are required. Their classes are obtained from | |
463 | the constraints in the insn pattern. | |
464 | ||
465 | X might be a pseudo-register or a `subreg' of a pseudo-register, which could | |
466 | either be in a hard register or in memory. Use `true_regnum' to find out; | |
467 | it will return -1 if the pseudo is in memory and the hard register number if | |
468 | it is in a register. | |
469 | ||
470 | These macros should not be used in the case where a particular class of | |
471 | registers can only be copied to memory and not to another class of | |
472 | registers. In that case, secondary reload registers are not needed and | |
473 | would not be helpful. Instead, a stack location must be used to perform the | |
e03f5d43 | 474 | copy and the `movM' pattern should use memory as an intermediate storage. |
4b58290f GK |
475 | This case often occurs between floating-point and general registers. */ |
476 | ||
477 | enum reg_class | |
0a2aaacc | 478 | xstormy16_secondary_reload_class (enum reg_class rclass, |
ef4bddc2 | 479 | machine_mode mode ATTRIBUTE_UNUSED, |
51c16b7e | 480 | rtx x) |
4b58290f GK |
481 | { |
482 | /* This chip has the interesting property that only the first eight | |
483 | registers can be moved to/from memory. */ | |
a21eaf5e NC |
484 | if ((MEM_P (x) |
485 | || ((GET_CODE (x) == SUBREG || REG_P (x)) | |
4b58290f GK |
486 | && (true_regnum (x) == -1 |
487 | || true_regnum (x) >= FIRST_PSEUDO_REGISTER))) | |
0a2aaacc | 488 | && ! reg_class_subset_p (rclass, EIGHT_REGS)) |
4b58290f GK |
489 | return EIGHT_REGS; |
490 | ||
4b58290f GK |
491 | return NO_REGS; |
492 | } | |
493 | ||
ef795fc2 AS |
494 | /* Worker function for TARGET_PREFERRED_RELOAD_CLASS |
495 | and TARGET_PREFERRED_OUTPUT_RELOAD_CLASS. */ | |
496 | ||
497 | static reg_class_t | |
498 | xstormy16_preferred_reload_class (rtx x, reg_class_t rclass) | |
4b58290f | 499 | { |
7f26e60c JL |
500 | /* Only the first eight registers can be moved to/from memory. |
501 | So those prefer EIGHT_REGS. | |
502 | ||
503 | Similarly reloading an auto-increment address is going to | |
504 | require loads and stores, so we must use EIGHT_REGS for those | |
505 | too. */ | |
506 | if (rclass == GENERAL_REGS | |
507 | && (MEM_P (x) | |
508 | || GET_CODE (x) == POST_INC | |
509 | || GET_CODE (x) == PRE_DEC | |
510 | || GET_CODE (x) == PRE_MODIFY)) | |
4b58290f GK |
511 | return EIGHT_REGS; |
512 | ||
0a2aaacc | 513 | return rclass; |
4b58290f GK |
514 | } |
515 | ||
54e9a19d DD |
516 | /* Predicate for symbols and addresses that reflect special 8-bit |
517 | addressing. */ | |
5ab9749e | 518 | |
a6fef2e1 | 519 | bool |
54e9a19d | 520 | xstormy16_below100_symbol (rtx x, |
ef4bddc2 | 521 | machine_mode mode ATTRIBUTE_UNUSED) |
54e9a19d DD |
522 | { |
523 | if (GET_CODE (x) == CONST) | |
524 | x = XEXP (x, 0); | |
a21eaf5e | 525 | if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1))) |
54e9a19d | 526 | x = XEXP (x, 0); |
2f806f3b | 527 | |
54e9a19d | 528 | if (GET_CODE (x) == SYMBOL_REF) |
2f806f3b NC |
529 | return (SYMBOL_REF_FLAGS (x) & SYMBOL_FLAG_XSTORMY16_BELOW100) != 0; |
530 | ||
a21eaf5e | 531 | if (CONST_INT_P (x)) |
54e9a19d DD |
532 | { |
533 | HOST_WIDE_INT i = INTVAL (x); | |
a21eaf5e | 534 | |
54e9a19d DD |
535 | if ((i >= 0x0000 && i <= 0x00ff) |
536 | || (i >= 0x7f00 && i <= 0x7fff)) | |
537 | return 1; | |
538 | } | |
539 | return 0; | |
540 | } | |
541 | ||
54e9a19d DD |
542 | /* Likewise, but only for non-volatile MEMs, for patterns where the |
543 | MEM will get split into smaller sized accesses. */ | |
5ab9749e | 544 | |
a6fef2e1 | 545 | bool |
ef4bddc2 | 546 | xstormy16_splittable_below100_operand (rtx x, machine_mode mode) |
54e9a19d | 547 | { |
a21eaf5e | 548 | if (MEM_P (x) && MEM_VOLATILE_P (x)) |
54e9a19d DD |
549 | return 0; |
550 | return xstormy16_below100_operand (x, mode); | |
551 | } | |
552 | ||
54e9a19d DD |
553 | /* Expand an 8-bit IOR. This either detects the one case we can |
554 | actually do, or uses a 16-bit IOR. */ | |
5ab9749e | 555 | |
54e9a19d DD |
556 | void |
557 | xstormy16_expand_iorqi3 (rtx *operands) | |
558 | { | |
559 | rtx in, out, outsub, val; | |
560 | ||
561 | out = operands[0]; | |
562 | in = operands[1]; | |
563 | val = operands[2]; | |
564 | ||
565 | if (xstormy16_onebit_set_operand (val, QImode)) | |
566 | { | |
567 | if (!xstormy16_below100_or_register (in, QImode)) | |
568 | in = copy_to_mode_reg (QImode, in); | |
569 | if (!xstormy16_below100_or_register (out, QImode)) | |
570 | out = gen_reg_rtx (QImode); | |
571 | emit_insn (gen_iorqi3_internal (out, in, val)); | |
572 | if (out != operands[0]) | |
573 | emit_move_insn (operands[0], out); | |
574 | return; | |
575 | } | |
576 | ||
a21eaf5e | 577 | if (! REG_P (in)) |
54e9a19d | 578 | in = copy_to_mode_reg (QImode, in); |
a21eaf5e NC |
579 | |
580 | if (! REG_P (val) && ! CONST_INT_P (val)) | |
54e9a19d | 581 | val = copy_to_mode_reg (QImode, val); |
a21eaf5e NC |
582 | |
583 | if (! REG_P (out)) | |
54e9a19d DD |
584 | out = gen_reg_rtx (QImode); |
585 | ||
586 | in = simplify_gen_subreg (HImode, in, QImode, 0); | |
587 | outsub = simplify_gen_subreg (HImode, out, QImode, 0); | |
a21eaf5e NC |
588 | |
589 | if (! CONST_INT_P (val)) | |
54e9a19d DD |
590 | val = simplify_gen_subreg (HImode, val, QImode, 0); |
591 | ||
592 | emit_insn (gen_iorhi3 (outsub, in, val)); | |
593 | ||
594 | if (out != operands[0]) | |
595 | emit_move_insn (operands[0], out); | |
596 | } | |
597 | ||
5ab9749e NC |
598 | /* Expand an 8-bit AND. This either detects the one case we can |
599 | actually do, or uses a 16-bit AND. */ | |
600 | ||
54e9a19d DD |
601 | void |
602 | xstormy16_expand_andqi3 (rtx *operands) | |
603 | { | |
604 | rtx in, out, outsub, val; | |
605 | ||
606 | out = operands[0]; | |
607 | in = operands[1]; | |
608 | val = operands[2]; | |
609 | ||
610 | if (xstormy16_onebit_clr_operand (val, QImode)) | |
611 | { | |
612 | if (!xstormy16_below100_or_register (in, QImode)) | |
613 | in = copy_to_mode_reg (QImode, in); | |
614 | if (!xstormy16_below100_or_register (out, QImode)) | |
615 | out = gen_reg_rtx (QImode); | |
616 | emit_insn (gen_andqi3_internal (out, in, val)); | |
617 | if (out != operands[0]) | |
618 | emit_move_insn (operands[0], out); | |
619 | return; | |
620 | } | |
621 | ||
a21eaf5e | 622 | if (! REG_P (in)) |
54e9a19d | 623 | in = copy_to_mode_reg (QImode, in); |
a21eaf5e NC |
624 | |
625 | if (! REG_P (val) && ! CONST_INT_P (val)) | |
54e9a19d | 626 | val = copy_to_mode_reg (QImode, val); |
a21eaf5e NC |
627 | |
628 | if (! REG_P (out)) | |
54e9a19d DD |
629 | out = gen_reg_rtx (QImode); |
630 | ||
631 | in = simplify_gen_subreg (HImode, in, QImode, 0); | |
632 | outsub = simplify_gen_subreg (HImode, out, QImode, 0); | |
a21eaf5e NC |
633 | |
634 | if (! CONST_INT_P (val)) | |
54e9a19d DD |
635 | val = simplify_gen_subreg (HImode, val, QImode, 0); |
636 | ||
637 | emit_insn (gen_andhi3 (outsub, in, val)); | |
638 | ||
639 | if (out != operands[0]) | |
640 | emit_move_insn (operands[0], out); | |
641 | } | |
642 | ||
4b58290f | 643 | #define LEGITIMATE_ADDRESS_INTEGER_P(X, OFFSET) \ |
a21eaf5e | 644 | (CONST_INT_P (X) \ |
4b58290f GK |
645 | && (unsigned HOST_WIDE_INT) (INTVAL (X) + (OFFSET) + 2048) < 4096) |
646 | ||
647 | #define LEGITIMATE_ADDRESS_CONST_INT_P(X, OFFSET) \ | |
a21eaf5e | 648 | (CONST_INT_P (X) \ |
4b58290f GK |
649 | && INTVAL (X) + (OFFSET) >= 0 \ |
650 | && INTVAL (X) + (OFFSET) < 0x8000 \ | |
651 | && (INTVAL (X) + (OFFSET) < 0x100 || INTVAL (X) + (OFFSET) >= 0x7F00)) | |
652 | ||
d634083b | 653 | bool |
ef4bddc2 | 654 | xstormy16_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED, |
c6c3dba9 | 655 | rtx x, bool strict) |
4b58290f GK |
656 | { |
657 | if (LEGITIMATE_ADDRESS_CONST_INT_P (x, 0)) | |
a21eaf5e | 658 | return true; |
4b58290f GK |
659 | |
660 | if (GET_CODE (x) == PLUS | |
661 | && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 0)) | |
813ab1d7 NC |
662 | { |
663 | x = XEXP (x, 0); | |
664 | /* PR 31232: Do not allow INT+INT as an address. */ | |
a21eaf5e NC |
665 | if (CONST_INT_P (x)) |
666 | return false; | |
813ab1d7 | 667 | } |
5ab9749e | 668 | |
a21eaf5e | 669 | if ((GET_CODE (x) == PRE_MODIFY && CONST_INT_P (XEXP (XEXP (x, 1), 1))) |
f3922fd2 | 670 | || GET_CODE (x) == POST_INC |
4b58290f GK |
671 | || GET_CODE (x) == PRE_DEC) |
672 | x = XEXP (x, 0); | |
5ab9749e | 673 | |
a21eaf5e NC |
674 | if (REG_P (x) |
675 | && REGNO_OK_FOR_BASE_P (REGNO (x)) | |
4b58290f | 676 | && (! strict || REGNO (x) < FIRST_PSEUDO_REGISTER)) |
a21eaf5e | 677 | return true; |
54e9a19d | 678 | |
f84fe9b6 | 679 | if (xstormy16_below100_symbol (x, mode)) |
a21eaf5e | 680 | return true; |
5ab9749e | 681 | |
a21eaf5e | 682 | return false; |
4b58290f GK |
683 | } |
684 | ||
192997cf | 685 | /* Worker function for TARGET_MODE_DEPENDENT_ADDRESS_P. |
5ab9749e | 686 | |
4b58290f GK |
687 | On this chip, this is true if the address is valid with an offset |
688 | of 0 but not of 6, because in that case it cannot be used as an | |
689 | address for DImode or DFmode, or if the address is a post-increment | |
690 | or pre-decrement address. */ | |
5ab9749e | 691 | |
192997cf | 692 | static bool |
5bfed9a9 GJL |
693 | xstormy16_mode_dependent_address_p (const_rtx x, |
694 | addr_space_t as ATTRIBUTE_UNUSED) | |
4b58290f GK |
695 | { |
696 | if (LEGITIMATE_ADDRESS_CONST_INT_P (x, 0) | |
697 | && ! LEGITIMATE_ADDRESS_CONST_INT_P (x, 6)) | |
192997cf | 698 | return true; |
5ab9749e | 699 | |
4b58290f GK |
700 | if (GET_CODE (x) == PLUS |
701 | && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 0) | |
702 | && ! LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 6)) | |
192997cf | 703 | return true; |
4b58290f | 704 | |
e53b6e56 | 705 | /* Auto-increment addresses are now treated generically in recog.cc. */ |
192997cf | 706 | return false; |
4b58290f GK |
707 | } |
708 | ||
4b58290f | 709 | int |
ef4bddc2 | 710 | short_memory_operand (rtx x, machine_mode mode) |
4b58290f GK |
711 | { |
712 | if (! memory_operand (x, mode)) | |
713 | return 0; | |
714 | return (GET_CODE (XEXP (x, 0)) != PLUS); | |
715 | } | |
716 | ||
fae778eb | 717 | /* Splitter for the 'move' patterns, for modes not directly implemented |
4b58290f GK |
718 | by hardware. Emit insns to copy a value of mode MODE from SRC to |
719 | DEST. | |
720 | ||
5ab9749e | 721 | This function is only called when reload_completed. */ |
4b58290f | 722 | |
5ab9749e | 723 | void |
ef4bddc2 | 724 | xstormy16_split_move (machine_mode mode, rtx dest, rtx src) |
4b58290f GK |
725 | { |
726 | int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD; | |
727 | int direction, end, i; | |
728 | int src_modifies = 0; | |
729 | int dest_modifies = 0; | |
730 | int src_volatile = 0; | |
731 | int dest_volatile = 0; | |
732 | rtx mem_operand; | |
7c87e9f9 | 733 | rtx auto_inc_reg_rtx = NULL_RTX; |
5ab9749e | 734 | |
4b58290f | 735 | /* Check initial conditions. */ |
4718bfd8 NS |
736 | gcc_assert (reload_completed |
737 | && mode != QImode && mode != HImode | |
738 | && nonimmediate_operand (dest, mode) | |
739 | && general_operand (src, mode)); | |
4b58290f GK |
740 | |
741 | /* This case is not supported below, and shouldn't be generated. */ | |
a21eaf5e | 742 | gcc_assert (! MEM_P (dest) || ! MEM_P (src)); |
4b58290f GK |
743 | |
744 | /* This case is very very bad after reload, so trap it now. */ | |
4718bfd8 | 745 | gcc_assert (GET_CODE (dest) != SUBREG && GET_CODE (src) != SUBREG); |
4b58290f GK |
746 | |
747 | /* The general idea is to copy by words, offsetting the source and | |
748 | destination. Normally the least-significant word will be copied | |
5ab9749e | 749 | first, but for pre-dec operations it's better to copy the |
4b58290f | 750 | most-significant word first. Only one operand can be a pre-dec |
5ab9749e | 751 | or post-inc operand. |
4b58290f GK |
752 | |
753 | It's also possible that the copy overlaps so that the direction | |
754 | must be reversed. */ | |
755 | direction = 1; | |
5ab9749e | 756 | |
a21eaf5e | 757 | if (MEM_P (dest)) |
4b58290f GK |
758 | { |
759 | mem_operand = XEXP (dest, 0); | |
760 | dest_modifies = side_effects_p (mem_operand); | |
7c87e9f9 CM |
761 | if (auto_inc_p (mem_operand)) |
762 | auto_inc_reg_rtx = XEXP (mem_operand, 0); | |
4b58290f GK |
763 | dest_volatile = MEM_VOLATILE_P (dest); |
764 | if (dest_volatile) | |
765 | { | |
766 | dest = copy_rtx (dest); | |
767 | MEM_VOLATILE_P (dest) = 0; | |
768 | } | |
769 | } | |
a21eaf5e | 770 | else if (MEM_P (src)) |
4b58290f GK |
771 | { |
772 | mem_operand = XEXP (src, 0); | |
773 | src_modifies = side_effects_p (mem_operand); | |
7c87e9f9 CM |
774 | if (auto_inc_p (mem_operand)) |
775 | auto_inc_reg_rtx = XEXP (mem_operand, 0); | |
4b58290f GK |
776 | src_volatile = MEM_VOLATILE_P (src); |
777 | if (src_volatile) | |
778 | { | |
779 | src = copy_rtx (src); | |
780 | MEM_VOLATILE_P (src) = 0; | |
781 | } | |
782 | } | |
783 | else | |
784 | mem_operand = NULL_RTX; | |
785 | ||
786 | if (mem_operand == NULL_RTX) | |
787 | { | |
a21eaf5e NC |
788 | if (REG_P (src) |
789 | && REG_P (dest) | |
4b58290f GK |
790 | && reg_overlap_mentioned_p (dest, src) |
791 | && REGNO (dest) > REGNO (src)) | |
792 | direction = -1; | |
793 | } | |
794 | else if (GET_CODE (mem_operand) == PRE_DEC | |
5ab9749e | 795 | || (GET_CODE (mem_operand) == PLUS |
4b58290f GK |
796 | && GET_CODE (XEXP (mem_operand, 0)) == PRE_DEC)) |
797 | direction = -1; | |
a21eaf5e | 798 | else if (MEM_P (src) && reg_overlap_mentioned_p (dest, src)) |
4b58290f GK |
799 | { |
800 | int regno; | |
5ab9749e | 801 | |
a21eaf5e | 802 | gcc_assert (REG_P (dest)); |
4b58290f | 803 | regno = REGNO (dest); |
5ab9749e | 804 | |
4718bfd8 NS |
805 | gcc_assert (refers_to_regno_p (regno, regno + num_words, |
806 | mem_operand, 0)); | |
5ab9749e | 807 | |
c9bd6bcd | 808 | if (refers_to_regno_p (regno, mem_operand)) |
4b58290f GK |
809 | direction = -1; |
810 | else if (refers_to_regno_p (regno + num_words - 1, regno + num_words, | |
811 | mem_operand, 0)) | |
812 | direction = 1; | |
813 | else | |
814 | /* This means something like | |
815 | (set (reg:DI r0) (mem:DI (reg:HI r1))) | |
816 | which we'd need to support by doing the set of the second word | |
817 | last. */ | |
4718bfd8 | 818 | gcc_unreachable (); |
4b58290f GK |
819 | } |
820 | ||
821 | end = direction < 0 ? -1 : num_words; | |
822 | for (i = direction < 0 ? num_words - 1 : 0; i != end; i += direction) | |
823 | { | |
7c87e9f9 CM |
824 | rtx w_src, w_dest, insn; |
825 | ||
4b58290f GK |
826 | if (src_modifies) |
827 | w_src = gen_rtx_MEM (word_mode, mem_operand); | |
828 | else | |
829 | w_src = simplify_gen_subreg (word_mode, src, mode, i * UNITS_PER_WORD); | |
830 | if (src_volatile) | |
831 | MEM_VOLATILE_P (w_src) = 1; | |
832 | if (dest_modifies) | |
833 | w_dest = gen_rtx_MEM (word_mode, mem_operand); | |
834 | else | |
5ab9749e | 835 | w_dest = simplify_gen_subreg (word_mode, dest, mode, |
4b58290f GK |
836 | i * UNITS_PER_WORD); |
837 | if (dest_volatile) | |
838 | MEM_VOLATILE_P (w_dest) = 1; | |
5ab9749e | 839 | |
4b58290f | 840 | /* The simplify_subreg calls must always be able to simplify. */ |
4718bfd8 NS |
841 | gcc_assert (GET_CODE (w_src) != SUBREG |
842 | && GET_CODE (w_dest) != SUBREG); | |
5ab9749e | 843 | |
f7df4a84 | 844 | insn = emit_insn (gen_rtx_SET (w_dest, w_src)); |
7c87e9f9 CM |
845 | if (auto_inc_reg_rtx) |
846 | REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC, | |
847 | auto_inc_reg_rtx, | |
848 | REG_NOTES (insn)); | |
4b58290f GK |
849 | } |
850 | } | |
851 | ||
852 | /* Expander for the 'move' patterns. Emit insns to copy a value of | |
853 | mode MODE from SRC to DEST. */ | |
854 | ||
5ab9749e | 855 | void |
ef4bddc2 | 856 | xstormy16_expand_move (machine_mode mode, rtx dest, rtx src) |
4b58290f | 857 | { |
a21eaf5e | 858 | if (MEM_P (dest) && (GET_CODE (XEXP (dest, 0)) == PRE_MODIFY)) |
f3922fd2 DD |
859 | { |
860 | rtx pmv = XEXP (dest, 0); | |
861 | rtx dest_reg = XEXP (pmv, 0); | |
862 | rtx dest_mod = XEXP (pmv, 1); | |
f7df4a84 | 863 | rtx set = gen_rtx_SET (dest_reg, dest_mod); |
b72bbbcb | 864 | rtx clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (BImode, CARRY_REGNUM)); |
5ab9749e | 865 | |
f3922fd2 DD |
866 | dest = gen_rtx_MEM (mode, dest_reg); |
867 | emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber))); | |
868 | } | |
a21eaf5e | 869 | else if (MEM_P (src) && (GET_CODE (XEXP (src, 0)) == PRE_MODIFY)) |
f3922fd2 DD |
870 | { |
871 | rtx pmv = XEXP (src, 0); | |
872 | rtx src_reg = XEXP (pmv, 0); | |
873 | rtx src_mod = XEXP (pmv, 1); | |
f7df4a84 | 874 | rtx set = gen_rtx_SET (src_reg, src_mod); |
b72bbbcb | 875 | rtx clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (BImode, CARRY_REGNUM)); |
5ab9749e | 876 | |
f3922fd2 DD |
877 | src = gen_rtx_MEM (mode, src_reg); |
878 | emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber))); | |
879 | } | |
5ab9749e | 880 | |
4b58290f GK |
881 | /* There are only limited immediate-to-memory move instructions. */ |
882 | if (! reload_in_progress | |
883 | && ! reload_completed | |
a21eaf5e NC |
884 | && MEM_P (dest) |
885 | && (! CONST_INT_P (XEXP (dest, 0)) | |
c6243b4c | 886 | || ! xstormy16_legitimate_address_p (mode, XEXP (dest, 0), 0)) |
54e9a19d | 887 | && ! xstormy16_below100_operand (dest, mode) |
a21eaf5e | 888 | && ! REG_P (src) |
4b58290f GK |
889 | && GET_CODE (src) != SUBREG) |
890 | src = copy_to_mode_reg (mode, src); | |
891 | ||
892 | /* Don't emit something we would immediately split. */ | |
893 | if (reload_completed | |
894 | && mode != HImode && mode != QImode) | |
895 | { | |
c6243b4c | 896 | xstormy16_split_move (mode, dest, src); |
4b58290f GK |
897 | return; |
898 | } | |
5ab9749e | 899 | |
f7df4a84 | 900 | emit_insn (gen_rtx_SET (dest, src)); |
4b58290f | 901 | } |
4b58290f GK |
902 | \f |
903 | /* Stack Layout: | |
904 | ||
905 | The stack is laid out as follows: | |
906 | ||
907 | SP-> | |
908 | FP-> Local variables | |
909 | Register save area (up to 4 words) | |
910 | Argument register save area for stdarg (NUM_ARGUMENT_REGISTERS words) | |
911 | ||
912 | AP-> Return address (two words) | |
913 | 9th procedure parameter word | |
914 | 10th procedure parameter word | |
915 | ... | |
916 | last procedure parameter word | |
917 | ||
918 | The frame pointer location is tuned to make it most likely that all | |
919 | parameters and local variables can be accessed using a load-indexed | |
920 | instruction. */ | |
921 | ||
922 | /* A structure to describe the layout. */ | |
c6243b4c | 923 | struct xstormy16_stack_layout |
4b58290f GK |
924 | { |
925 | /* Size of the topmost three items on the stack. */ | |
926 | int locals_size; | |
927 | int register_save_size; | |
928 | int stdarg_save_size; | |
929 | /* Sum of the above items. */ | |
930 | int frame_size; | |
931 | /* Various offsets. */ | |
932 | int first_local_minus_ap; | |
933 | int sp_minus_fp; | |
934 | int fp_minus_ap; | |
935 | }; | |
936 | ||
937 | /* Does REGNO need to be saved? */ | |
938 | #define REG_NEEDS_SAVE(REGNUM, IFUN) \ | |
a365fa06 RS |
939 | ((df_regs_ever_live_p (REGNUM) && !call_used_or_fixed_reg_p (REGNUM)) \ |
940 | || (IFUN && !fixed_regs[REGNUM] && call_used_or_fixed_reg_p (REGNUM) \ | |
d40ba0b6 | 941 | && (REGNUM != CARRY_REGNUM) \ |
416ff32e | 942 | && (df_regs_ever_live_p (REGNUM) || ! crtl->is_leaf))) |
4b58290f GK |
943 | |
944 | /* Compute the stack layout. */ | |
5ab9749e NC |
945 | |
946 | struct xstormy16_stack_layout | |
51c16b7e | 947 | xstormy16_compute_stack_layout (void) |
4b58290f | 948 | { |
c6243b4c | 949 | struct xstormy16_stack_layout layout; |
4b58290f | 950 | int regno; |
c6243b4c | 951 | const int ifun = xstormy16_interrupt_function_p (); |
4b58290f GK |
952 | |
953 | layout.locals_size = get_frame_size (); | |
5ab9749e | 954 | |
4b58290f GK |
955 | layout.register_save_size = 0; |
956 | for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) | |
957 | if (REG_NEEDS_SAVE (regno, ifun)) | |
958 | layout.register_save_size += UNITS_PER_WORD; | |
5ab9749e | 959 | |
e3b5732b | 960 | if (cfun->stdarg) |
4b58290f GK |
961 | layout.stdarg_save_size = NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD; |
962 | else | |
963 | layout.stdarg_save_size = 0; | |
5ab9749e NC |
964 | |
965 | layout.frame_size = (layout.locals_size | |
966 | + layout.register_save_size | |
4b58290f | 967 | + layout.stdarg_save_size); |
5ab9749e | 968 | |
38173d38 | 969 | if (crtl->args.size <= 2048 && crtl->args.size != -1) |
4b58290f | 970 | { |
5ab9749e | 971 | if (layout.frame_size - INCOMING_FRAME_SP_OFFSET |
38173d38 | 972 | + crtl->args.size <= 2048) |
b72bbbcb | 973 | layout.fp_minus_ap = layout.frame_size - INCOMING_FRAME_SP_OFFSET; |
4b58290f | 974 | else |
38173d38 | 975 | layout.fp_minus_ap = 2048 - crtl->args.size; |
4b58290f GK |
976 | } |
977 | else | |
5ab9749e | 978 | layout.fp_minus_ap = (layout.stdarg_save_size |
4b58290f | 979 | + layout.register_save_size |
b72bbbcb | 980 | - INCOMING_FRAME_SP_OFFSET); |
5ab9749e | 981 | layout.sp_minus_fp = (layout.frame_size - INCOMING_FRAME_SP_OFFSET |
4b58290f GK |
982 | - layout.fp_minus_ap); |
983 | layout.first_local_minus_ap = layout.sp_minus_fp - layout.locals_size; | |
984 | return layout; | |
985 | } | |
986 | ||
7b5cbb57 AS |
987 | /* Worker function for TARGET_CAN_ELIMINATE. */ |
988 | ||
989 | static bool | |
990 | xstormy16_can_eliminate (const int from, const int to) | |
991 | { | |
992 | return (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM | |
993 | ? ! frame_pointer_needed | |
994 | : true); | |
995 | } | |
996 | ||
4b58290f | 997 | /* Determine how all the special registers get eliminated. */ |
5ab9749e | 998 | |
4b58290f | 999 | int |
51c16b7e | 1000 | xstormy16_initial_elimination_offset (int from, int to) |
4b58290f | 1001 | { |
c6243b4c | 1002 | struct xstormy16_stack_layout layout; |
4b58290f | 1003 | int result; |
5ab9749e | 1004 | |
c6243b4c | 1005 | layout = xstormy16_compute_stack_layout (); |
4b58290f GK |
1006 | |
1007 | if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM) | |
1008 | result = layout.sp_minus_fp - layout.locals_size; | |
1009 | else if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) | |
b72bbbcb | 1010 | result = - layout.locals_size; |
4b58290f | 1011 | else if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM) |
b72bbbcb | 1012 | result = - layout.fp_minus_ap; |
4b58290f | 1013 | else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) |
b72bbbcb | 1014 | result = - (layout.sp_minus_fp + layout.fp_minus_ap); |
4b58290f | 1015 | else |
4718bfd8 | 1016 | gcc_unreachable (); |
4b58290f GK |
1017 | |
1018 | return result; | |
1019 | } | |
1020 | ||
1021 | static rtx | |
51c16b7e | 1022 | emit_addhi3_postreload (rtx dest, rtx src0, rtx src1) |
4b58290f GK |
1023 | { |
1024 | rtx set, clobber, insn; | |
5ab9749e | 1025 | |
f7df4a84 | 1026 | set = gen_rtx_SET (dest, gen_rtx_PLUS (HImode, src0, src1)); |
b72bbbcb | 1027 | clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (BImode, CARRY_REGNUM)); |
4b58290f GK |
1028 | insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber))); |
1029 | return insn; | |
1030 | } | |
1031 | ||
41441dc7 NB |
1032 | /* Called after register allocation to add any instructions needed for |
1033 | the prologue. Using a prologue insn is favored compared to putting | |
1034 | all of the instructions in the TARGET_ASM_FUNCTION_PROLOGUE macro, | |
1035 | since it allows the scheduler to intermix instructions with the | |
1036 | saves of the caller saved registers. In some cases, it might be | |
1037 | necessary to emit a barrier instruction as the last insn to prevent | |
1038 | such scheduling. | |
4b58290f GK |
1039 | |
1040 | Also any insns generated here should have RTX_FRAME_RELATED_P(insn) = 1 | |
1041 | so that the debug info generation code can handle them properly. */ | |
5ab9749e | 1042 | |
4b58290f | 1043 | void |
51c16b7e | 1044 | xstormy16_expand_prologue (void) |
4b58290f | 1045 | { |
c6243b4c | 1046 | struct xstormy16_stack_layout layout; |
4b58290f GK |
1047 | int regno; |
1048 | rtx insn; | |
1049 | rtx mem_push_rtx; | |
c6243b4c | 1050 | const int ifun = xstormy16_interrupt_function_p (); |
5ab9749e | 1051 | |
4b58290f GK |
1052 | mem_push_rtx = gen_rtx_POST_INC (Pmode, stack_pointer_rtx); |
1053 | mem_push_rtx = gen_rtx_MEM (HImode, mem_push_rtx); | |
5ab9749e | 1054 | |
c6243b4c | 1055 | layout = xstormy16_compute_stack_layout (); |
4b58290f | 1056 | |
f3922fd2 | 1057 | if (layout.locals_size >= 32768) |
ab532386 | 1058 | error ("local variable memory requirements exceed capacity"); |
f3922fd2 | 1059 | |
315bdf71 NC |
1060 | if (flag_stack_usage_info) |
1061 | current_function_static_stack_size = layout.frame_size; | |
1062 | ||
4b58290f GK |
1063 | /* Save the argument registers if necessary. */ |
1064 | if (layout.stdarg_save_size) | |
5ab9749e | 1065 | for (regno = FIRST_ARGUMENT_REGISTER; |
4b58290f GK |
1066 | regno < FIRST_ARGUMENT_REGISTER + NUM_ARGUMENT_REGISTERS; |
1067 | regno++) | |
1068 | { | |
f3922fd2 | 1069 | rtx dwarf; |
4b58290f | 1070 | rtx reg = gen_rtx_REG (HImode, regno); |
f3922fd2 | 1071 | |
4b58290f GK |
1072 | insn = emit_move_insn (mem_push_rtx, reg); |
1073 | RTX_FRAME_RELATED_P (insn) = 1; | |
f3922fd2 DD |
1074 | |
1075 | dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (2)); | |
5ab9749e | 1076 | |
f7df4a84 | 1077 | XVECEXP (dwarf, 0, 0) = gen_rtx_SET (gen_rtx_MEM (Pmode, stack_pointer_rtx), |
f3922fd2 | 1078 | reg); |
f7df4a84 | 1079 | XVECEXP (dwarf, 0, 1) = gen_rtx_SET (stack_pointer_rtx, |
0a81f074 RS |
1080 | plus_constant (Pmode, |
1081 | stack_pointer_rtx, | |
f3922fd2 | 1082 | GET_MODE_SIZE (Pmode))); |
f1cb6795 | 1083 | add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf); |
f3922fd2 DD |
1084 | RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 0)) = 1; |
1085 | RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 1)) = 1; | |
4b58290f | 1086 | } |
5ab9749e | 1087 | |
4b58290f GK |
1088 | /* Push each of the registers to save. */ |
1089 | for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) | |
1090 | if (REG_NEEDS_SAVE (regno, ifun)) | |
1091 | { | |
f3922fd2 | 1092 | rtx dwarf; |
4b58290f | 1093 | rtx reg = gen_rtx_REG (HImode, regno); |
f3922fd2 | 1094 | |
4b58290f GK |
1095 | insn = emit_move_insn (mem_push_rtx, reg); |
1096 | RTX_FRAME_RELATED_P (insn) = 1; | |
f3922fd2 DD |
1097 | |
1098 | dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (2)); | |
5ab9749e | 1099 | |
f7df4a84 | 1100 | XVECEXP (dwarf, 0, 0) = gen_rtx_SET (gen_rtx_MEM (Pmode, stack_pointer_rtx), |
f3922fd2 | 1101 | reg); |
f7df4a84 | 1102 | XVECEXP (dwarf, 0, 1) = gen_rtx_SET (stack_pointer_rtx, |
a6178a25 | 1103 | plus_constant (Pmode, |
0a81f074 | 1104 | stack_pointer_rtx, |
f3922fd2 | 1105 | GET_MODE_SIZE (Pmode))); |
f1cb6795 | 1106 | add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf); |
f3922fd2 DD |
1107 | RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 0)) = 1; |
1108 | RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 1)) = 1; | |
4b58290f GK |
1109 | } |
1110 | ||
1111 | /* It's just possible that the SP here might be what we need for | |
b1c9bc51 | 1112 | the new FP... */ |
4b58290f | 1113 | if (frame_pointer_needed && layout.sp_minus_fp == layout.locals_size) |
b72bbbcb DD |
1114 | { |
1115 | insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx); | |
1116 | RTX_FRAME_RELATED_P (insn) = 1; | |
1117 | } | |
4b58290f GK |
1118 | |
1119 | /* Allocate space for local variables. */ | |
1120 | if (layout.locals_size) | |
1121 | { | |
1122 | insn = emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx, | |
1123 | GEN_INT (layout.locals_size)); | |
1124 | RTX_FRAME_RELATED_P (insn) = 1; | |
1125 | } | |
1126 | ||
1127 | /* Set up the frame pointer, if required. */ | |
1128 | if (frame_pointer_needed && layout.sp_minus_fp != layout.locals_size) | |
1129 | { | |
1130 | insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx); | |
b72bbbcb | 1131 | RTX_FRAME_RELATED_P (insn) = 1; |
6208b55d | 1132 | |
4b58290f | 1133 | if (layout.sp_minus_fp) |
b72bbbcb DD |
1134 | { |
1135 | insn = emit_addhi3_postreload (hard_frame_pointer_rtx, | |
1136 | hard_frame_pointer_rtx, | |
1137 | GEN_INT (- layout.sp_minus_fp)); | |
1138 | RTX_FRAME_RELATED_P (insn) = 1; | |
1139 | } | |
4b58290f GK |
1140 | } |
1141 | } | |
1142 | ||
1143 | /* Do we need an epilogue at all? */ | |
5ab9749e | 1144 | |
4b58290f | 1145 | int |
51c16b7e | 1146 | direct_return (void) |
4b58290f | 1147 | { |
5ab9749e | 1148 | return (reload_completed |
2fd7ba4a NC |
1149 | && xstormy16_compute_stack_layout ().frame_size == 0 |
1150 | && ! xstormy16_interrupt_function_p ()); | |
4b58290f GK |
1151 | } |
1152 | ||
41441dc7 | 1153 | /* Called after register allocation to add any instructions needed for |
e03f5d43 | 1154 | the epilogue. Using an epilogue insn is favored compared to putting |
41441dc7 NB |
1155 | all of the instructions in the TARGET_ASM_FUNCTION_PROLOGUE macro, |
1156 | since it allows the scheduler to intermix instructions with the | |
1157 | saves of the caller saved registers. In some cases, it might be | |
1158 | necessary to emit a barrier instruction as the last insn to prevent | |
1159 | such scheduling. */ | |
4b58290f GK |
1160 | |
1161 | void | |
51c16b7e | 1162 | xstormy16_expand_epilogue (void) |
4b58290f | 1163 | { |
c6243b4c | 1164 | struct xstormy16_stack_layout layout; |
a21eaf5e | 1165 | rtx mem_pop_rtx; |
4b58290f | 1166 | int regno; |
c6243b4c | 1167 | const int ifun = xstormy16_interrupt_function_p (); |
5ab9749e | 1168 | |
4b58290f GK |
1169 | mem_pop_rtx = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx); |
1170 | mem_pop_rtx = gen_rtx_MEM (HImode, mem_pop_rtx); | |
5ab9749e | 1171 | |
c6243b4c | 1172 | layout = xstormy16_compute_stack_layout (); |
4b58290f GK |
1173 | |
1174 | /* Pop the stack for the locals. */ | |
1175 | if (layout.locals_size) | |
e2470e1b GK |
1176 | { |
1177 | if (frame_pointer_needed && layout.sp_minus_fp == layout.locals_size) | |
1178 | emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx); | |
1179 | else | |
b72bbbcb DD |
1180 | emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx, |
1181 | GEN_INT (- layout.locals_size)); | |
e2470e1b | 1182 | } |
4b58290f GK |
1183 | |
1184 | /* Restore any call-saved registers. */ | |
1185 | for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno--) | |
1186 | if (REG_NEEDS_SAVE (regno, ifun)) | |
b72bbbcb | 1187 | emit_move_insn (gen_rtx_REG (HImode, regno), mem_pop_rtx); |
5ab9749e | 1188 | |
4b58290f GK |
1189 | /* Pop the stack for the stdarg save area. */ |
1190 | if (layout.stdarg_save_size) | |
b72bbbcb DD |
1191 | emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx, |
1192 | GEN_INT (- layout.stdarg_save_size)); | |
4b58290f GK |
1193 | |
1194 | /* Return. */ | |
1195 | if (ifun) | |
1196 | emit_jump_insn (gen_return_internal_interrupt ()); | |
1197 | else | |
1198 | emit_jump_insn (gen_return_internal ()); | |
1199 | } | |
1200 | ||
1201 | int | |
51c16b7e | 1202 | xstormy16_epilogue_uses (int regno) |
4b58290f | 1203 | { |
a365fa06 | 1204 | if (reload_completed && call_used_or_fixed_reg_p (regno)) |
4b58290f | 1205 | { |
c6243b4c | 1206 | const int ifun = xstormy16_interrupt_function_p (); |
4b58290f GK |
1207 | return REG_NEEDS_SAVE (regno, ifun); |
1208 | } | |
1209 | return 0; | |
1210 | } | |
14b56832 DD |
1211 | |
1212 | void | |
51c16b7e | 1213 | xstormy16_function_profiler (void) |
14b56832 DD |
1214 | { |
1215 | sorry ("function_profiler support"); | |
1216 | } | |
4b58290f | 1217 | \f |
6930c98c RS |
1218 | /* Update CUM to advance past argument ARG. Once this is done, |
1219 | the variable CUM is suitable for analyzing the *following* | |
bf425ddd | 1220 | argument with `TARGET_FUNCTION_ARG', etc. |
4b58290f GK |
1221 | |
1222 | This function need not do anything if the argument in question was | |
1223 | passed on the stack. The compiler knows how to track the amount of | |
1224 | stack space used for arguments without any special help. However, | |
c6243b4c | 1225 | it makes life easier for xstormy16_build_va_list if it does update |
4b58290f | 1226 | the word count. */ |
5ab9749e | 1227 | |
bf425ddd | 1228 | static void |
6930c98c RS |
1229 | xstormy16_function_arg_advance (cumulative_args_t cum_v, |
1230 | const function_arg_info &arg) | |
4b58290f | 1231 | { |
d5cc9181 JR |
1232 | CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); |
1233 | ||
4b58290f GK |
1234 | /* If an argument would otherwise be passed partially in registers, |
1235 | and partially on the stack, the whole of it is passed on the | |
1236 | stack. */ | |
bf425ddd | 1237 | if (*cum < NUM_ARGUMENT_REGISTERS |
6930c98c RS |
1238 | && (*cum + XSTORMY16_WORD_SIZE (arg.type, arg.mode) |
1239 | > NUM_ARGUMENT_REGISTERS)) | |
bf425ddd | 1240 | *cum = NUM_ARGUMENT_REGISTERS; |
5ab9749e | 1241 | |
6930c98c | 1242 | *cum += XSTORMY16_WORD_SIZE (arg.type, arg.mode); |
4b58290f GK |
1243 | } |
1244 | ||
bf425ddd | 1245 | static rtx |
6783fdb7 | 1246 | xstormy16_function_arg (cumulative_args_t cum_v, const function_arg_info &arg) |
06d22853 | 1247 | { |
d5cc9181 JR |
1248 | CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); |
1249 | ||
6783fdb7 | 1250 | if (arg.end_marker_p ()) |
06d22853 | 1251 | return const0_rtx; |
0ffef200 | 1252 | if (targetm.calls.must_pass_in_stack (arg) |
6783fdb7 RS |
1253 | || (*cum + XSTORMY16_WORD_SIZE (arg.type, arg.mode) |
1254 | > NUM_ARGUMENT_REGISTERS)) | |
5ab9749e | 1255 | return NULL_RTX; |
6783fdb7 | 1256 | return gen_rtx_REG (arg.mode, *cum + FIRST_ARGUMENT_REGISTER); |
06d22853 DD |
1257 | } |
1258 | ||
4b58290f GK |
1259 | /* Build the va_list type. |
1260 | ||
1261 | For this chip, va_list is a record containing a counter and a pointer. | |
1262 | The counter is of type 'int' and indicates how many bytes | |
1263 | have been used to date. The pointer indicates the stack position | |
5ab9749e | 1264 | for arguments that have not been passed in registers. |
4b58290f GK |
1265 | To keep the layout nice, the pointer is first in the structure. */ |
1266 | ||
37cd4bca NC |
1267 | static tree |
1268 | xstormy16_build_builtin_va_list (void) | |
4b58290f GK |
1269 | { |
1270 | tree f_1, f_2, record, type_decl; | |
1271 | ||
f1e639b1 | 1272 | record = (*lang_hooks.types.make_type) (RECORD_TYPE); |
4c4bde29 AH |
1273 | type_decl = build_decl (BUILTINS_LOCATION, |
1274 | TYPE_DECL, get_identifier ("__va_list_tag"), record); | |
4b58290f | 1275 | |
4c4bde29 AH |
1276 | f_1 = build_decl (BUILTINS_LOCATION, |
1277 | FIELD_DECL, get_identifier ("base"), | |
4b58290f | 1278 | ptr_type_node); |
4c4bde29 AH |
1279 | f_2 = build_decl (BUILTINS_LOCATION, |
1280 | FIELD_DECL, get_identifier ("count"), | |
4b58290f GK |
1281 | unsigned_type_node); |
1282 | ||
1283 | DECL_FIELD_CONTEXT (f_1) = record; | |
1284 | DECL_FIELD_CONTEXT (f_2) = record; | |
1285 | ||
0fd2eac2 | 1286 | TYPE_STUB_DECL (record) = type_decl; |
4b58290f GK |
1287 | TYPE_NAME (record) = type_decl; |
1288 | TYPE_FIELDS (record) = f_1; | |
910ad8de | 1289 | DECL_CHAIN (f_1) = f_2; |
4b58290f GK |
1290 | |
1291 | layout_type (record); | |
1292 | ||
1293 | return record; | |
1294 | } | |
1295 | ||
5e7a8ee0 | 1296 | /* Implement the stdarg/varargs va_start macro. STDARG_P is nonzero if this |
4b58290f GK |
1297 | is stdarg.h instead of varargs.h. VALIST is the tree of the va_list |
1298 | variable to initialize. NEXTARG is the machine independent notion of the | |
1299 | 'next' argument after the variable arguments. */ | |
5ab9749e | 1300 | |
d7bd8aeb | 1301 | static void |
51c16b7e | 1302 | xstormy16_expand_builtin_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED) |
4b58290f GK |
1303 | { |
1304 | tree f_base, f_count; | |
1305 | tree base, count; | |
f84fe9b6 | 1306 | tree t,u; |
4b58290f | 1307 | |
c6243b4c | 1308 | if (xstormy16_interrupt_function_p ()) |
4b58290f | 1309 | error ("cannot use va_start in interrupt function"); |
5ab9749e | 1310 | |
4b58290f | 1311 | f_base = TYPE_FIELDS (va_list_type_node); |
910ad8de | 1312 | f_count = DECL_CHAIN (f_base); |
5ab9749e | 1313 | |
47a25a46 RG |
1314 | base = build3 (COMPONENT_REF, TREE_TYPE (f_base), valist, f_base, NULL_TREE); |
1315 | count = build3 (COMPONENT_REF, TREE_TYPE (f_count), valist, f_count, | |
1316 | NULL_TREE); | |
4b58290f GK |
1317 | |
1318 | t = make_tree (TREE_TYPE (base), virtual_incoming_args_rtx); | |
b72bbbcb | 1319 | u = build_int_cst (NULL_TREE, - INCOMING_FRAME_SP_OFFSET); |
f84fe9b6 | 1320 | u = fold_convert (TREE_TYPE (count), u); |
5d49b6a7 | 1321 | t = fold_build_pointer_plus (t, u); |
726a989a | 1322 | t = build2 (MODIFY_EXPR, TREE_TYPE (base), base, t); |
4b58290f GK |
1323 | TREE_SIDE_EFFECTS (t) = 1; |
1324 | expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); | |
1325 | ||
5ab9749e | 1326 | t = build2 (MODIFY_EXPR, TREE_TYPE (count), count, |
47a25a46 | 1327 | build_int_cst (NULL_TREE, |
38173d38 | 1328 | crtl->args.info * UNITS_PER_WORD)); |
4b58290f GK |
1329 | TREE_SIDE_EFFECTS (t) = 1; |
1330 | expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); | |
1331 | } | |
1332 | ||
1333 | /* Implement the stdarg/varargs va_arg macro. VALIST is the variable | |
cf4c092e CM |
1334 | of type va_list as a tree, TYPE is the type passed to va_arg. |
1335 | Note: This algorithm is documented in stormy-abi. */ | |
5ab9749e | 1336 | |
5d47df87 | 1337 | static tree |
726a989a RB |
1338 | xstormy16_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, |
1339 | gimple_seq *post_p ATTRIBUTE_UNUSED) | |
4b58290f GK |
1340 | { |
1341 | tree f_base, f_count; | |
1342 | tree base, count; | |
5d47df87 RH |
1343 | tree count_tmp, addr, t; |
1344 | tree lab_gotaddr, lab_fromstack; | |
06d22853 | 1345 | int size, size_of_reg_args, must_stack; |
5d47df87 RH |
1346 | tree size_tree; |
1347 | ||
4b58290f | 1348 | f_base = TYPE_FIELDS (va_list_type_node); |
910ad8de | 1349 | f_count = DECL_CHAIN (f_base); |
5ab9749e | 1350 | |
47a25a46 RG |
1351 | base = build3 (COMPONENT_REF, TREE_TYPE (f_base), valist, f_base, NULL_TREE); |
1352 | count = build3 (COMPONENT_REF, TREE_TYPE (f_count), valist, f_count, | |
1353 | NULL_TREE); | |
4b58290f | 1354 | |
4f53599c | 1355 | must_stack = must_pass_va_arg_in_stack (type); |
4b58290f | 1356 | size_tree = round_up (size_in_bytes (type), UNITS_PER_WORD); |
5d47df87 | 1357 | gimplify_expr (&size_tree, pre_p, NULL, is_gimple_val, fb_rvalue); |
5ab9749e | 1358 | |
cf4c092e | 1359 | size_of_reg_args = NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD; |
4b58290f | 1360 | |
5d47df87 | 1361 | count_tmp = get_initialized_tmp_var (count, pre_p, NULL); |
4c4bde29 AH |
1362 | lab_gotaddr = create_artificial_label (UNKNOWN_LOCATION); |
1363 | lab_fromstack = create_artificial_label (UNKNOWN_LOCATION); | |
9b489f31 | 1364 | addr = create_tmp_var (ptr_type_node); |
cf4c092e | 1365 | |
06d22853 DD |
1366 | if (!must_stack) |
1367 | { | |
5d47df87 RH |
1368 | tree r; |
1369 | ||
1370 | t = fold_convert (TREE_TYPE (count), size_tree); | |
47a25a46 | 1371 | t = build2 (PLUS_EXPR, TREE_TYPE (count), count_tmp, t); |
5d47df87 | 1372 | r = fold_convert (TREE_TYPE (count), size_int (size_of_reg_args)); |
47a25a46 RG |
1373 | t = build2 (GT_EXPR, boolean_type_node, t, r); |
1374 | t = build3 (COND_EXPR, void_type_node, t, | |
1375 | build1 (GOTO_EXPR, void_type_node, lab_fromstack), | |
1376 | NULL_TREE); | |
5d47df87 | 1377 | gimplify_and_add (t, pre_p); |
f84fe9b6 | 1378 | |
5d49b6a7 | 1379 | t = fold_build_pointer_plus (base, count_tmp); |
726a989a | 1380 | gimplify_assign (addr, t, pre_p); |
5d47df87 | 1381 | |
47a25a46 | 1382 | t = build1 (GOTO_EXPR, void_type_node, lab_gotaddr); |
5d47df87 RH |
1383 | gimplify_and_add (t, pre_p); |
1384 | ||
47a25a46 | 1385 | t = build1 (LABEL_EXPR, void_type_node, lab_fromstack); |
5d47df87 | 1386 | gimplify_and_add (t, pre_p); |
06d22853 | 1387 | } |
5ab9749e | 1388 | |
4b58290f GK |
1389 | /* Arguments larger than a word might need to skip over some |
1390 | registers, since arguments are either passed entirely in | |
1391 | registers or entirely on the stack. */ | |
06d22853 DD |
1392 | size = PUSH_ROUNDING (int_size_in_bytes (type)); |
1393 | if (size > 2 || size < 0 || must_stack) | |
4b58290f | 1394 | { |
5d47df87 RH |
1395 | tree r, u; |
1396 | ||
1397 | r = size_int (NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD); | |
726a989a | 1398 | u = build2 (MODIFY_EXPR, TREE_TYPE (count_tmp), count_tmp, r); |
5d47df87 RH |
1399 | |
1400 | t = fold_convert (TREE_TYPE (count), r); | |
47a25a46 RG |
1401 | t = build2 (GE_EXPR, boolean_type_node, count_tmp, t); |
1402 | t = build3 (COND_EXPR, void_type_node, t, NULL_TREE, u); | |
5d47df87 | 1403 | gimplify_and_add (t, pre_p); |
4b58290f GK |
1404 | } |
1405 | ||
5d47df87 | 1406 | t = size_int (NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD |
b72bbbcb | 1407 | + INCOMING_FRAME_SP_OFFSET); |
5d47df87 | 1408 | t = fold_convert (TREE_TYPE (count), t); |
47a25a46 RG |
1409 | t = build2 (MINUS_EXPR, TREE_TYPE (count), count_tmp, t); |
1410 | t = build2 (PLUS_EXPR, TREE_TYPE (count), t, | |
1411 | fold_convert (TREE_TYPE (count), size_tree)); | |
f84fe9b6 NC |
1412 | t = fold_convert (TREE_TYPE (t), fold (t)); |
1413 | t = fold_build1 (NEGATE_EXPR, TREE_TYPE (t), t); | |
5d49b6a7 | 1414 | t = fold_build_pointer_plus (base, t); |
726a989a | 1415 | gimplify_assign (addr, t, pre_p); |
5d47df87 | 1416 | |
47a25a46 | 1417 | t = build1 (LABEL_EXPR, void_type_node, lab_gotaddr); |
5d47df87 | 1418 | gimplify_and_add (t, pre_p); |
4b58290f | 1419 | |
5d47df87 | 1420 | t = fold_convert (TREE_TYPE (count), size_tree); |
47a25a46 | 1421 | t = build2 (PLUS_EXPR, TREE_TYPE (count), count_tmp, t); |
726a989a | 1422 | gimplify_assign (count, t, pre_p); |
5ab9749e | 1423 | |
5d47df87 | 1424 | addr = fold_convert (build_pointer_type (type), addr); |
d6e9821f | 1425 | return build_va_arg_indirect_ref (addr); |
4b58290f GK |
1426 | } |
1427 | ||
fb8d0fac | 1428 | /* Worker function for TARGET_TRAMPOLINE_INIT. */ |
5ab9749e | 1429 | |
fb8d0fac RH |
1430 | static void |
1431 | xstormy16_trampoline_init (rtx m_tramp, tree fndecl, rtx static_chain) | |
4b58290f | 1432 | { |
4b58290f GK |
1433 | rtx temp = gen_reg_rtx (HImode); |
1434 | rtx reg_fnaddr = gen_reg_rtx (HImode); | |
fb8d0fac | 1435 | rtx reg_addr, reg_addr_mem; |
4b58290f | 1436 | |
fb8d0fac RH |
1437 | reg_addr = copy_to_reg (XEXP (m_tramp, 0)); |
1438 | reg_addr_mem = adjust_automodify_address (m_tramp, HImode, reg_addr, 0); | |
5ab9749e | 1439 | |
4b58290f GK |
1440 | emit_move_insn (temp, GEN_INT (0x3130 | STATIC_CHAIN_REGNUM)); |
1441 | emit_move_insn (reg_addr_mem, temp); | |
e2470e1b | 1442 | emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx)); |
fb8d0fac RH |
1443 | reg_addr_mem = adjust_automodify_address (reg_addr_mem, VOIDmode, NULL, 2); |
1444 | ||
4b58290f GK |
1445 | emit_move_insn (temp, static_chain); |
1446 | emit_move_insn (reg_addr_mem, temp); | |
e2470e1b | 1447 | emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx)); |
fb8d0fac RH |
1448 | reg_addr_mem = adjust_automodify_address (reg_addr_mem, VOIDmode, NULL, 2); |
1449 | ||
1450 | emit_move_insn (reg_fnaddr, XEXP (DECL_RTL (fndecl), 0)); | |
4b58290f GK |
1451 | emit_move_insn (temp, reg_fnaddr); |
1452 | emit_insn (gen_andhi3 (temp, temp, GEN_INT (0xFF))); | |
1453 | emit_insn (gen_iorhi3 (temp, temp, GEN_INT (0x0200))); | |
1454 | emit_move_insn (reg_addr_mem, temp); | |
e2470e1b | 1455 | emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx)); |
fb8d0fac RH |
1456 | reg_addr_mem = adjust_automodify_address (reg_addr_mem, VOIDmode, NULL, 2); |
1457 | ||
4b58290f GK |
1458 | emit_insn (gen_lshrhi3 (reg_fnaddr, reg_fnaddr, GEN_INT (8))); |
1459 | emit_move_insn (reg_addr_mem, reg_fnaddr); | |
1460 | } | |
1461 | ||
998871e9 | 1462 | /* Worker function for TARGET_FUNCTION_VALUE. */ |
bd5bd7ac | 1463 | |
998871e9 AS |
1464 | static rtx |
1465 | xstormy16_function_value (const_tree valtype, | |
1466 | const_tree func ATTRIBUTE_UNUSED, | |
1467 | bool outgoing ATTRIBUTE_UNUSED) | |
4b58290f | 1468 | { |
ef4bddc2 | 1469 | machine_mode mode; |
4b58290f GK |
1470 | mode = TYPE_MODE (valtype); |
1471 | PROMOTE_MODE (mode, 0, valtype); | |
1472 | return gen_rtx_REG (mode, RETURN_VALUE_REGNUM); | |
1473 | } | |
1474 | ||
998871e9 AS |
1475 | /* Worker function for TARGET_LIBCALL_VALUE. */ |
1476 | ||
1477 | static rtx | |
ef4bddc2 | 1478 | xstormy16_libcall_value (machine_mode mode, |
998871e9 AS |
1479 | const_rtx fun ATTRIBUTE_UNUSED) |
1480 | { | |
1481 | return gen_rtx_REG (mode, RETURN_VALUE_REGNUM); | |
1482 | } | |
1483 | ||
1484 | /* Worker function for TARGET_FUNCTION_VALUE_REGNO_P. */ | |
1485 | ||
1486 | static bool | |
1487 | xstormy16_function_value_regno_p (const unsigned int regno) | |
1488 | { | |
1489 | return (regno == RETURN_VALUE_REGNUM); | |
1490 | } | |
1491 | ||
52560c7b GK |
1492 | /* A C compound statement that outputs the assembler code for a thunk function, |
1493 | used to implement C++ virtual function calls with multiple inheritance. The | |
1494 | thunk acts as a wrapper around a virtual function, adjusting the implicit | |
1495 | object parameter before handing control off to the real function. | |
1496 | ||
1497 | First, emit code to add the integer DELTA to the location that contains the | |
1498 | incoming first argument. Assume that this argument contains a pointer, and | |
1499 | is the one used to pass the `this' pointer in C++. This is the incoming | |
1500 | argument *before* the function prologue, e.g. `%o0' on a sparc. The | |
1501 | addition must preserve the values of all other incoming arguments. | |
1502 | ||
1503 | After the addition, emit code to jump to FUNCTION, which is a | |
1504 | `FUNCTION_DECL'. This is a direct pure jump, not a call, and does not touch | |
1505 | the return address. Hence returning from FUNCTION will return to whoever | |
1506 | called the current `thunk'. | |
1507 | ||
1508 | The effect must be as if @var{function} had been called directly | |
1509 | with the adjusted first argument. This macro is responsible for | |
1510 | emitting all of the code for a thunk function; | |
1511 | TARGET_ASM_FUNCTION_PROLOGUE and TARGET_ASM_FUNCTION_EPILOGUE are | |
1512 | not invoked. | |
1513 | ||
1514 | The THUNK_FNDECL is redundant. (DELTA and FUNCTION have already been | |
1515 | extracted from it.) It might possibly be useful on some targets, but | |
1516 | probably not. */ | |
1517 | ||
c590b625 | 1518 | static void |
51c16b7e SB |
1519 | xstormy16_asm_output_mi_thunk (FILE *file, |
1520 | tree thunk_fndecl ATTRIBUTE_UNUSED, | |
1521 | HOST_WIDE_INT delta, | |
1522 | HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED, | |
1523 | tree function) | |
52560c7b | 1524 | { |
f7430263 | 1525 | const char *fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk_fndecl)); |
52560c7b | 1526 | int regnum = FIRST_ARGUMENT_REGISTER; |
5ab9749e | 1527 | |
f7430263 | 1528 | assemble_start_function (thunk_fndecl, fnname); |
52560c7b | 1529 | /* There might be a hidden first argument for a returned structure. */ |
61f71b34 | 1530 | if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) |
52560c7b | 1531 | regnum += 1; |
5ab9749e | 1532 | |
eb0424da | 1533 | fprintf (file, "\tadd %s,#0x%x\n", reg_names[regnum], (int) delta & 0xFFFF); |
52560c7b GK |
1534 | fputs ("\tjmpf ", file); |
1535 | assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0)); | |
1536 | putc ('\n', file); | |
f7430263 | 1537 | assemble_end_function (thunk_fndecl, fnname); |
52560c7b GK |
1538 | } |
1539 | ||
54e9a19d DD |
1540 | /* The purpose of this function is to override the default behavior of |
1541 | BSS objects. Normally, they go into .bss or .sbss via ".common" | |
1542 | directives, but we need to override that and put them in | |
1543 | .bss_below100. We can't just use a section override (like we do | |
1544 | for .data_below100), because that makes them initialized rather | |
1545 | than uninitialized. */ | |
5ab9749e | 1546 | |
54e9a19d DD |
1547 | void |
1548 | xstormy16_asm_output_aligned_common (FILE *stream, | |
2f806f3b | 1549 | tree decl, |
54e9a19d DD |
1550 | const char *name, |
1551 | int size, | |
1552 | int align, | |
1553 | int global) | |
1554 | { | |
038eab67 | 1555 | rtx mem = decl == NULL_TREE ? NULL_RTX : DECL_RTL (decl); |
2f806f3b | 1556 | rtx symbol; |
5ab9749e | 1557 | |
2f806f3b | 1558 | if (mem != NULL_RTX |
a21eaf5e | 1559 | && MEM_P (mem) |
2f806f3b NC |
1560 | && GET_CODE (symbol = XEXP (mem, 0)) == SYMBOL_REF |
1561 | && SYMBOL_REF_FLAGS (symbol) & SYMBOL_FLAG_XSTORMY16_BELOW100) | |
54e9a19d | 1562 | { |
2f806f3b NC |
1563 | const char *name2; |
1564 | int p2align = 0; | |
1565 | ||
d6b5193b | 1566 | switch_to_section (bss100_section); |
2f806f3b NC |
1567 | |
1568 | while (align > 8) | |
54e9a19d | 1569 | { |
2f806f3b NC |
1570 | align /= 2; |
1571 | p2align ++; | |
54e9a19d | 1572 | } |
54e9a19d | 1573 | |
2f806f3b NC |
1574 | name2 = default_strip_name_encoding (name); |
1575 | if (global) | |
1576 | fprintf (stream, "\t.globl\t%s\n", name2); | |
1577 | if (p2align) | |
1578 | fprintf (stream, "\t.p2align %d\n", p2align); | |
1579 | fprintf (stream, "\t.type\t%s, @object\n", name2); | |
1580 | fprintf (stream, "\t.size\t%s, %d\n", name2, size); | |
1581 | fprintf (stream, "%s:\n\t.space\t%d\n", name2, size); | |
1582 | return; | |
54e9a19d DD |
1583 | } |
1584 | ||
1585 | if (!global) | |
1586 | { | |
1587 | fprintf (stream, "\t.local\t"); | |
1588 | assemble_name (stream, name); | |
1589 | fprintf (stream, "\n"); | |
1590 | } | |
1591 | fprintf (stream, "\t.comm\t"); | |
1592 | assemble_name (stream, name); | |
43f51151 | 1593 | fprintf (stream, ",%u,%u\n", size, align / BITS_PER_UNIT); |
54e9a19d DD |
1594 | } |
1595 | ||
d6b5193b RS |
1596 | /* Implement TARGET_ASM_INIT_SECTIONS. */ |
1597 | ||
1598 | static void | |
1599 | xstormy16_asm_init_sections (void) | |
1600 | { | |
1601 | bss100_section | |
1602 | = get_unnamed_section (SECTION_WRITE | SECTION_BSS, | |
1603 | output_section_asm_op, | |
1604 | "\t.section \".bss_below100\",\"aw\",@nobits"); | |
1605 | } | |
1606 | ||
54e9a19d DD |
1607 | /* Mark symbols with the "below100" attribute so that we can use the |
1608 | special addressing modes for them. */ | |
1609 | ||
1610 | static void | |
2f806f3b | 1611 | xstormy16_encode_section_info (tree decl, rtx r, int first) |
54e9a19d | 1612 | { |
e5eb9a52 NC |
1613 | default_encode_section_info (decl, r, first); |
1614 | ||
2f806f3b | 1615 | if (TREE_CODE (decl) == VAR_DECL |
54e9a19d DD |
1616 | && (lookup_attribute ("below100", DECL_ATTRIBUTES (decl)) |
1617 | || lookup_attribute ("BELOW100", DECL_ATTRIBUTES (decl)))) | |
1618 | { | |
2f806f3b | 1619 | rtx symbol = XEXP (r, 0); |
5ab9749e | 1620 | |
2f806f3b NC |
1621 | gcc_assert (GET_CODE (symbol) == SYMBOL_REF); |
1622 | SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_XSTORMY16_BELOW100; | |
54e9a19d DD |
1623 | } |
1624 | } | |
1625 | ||
6208b55d | 1626 | #undef TARGET_ASM_CONSTRUCTOR |
5ab9749e | 1627 | #define TARGET_ASM_CONSTRUCTOR xstormy16_asm_out_constructor |
6208b55d | 1628 | #undef TARGET_ASM_DESTRUCTOR |
5ab9749e NC |
1629 | #define TARGET_ASM_DESTRUCTOR xstormy16_asm_out_destructor |
1630 | ||
1631 | /* Output constructors and destructors. Just like | |
1632 | default_named_section_asm_out_* but don't set the sections writable. */ | |
43898541 GK |
1633 | |
1634 | static void | |
51c16b7e | 1635 | xstormy16_asm_out_destructor (rtx symbol, int priority) |
43898541 GK |
1636 | { |
1637 | const char *section = ".dtors"; | |
42ee9611 | 1638 | char buf[18]; |
43898541 | 1639 | |
71cc389b | 1640 | /* ??? This only works reliably with the GNU linker. */ |
43898541 GK |
1641 | if (priority != DEFAULT_INIT_PRIORITY) |
1642 | { | |
1643 | sprintf (buf, ".dtors.%.5u", | |
1644 | /* Invert the numbering so the linker puts us in the proper | |
1645 | order; constructors are run from right to left, and the | |
1646 | linker sorts in increasing order. */ | |
1647 | MAX_INIT_PRIORITY - priority); | |
1648 | section = buf; | |
1649 | } | |
1650 | ||
d6b5193b | 1651 | switch_to_section (get_section (section, 0, NULL)); |
43898541 GK |
1652 | assemble_align (POINTER_SIZE); |
1653 | assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1); | |
1654 | } | |
1655 | ||
1656 | static void | |
51c16b7e | 1657 | xstormy16_asm_out_constructor (rtx symbol, int priority) |
43898541 GK |
1658 | { |
1659 | const char *section = ".ctors"; | |
42ee9611 | 1660 | char buf[18]; |
43898541 | 1661 | |
71cc389b | 1662 | /* ??? This only works reliably with the GNU linker. */ |
43898541 GK |
1663 | if (priority != DEFAULT_INIT_PRIORITY) |
1664 | { | |
1665 | sprintf (buf, ".ctors.%.5u", | |
1666 | /* Invert the numbering so the linker puts us in the proper | |
1667 | order; constructors are run from right to left, and the | |
1668 | linker sorts in increasing order. */ | |
1669 | MAX_INIT_PRIORITY - priority); | |
1670 | section = buf; | |
1671 | } | |
1672 | ||
d6b5193b | 1673 | switch_to_section (get_section (section, 0, NULL)); |
43898541 GK |
1674 | assemble_align (POINTER_SIZE); |
1675 | assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1); | |
1676 | } | |
4b58290f | 1677 | \f |
43070a6e | 1678 | /* Worker function for TARGET_PRINT_OPERAND_ADDRESS. |
5ab9749e | 1679 | |
43070a6e AS |
1680 | Print a memory address as an operand to reference that memory location. */ |
1681 | ||
1682 | static void | |
cc8ca59e JB |
1683 | xstormy16_print_operand_address (FILE *file, machine_mode /*mode*/, |
1684 | rtx address) | |
4b58290f GK |
1685 | { |
1686 | HOST_WIDE_INT offset; | |
1687 | int pre_dec, post_inc; | |
1688 | ||
1689 | /* There are a few easy cases. */ | |
a21eaf5e | 1690 | if (CONST_INT_P (address)) |
4b58290f GK |
1691 | { |
1692 | fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (address) & 0xFFFF); | |
1693 | return; | |
1694 | } | |
5ab9749e | 1695 | |
a21eaf5e | 1696 | if (CONSTANT_P (address) || LABEL_P (address)) |
4b58290f GK |
1697 | { |
1698 | output_addr_const (file, address); | |
1699 | return; | |
1700 | } | |
4b58290f | 1701 | |
5ab9749e NC |
1702 | /* Otherwise, it's hopefully something of the form |
1703 | (plus:HI (pre_dec:HI (reg:HI ...)) (const_int ...)). */ | |
4b58290f GK |
1704 | if (GET_CODE (address) == PLUS) |
1705 | { | |
a21eaf5e | 1706 | gcc_assert (CONST_INT_P (XEXP (address, 1))); |
4b58290f GK |
1707 | offset = INTVAL (XEXP (address, 1)); |
1708 | address = XEXP (address, 0); | |
1709 | } | |
1710 | else | |
1711 | offset = 0; | |
1712 | ||
1713 | pre_dec = (GET_CODE (address) == PRE_DEC); | |
1714 | post_inc = (GET_CODE (address) == POST_INC); | |
1715 | if (pre_dec || post_inc) | |
1716 | address = XEXP (address, 0); | |
5ab9749e | 1717 | |
a21eaf5e | 1718 | gcc_assert (REG_P (address)); |
4b58290f GK |
1719 | |
1720 | fputc ('(', file); | |
1721 | if (pre_dec) | |
1722 | fputs ("--", file); | |
1723 | fputs (reg_names [REGNO (address)], file); | |
1724 | if (post_inc) | |
1725 | fputs ("++", file); | |
1726 | if (offset != 0) | |
4a0a75dd | 1727 | fprintf (file, "," HOST_WIDE_INT_PRINT_DEC, offset); |
4b58290f GK |
1728 | fputc (')', file); |
1729 | } | |
1730 | ||
43070a6e | 1731 | /* Worker function for TARGET_PRINT_OPERAND. |
5ab9749e | 1732 | |
43070a6e AS |
1733 | Print an operand to an assembler instruction. */ |
1734 | ||
1735 | static void | |
51c16b7e | 1736 | xstormy16_print_operand (FILE *file, rtx x, int code) |
4b58290f GK |
1737 | { |
1738 | switch (code) | |
1739 | { | |
1740 | case 'B': | |
1741 | /* There is either one bit set, or one bit clear, in X. | |
1742 | Print it preceded by '#'. */ | |
1743 | { | |
54e9a19d | 1744 | static int bits_set[8] = { 0, 1, 1, 2, 1, 2, 2, 3 }; |
e9818db2 GK |
1745 | HOST_WIDE_INT xx = 1; |
1746 | HOST_WIDE_INT l; | |
4b58290f | 1747 | |
a21eaf5e | 1748 | if (CONST_INT_P (x)) |
4b58290f GK |
1749 | xx = INTVAL (x); |
1750 | else | |
9e637a26 | 1751 | output_operand_lossage ("'B' operand is not constant"); |
5ab9749e | 1752 | |
54e9a19d DD |
1753 | /* GCC sign-extends masks with the MSB set, so we have to |
1754 | detect all the cases that differ only in sign extension | |
1755 | beyond the bits we care about. Normally, the predicates | |
1756 | and constraints ensure that we have the right values. This | |
1757 | works correctly for valid masks. */ | |
1758 | if (bits_set[xx & 7] <= 1) | |
1759 | { | |
1760 | /* Remove sign extension bits. */ | |
1761 | if ((~xx & ~(HOST_WIDE_INT)0xff) == 0) | |
1762 | xx &= 0xff; | |
1763 | else if ((~xx & ~(HOST_WIDE_INT)0xffff) == 0) | |
1764 | xx &= 0xffff; | |
1765 | l = exact_log2 (xx); | |
1766 | } | |
1767 | else | |
1768 | { | |
1769 | /* Add sign extension bits. */ | |
1770 | if ((xx & ~(HOST_WIDE_INT)0xff) == 0) | |
1771 | xx |= ~(HOST_WIDE_INT)0xff; | |
1772 | else if ((xx & ~(HOST_WIDE_INT)0xffff) == 0) | |
1773 | xx |= ~(HOST_WIDE_INT)0xffff; | |
1774 | l = exact_log2 (~xx); | |
1775 | } | |
1776 | ||
4b58290f | 1777 | if (l == -1) |
9e637a26 | 1778 | output_operand_lossage ("'B' operand has multiple bits set"); |
5ab9749e | 1779 | |
4a0a75dd | 1780 | fprintf (file, IMMEDIATE_PREFIX HOST_WIDE_INT_PRINT_DEC, l); |
4b58290f GK |
1781 | return; |
1782 | } | |
1783 | ||
1784 | case 'C': | |
1785 | /* Print the symbol without a surrounding @fptr(). */ | |
1786 | if (GET_CODE (x) == SYMBOL_REF) | |
1787 | assemble_name (file, XSTR (x, 0)); | |
a21eaf5e | 1788 | else if (LABEL_P (x)) |
2f0b7af6 | 1789 | output_asm_label (x); |
4b58290f | 1790 | else |
cc8ca59e | 1791 | xstormy16_print_operand_address (file, VOIDmode, x); |
4b58290f GK |
1792 | return; |
1793 | ||
1794 | case 'o': | |
1795 | case 'O': | |
5ab9749e | 1796 | /* Print the immediate operand less one, preceded by '#'. |
4b58290f GK |
1797 | For 'O', negate it first. */ |
1798 | { | |
e9818db2 | 1799 | HOST_WIDE_INT xx = 0; |
5ab9749e | 1800 | |
a21eaf5e | 1801 | if (CONST_INT_P (x)) |
4b58290f GK |
1802 | xx = INTVAL (x); |
1803 | else | |
9e637a26 | 1804 | output_operand_lossage ("'o' operand is not constant"); |
5ab9749e | 1805 | |
4b58290f GK |
1806 | if (code == 'O') |
1807 | xx = -xx; | |
5ab9749e | 1808 | |
4a0a75dd | 1809 | fprintf (file, IMMEDIATE_PREFIX HOST_WIDE_INT_PRINT_DEC, xx - 1); |
4b58290f GK |
1810 | return; |
1811 | } | |
1812 | ||
54e9a19d DD |
1813 | case 'b': |
1814 | /* Print the shift mask for bp/bn. */ | |
1815 | { | |
1816 | HOST_WIDE_INT xx = 1; | |
1817 | HOST_WIDE_INT l; | |
1818 | ||
a21eaf5e | 1819 | if (CONST_INT_P (x)) |
54e9a19d DD |
1820 | xx = INTVAL (x); |
1821 | else | |
9e637a26 | 1822 | output_operand_lossage ("'B' operand is not constant"); |
5ab9749e | 1823 | |
54e9a19d | 1824 | l = 7 - xx; |
5ab9749e | 1825 | |
54e9a19d DD |
1826 | fputs (IMMEDIATE_PREFIX, file); |
1827 | fprintf (file, HOST_WIDE_INT_PRINT_DEC, l); | |
1828 | return; | |
1829 | } | |
1830 | ||
4b58290f GK |
1831 | case 0: |
1832 | /* Handled below. */ | |
1833 | break; | |
5ab9749e | 1834 | |
4b58290f | 1835 | default: |
c6243b4c | 1836 | output_operand_lossage ("xstormy16_print_operand: unknown code"); |
4b58290f GK |
1837 | return; |
1838 | } | |
1839 | ||
1840 | switch (GET_CODE (x)) | |
1841 | { | |
1842 | case REG: | |
1843 | fputs (reg_names [REGNO (x)], file); | |
1844 | break; | |
1845 | ||
1846 | case MEM: | |
cc8ca59e | 1847 | xstormy16_print_operand_address (file, GET_MODE (x), XEXP (x, 0)); |
4b58290f GK |
1848 | break; |
1849 | ||
1850 | default: | |
1851 | /* Some kind of constant or label; an immediate operand, | |
1852 | so prefix it with '#' for the assembler. */ | |
1853 | fputs (IMMEDIATE_PREFIX, file); | |
1854 | output_addr_const (file, x); | |
1855 | break; | |
1856 | } | |
1857 | ||
1858 | return; | |
1859 | } | |
4b58290f GK |
1860 | \f |
1861 | /* Expander for the `casesi' pattern. | |
1862 | INDEX is the index of the switch statement. | |
1863 | LOWER_BOUND is a CONST_INT that is the value of INDEX corresponding | |
1864 | to the first table entry. | |
1865 | RANGE is the number of table entries. | |
1866 | TABLE is an ADDR_VEC that is the jump table. | |
1867 | DEFAULT_LABEL is the address to branch to if INDEX is outside the | |
5ab9749e | 1868 | range LOWER_BOUND to LOWER_BOUND + RANGE - 1. */ |
4b58290f | 1869 | |
5ab9749e | 1870 | void |
51c16b7e SB |
1871 | xstormy16_expand_casesi (rtx index, rtx lower_bound, rtx range, |
1872 | rtx table, rtx default_label) | |
4b58290f GK |
1873 | { |
1874 | HOST_WIDE_INT range_i = INTVAL (range); | |
1875 | rtx int_index; | |
1876 | ||
1877 | /* This code uses 'br', so it can deal only with tables of size up to | |
1878 | 8192 entries. */ | |
1879 | if (range_i >= 8192) | |
5ab9749e | 1880 | sorry ("switch statement of size %lu entries too large", |
4b58290f GK |
1881 | (unsigned long) range_i); |
1882 | ||
4192f0d2 | 1883 | index = expand_binop (SImode, sub_optab, index, lower_bound, NULL_RTX, 0, |
4b58290f GK |
1884 | OPTAB_LIB_WIDEN); |
1885 | emit_cmp_and_jump_insns (index, range, GTU, NULL_RTX, SImode, 1, | |
40c13662 | 1886 | default_label); |
4b58290f | 1887 | int_index = gen_lowpart_common (HImode, index); |
a556fd39 | 1888 | emit_insn (gen_ashlhi3 (int_index, int_index, const2_rtx)); |
4b58290f GK |
1889 | emit_jump_insn (gen_tablejump_pcrel (int_index, table)); |
1890 | } | |
1891 | ||
1892 | /* Output an ADDR_VEC. It is output as a sequence of 'jmpf' | |
1893 | instructions, without label or alignment or any other special | |
1894 | constructs. We know that the previous instruction will be the | |
1895 | `tablejump_pcrel' output above. | |
1896 | ||
1897 | TODO: it might be nice to output 'br' instructions if they could | |
1898 | all reach. */ | |
1899 | ||
1900 | void | |
51c16b7e | 1901 | xstormy16_output_addr_vec (FILE *file, rtx label ATTRIBUTE_UNUSED, rtx table) |
5ab9749e | 1902 | { |
4b58290f | 1903 | int vlen, idx; |
5ab9749e | 1904 | |
d6b5193b | 1905 | switch_to_section (current_function_section ()); |
4b58290f GK |
1906 | |
1907 | vlen = XVECLEN (table, 0); | |
1908 | for (idx = 0; idx < vlen; idx++) | |
1909 | { | |
1910 | fputs ("\tjmpf ", file); | |
2f0b7af6 | 1911 | output_asm_label (XEXP (XVECEXP (table, 0, idx), 0)); |
4b58290f GK |
1912 | fputc ('\n', file); |
1913 | } | |
1914 | } | |
4b58290f GK |
1915 | \f |
1916 | /* Expander for the `call' patterns. | |
a21eaf5e NC |
1917 | RETVAL is the RTL for the return register or NULL for void functions. |
1918 | DEST is the function to call, expressed as a MEM. | |
1919 | COUNTER is ignored. */ | |
4b58290f | 1920 | |
5ab9749e | 1921 | void |
51c16b7e | 1922 | xstormy16_expand_call (rtx retval, rtx dest, rtx counter) |
4b58290f GK |
1923 | { |
1924 | rtx call, temp; | |
ef4bddc2 | 1925 | machine_mode mode; |
4b58290f | 1926 | |
a21eaf5e | 1927 | gcc_assert (MEM_P (dest)); |
4b58290f GK |
1928 | dest = XEXP (dest, 0); |
1929 | ||
a21eaf5e | 1930 | if (! CONSTANT_P (dest) && ! REG_P (dest)) |
4b58290f | 1931 | dest = force_reg (Pmode, dest); |
5ab9749e | 1932 | |
4b58290f GK |
1933 | if (retval == NULL) |
1934 | mode = VOIDmode; | |
1935 | else | |
1936 | mode = GET_MODE (retval); | |
1937 | ||
1938 | call = gen_rtx_CALL (mode, gen_rtx_MEM (FUNCTION_MODE, dest), | |
1939 | counter); | |
1940 | if (retval) | |
f7df4a84 | 1941 | call = gen_rtx_SET (retval, call); |
5ab9749e | 1942 | |
4b58290f GK |
1943 | if (! CONSTANT_P (dest)) |
1944 | { | |
1945 | temp = gen_reg_rtx (HImode); | |
1946 | emit_move_insn (temp, const0_rtx); | |
1947 | } | |
1948 | else | |
1949 | temp = const0_rtx; | |
5ab9749e NC |
1950 | |
1951 | call = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, call, | |
4b58290f GK |
1952 | gen_rtx_USE (VOIDmode, temp))); |
1953 | emit_call_insn (call); | |
1954 | } | |
1955 | \f | |
1956 | /* Expanders for multiword computational operations. */ | |
1957 | ||
1958 | /* Expander for arithmetic operations; emit insns to compute | |
1959 | ||
1960 | (set DEST (CODE:MODE SRC0 SRC1)) | |
5ab9749e | 1961 | |
d40ba0b6 NC |
1962 | When CODE is COMPARE, a branch template is generated |
1963 | (this saves duplicating code in xstormy16_split_cbranch). */ | |
4b58290f | 1964 | |
5ab9749e | 1965 | void |
ef4bddc2 | 1966 | xstormy16_expand_arith (machine_mode mode, enum rtx_code code, |
d40ba0b6 | 1967 | rtx dest, rtx src0, rtx src1) |
4b58290f GK |
1968 | { |
1969 | int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD; | |
1970 | int i; | |
1971 | int firstloop = 1; | |
1972 | ||
1973 | if (code == NEG) | |
9be13211 | 1974 | emit_move_insn (src0, const0_rtx); |
5ab9749e | 1975 | |
4b58290f GK |
1976 | for (i = 0; i < num_words; i++) |
1977 | { | |
1978 | rtx w_src0, w_src1, w_dest; | |
1979 | rtx insn; | |
5ab9749e NC |
1980 | |
1981 | w_src0 = simplify_gen_subreg (word_mode, src0, mode, | |
9be13211 | 1982 | i * UNITS_PER_WORD); |
4b58290f GK |
1983 | w_src1 = simplify_gen_subreg (word_mode, src1, mode, i * UNITS_PER_WORD); |
1984 | w_dest = simplify_gen_subreg (word_mode, dest, mode, i * UNITS_PER_WORD); | |
1985 | ||
1986 | switch (code) | |
1987 | { | |
1988 | case PLUS: | |
1989 | if (firstloop | |
a21eaf5e NC |
1990 | && CONST_INT_P (w_src1) |
1991 | && INTVAL (w_src1) == 0) | |
4b58290f | 1992 | continue; |
5ab9749e | 1993 | |
4b58290f | 1994 | if (firstloop) |
d40ba0b6 | 1995 | insn = gen_addchi4 (w_dest, w_src0, w_src1); |
4b58290f | 1996 | else |
d40ba0b6 | 1997 | insn = gen_addchi5 (w_dest, w_src0, w_src1); |
4b58290f GK |
1998 | break; |
1999 | ||
2000 | case NEG: | |
2001 | case MINUS: | |
2002 | case COMPARE: | |
2003 | if (code == COMPARE && i == num_words - 1) | |
2004 | { | |
2005 | rtx branch, sub, clobber, sub_1; | |
5ab9749e NC |
2006 | |
2007 | sub_1 = gen_rtx_MINUS (HImode, w_src0, | |
b72bbbcb | 2008 | gen_rtx_ZERO_EXTEND (HImode, gen_rtx_REG (BImode, CARRY_REGNUM))); |
f7df4a84 | 2009 | sub = gen_rtx_SET (w_dest, |
4b58290f | 2010 | gen_rtx_MINUS (HImode, sub_1, w_src1)); |
b72bbbcb | 2011 | clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (BImode, CARRY_REGNUM)); |
f7df4a84 | 2012 | branch = gen_rtx_SET (pc_rtx, |
4b58290f GK |
2013 | gen_rtx_IF_THEN_ELSE (VOIDmode, |
2014 | gen_rtx_EQ (HImode, | |
2015 | sub_1, | |
2016 | w_src1), | |
2017 | pc_rtx, | |
2018 | pc_rtx)); | |
2019 | insn = gen_rtx_PARALLEL (VOIDmode, | |
2020 | gen_rtvec (3, branch, sub, clobber)); | |
2021 | } | |
2022 | else if (firstloop | |
2023 | && code != COMPARE | |
a21eaf5e NC |
2024 | && CONST_INT_P (w_src1) |
2025 | && INTVAL (w_src1) == 0) | |
4b58290f GK |
2026 | continue; |
2027 | else if (firstloop) | |
d40ba0b6 | 2028 | insn = gen_subchi4 (w_dest, w_src0, w_src1); |
4b58290f | 2029 | else |
d40ba0b6 | 2030 | insn = gen_subchi5 (w_dest, w_src0, w_src1); |
4b58290f GK |
2031 | break; |
2032 | ||
2033 | case IOR: | |
2034 | case XOR: | |
2035 | case AND: | |
a21eaf5e | 2036 | if (CONST_INT_P (w_src1) |
4b58290f GK |
2037 | && INTVAL (w_src1) == -(code == AND)) |
2038 | continue; | |
5ab9749e | 2039 | |
f7df4a84 RS |
2040 | insn = gen_rtx_SET (w_dest, gen_rtx_fmt_ee (code, mode, |
2041 | w_src0, w_src1)); | |
4b58290f GK |
2042 | break; |
2043 | ||
2044 | case NOT: | |
f7df4a84 | 2045 | insn = gen_rtx_SET (w_dest, gen_rtx_NOT (mode, w_src0)); |
4b58290f GK |
2046 | break; |
2047 | ||
2048 | default: | |
4718bfd8 | 2049 | gcc_unreachable (); |
4b58290f | 2050 | } |
5ab9749e | 2051 | |
4b58290f GK |
2052 | firstloop = 0; |
2053 | emit (insn); | |
2054 | } | |
f3cd0185 DD |
2055 | |
2056 | /* If we emit nothing, try_split() will think we failed. So emit | |
2057 | something that does nothing and can be optimized away. */ | |
2058 | if (firstloop) | |
2059 | emit (gen_nop ()); | |
4b58290f GK |
2060 | } |
2061 | ||
4b58290f | 2062 | /* The shift operations are split at output time for constant values; |
5ab9749e | 2063 | variable-width shifts get handed off to a library routine. |
4b58290f GK |
2064 | |
2065 | Generate an output string to do (set X (CODE:MODE X SIZE_R)) | |
2066 | SIZE_R will be a CONST_INT, X will be a hard register. */ | |
2067 | ||
5ab9749e | 2068 | const char * |
ef4bddc2 | 2069 | xstormy16_output_shift (machine_mode mode, enum rtx_code code, |
51c16b7e | 2070 | rtx x, rtx size_r, rtx temp) |
4b58290f GK |
2071 | { |
2072 | HOST_WIDE_INT size; | |
2073 | const char *r0, *r1, *rt; | |
2074 | static char r[64]; | |
2075 | ||
a21eaf5e NC |
2076 | gcc_assert (CONST_INT_P (size_r) |
2077 | && REG_P (x) | |
2078 | && mode == SImode); | |
2079 | ||
4b58290f GK |
2080 | size = INTVAL (size_r) & (GET_MODE_BITSIZE (mode) - 1); |
2081 | ||
2082 | if (size == 0) | |
2083 | return ""; | |
2084 | ||
2085 | r0 = reg_names [REGNO (x)]; | |
2086 | r1 = reg_names [REGNO (x) + 1]; | |
4b58290f GK |
2087 | |
2088 | /* For shifts of size 1, we can use the rotate instructions. */ | |
2089 | if (size == 1) | |
2090 | { | |
2091 | switch (code) | |
2092 | { | |
2093 | case ASHIFT: | |
2094 | sprintf (r, "shl %s,#1 | rlc %s,#1", r0, r1); | |
2095 | break; | |
2096 | case ASHIFTRT: | |
2097 | sprintf (r, "asr %s,#1 | rrc %s,#1", r1, r0); | |
2098 | break; | |
2099 | case LSHIFTRT: | |
2100 | sprintf (r, "shr %s,#1 | rrc %s,#1", r1, r0); | |
2101 | break; | |
2102 | default: | |
4718bfd8 | 2103 | gcc_unreachable (); |
4b58290f GK |
2104 | } |
2105 | return r; | |
2106 | } | |
5ab9749e | 2107 | |
4b58290f GK |
2108 | /* For large shifts, there are easy special cases. */ |
2109 | if (size == 16) | |
2110 | { | |
2111 | switch (code) | |
2112 | { | |
2113 | case ASHIFT: | |
2114 | sprintf (r, "mov %s,%s | mov %s,#0", r1, r0, r0); | |
2115 | break; | |
2116 | case ASHIFTRT: | |
2117 | sprintf (r, "mov %s,%s | asr %s,#15", r0, r1, r1); | |
2118 | break; | |
2119 | case LSHIFTRT: | |
2120 | sprintf (r, "mov %s,%s | mov %s,#0", r0, r1, r1); | |
2121 | break; | |
2122 | default: | |
4718bfd8 | 2123 | gcc_unreachable (); |
4b58290f GK |
2124 | } |
2125 | return r; | |
2126 | } | |
2127 | if (size > 16) | |
2128 | { | |
2129 | switch (code) | |
2130 | { | |
2131 | case ASHIFT: | |
5ab9749e | 2132 | sprintf (r, "mov %s,%s | mov %s,#0 | shl %s,#%d", |
4b58290f GK |
2133 | r1, r0, r0, r1, (int) size - 16); |
2134 | break; | |
2135 | case ASHIFTRT: | |
5ab9749e | 2136 | sprintf (r, "mov %s,%s | asr %s,#15 | asr %s,#%d", |
4b58290f GK |
2137 | r0, r1, r1, r0, (int) size - 16); |
2138 | break; | |
2139 | case LSHIFTRT: | |
5ab9749e | 2140 | sprintf (r, "mov %s,%s | mov %s,#0 | shr %s,#%d", |
4b58290f GK |
2141 | r0, r1, r1, r0, (int) size - 16); |
2142 | break; | |
2143 | default: | |
4718bfd8 | 2144 | gcc_unreachable (); |
4b58290f GK |
2145 | } |
2146 | return r; | |
2147 | } | |
2148 | ||
2149 | /* For the rest, we have to do more work. In particular, we | |
2150 | need a temporary. */ | |
5766e0ef | 2151 | rt = reg_names [REGNO (temp)]; |
4b58290f GK |
2152 | switch (code) |
2153 | { | |
2154 | case ASHIFT: | |
5ab9749e NC |
2155 | sprintf (r, |
2156 | "mov %s,%s | shl %s,#%d | shl %s,#%d | shr %s,#%d | or %s,%s", | |
2157 | rt, r0, r0, (int) size, r1, (int) size, rt, (int) (16 - size), | |
4b58290f GK |
2158 | r1, rt); |
2159 | break; | |
2160 | case ASHIFTRT: | |
5ab9749e NC |
2161 | sprintf (r, |
2162 | "mov %s,%s | asr %s,#%d | shr %s,#%d | shl %s,#%d | or %s,%s", | |
2163 | rt, r1, r1, (int) size, r0, (int) size, rt, (int) (16 - size), | |
4b58290f GK |
2164 | r0, rt); |
2165 | break; | |
2166 | case LSHIFTRT: | |
5ab9749e NC |
2167 | sprintf (r, |
2168 | "mov %s,%s | shr %s,#%d | shr %s,#%d | shl %s,#%d | or %s,%s", | |
2169 | rt, r1, r1, (int) size, r0, (int) size, rt, (int) (16 - size), | |
4b58290f GK |
2170 | r0, rt); |
2171 | break; | |
2172 | default: | |
4718bfd8 | 2173 | gcc_unreachable (); |
4b58290f GK |
2174 | } |
2175 | return r; | |
2176 | } | |
2177 | \f | |
2178 | /* Attribute handling. */ | |
2179 | ||
2180 | /* Return nonzero if the function is an interrupt function. */ | |
5ab9749e | 2181 | |
4b58290f | 2182 | int |
51c16b7e | 2183 | xstormy16_interrupt_function_p (void) |
4b58290f GK |
2184 | { |
2185 | tree attributes; | |
5ab9749e | 2186 | |
4b58290f GK |
2187 | /* The dwarf2 mechanism asks for INCOMING_FRAME_SP_OFFSET before |
2188 | any functions are declared, which is demonstrably wrong, but | |
2189 | it is worked around here. FIXME. */ | |
2190 | if (!cfun) | |
2191 | return 0; | |
2192 | ||
2193 | attributes = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl)); | |
2194 | return lookup_attribute ("interrupt", attributes) != NULL_TREE; | |
2195 | } | |
2196 | ||
5ab9749e NC |
2197 | #undef TARGET_ATTRIBUTE_TABLE |
2198 | #define TARGET_ATTRIBUTE_TABLE xstormy16_attribute_table | |
2199 | ||
51c16b7e SB |
2200 | static tree xstormy16_handle_interrupt_attribute |
2201 | (tree *, tree, tree, int, bool *); | |
54e9a19d DD |
2202 | static tree xstormy16_handle_below100_attribute |
2203 | (tree *, tree, tree, int, bool *); | |
51c16b7e | 2204 | |
c6243b4c | 2205 | static const struct attribute_spec xstormy16_attribute_table[] = |
91d231cb | 2206 | { |
4849deb1 JJ |
2207 | /* name, min_len, max_len, decl_req, type_req, fn_type_req, |
2208 | affects_type_identity, handler, exclude. */ | |
2209 | { "interrupt", 0, 0, false, true, true, false, | |
2210 | xstormy16_handle_interrupt_attribute, NULL }, | |
2211 | { "BELOW100", 0, 0, false, false, false, false, | |
2212 | xstormy16_handle_below100_attribute, NULL }, | |
2213 | { "below100", 0, 0, false, false, false, false, | |
2214 | xstormy16_handle_below100_attribute, NULL }, | |
2215 | { NULL, 0, 0, false, false, false, false, NULL, NULL } | |
91d231cb JM |
2216 | }; |
2217 | ||
2218 | /* Handle an "interrupt" attribute; | |
2219 | arguments as in struct attribute_spec.handler. */ | |
5ab9749e | 2220 | |
91d231cb | 2221 | static tree |
51c16b7e SB |
2222 | xstormy16_handle_interrupt_attribute (tree *node, tree name, |
2223 | tree args ATTRIBUTE_UNUSED, | |
2224 | int flags ATTRIBUTE_UNUSED, | |
2225 | bool *no_add_attrs) | |
4b58290f | 2226 | { |
91d231cb JM |
2227 | if (TREE_CODE (*node) != FUNCTION_TYPE) |
2228 | { | |
29d08eba JM |
2229 | warning (OPT_Wattributes, "%qE attribute only applies to functions", |
2230 | name); | |
91d231cb JM |
2231 | *no_add_attrs = true; |
2232 | } | |
4b58290f | 2233 | |
91d231cb | 2234 | return NULL_TREE; |
4b58290f | 2235 | } |
54e9a19d DD |
2236 | |
2237 | /* Handle an "below" attribute; | |
2238 | arguments as in struct attribute_spec.handler. */ | |
5ab9749e | 2239 | |
54e9a19d DD |
2240 | static tree |
2241 | xstormy16_handle_below100_attribute (tree *node, | |
2242 | tree name ATTRIBUTE_UNUSED, | |
2243 | tree args ATTRIBUTE_UNUSED, | |
2244 | int flags ATTRIBUTE_UNUSED, | |
2245 | bool *no_add_attrs) | |
2246 | { | |
2247 | if (TREE_CODE (*node) != VAR_DECL | |
2248 | && TREE_CODE (*node) != POINTER_TYPE | |
2249 | && TREE_CODE (*node) != TYPE_DECL) | |
2250 | { | |
5c498b10 DD |
2251 | warning (OPT_Wattributes, |
2252 | "%<__BELOW100__%> attribute only applies to variables"); | |
54e9a19d DD |
2253 | *no_add_attrs = true; |
2254 | } | |
2255 | else if (args == NULL_TREE && TREE_CODE (*node) == VAR_DECL) | |
2256 | { | |
2257 | if (! (TREE_PUBLIC (*node) || TREE_STATIC (*node))) | |
2258 | { | |
5c498b10 | 2259 | warning (OPT_Wattributes, "__BELOW100__ attribute not allowed " |
ab532386 | 2260 | "with auto storage class"); |
54e9a19d DD |
2261 | *no_add_attrs = true; |
2262 | } | |
2263 | } | |
5ab9749e | 2264 | |
54e9a19d DD |
2265 | return NULL_TREE; |
2266 | } | |
3d4b192a | 2267 | \f |
5ab9749e NC |
2268 | #undef TARGET_INIT_BUILTINS |
2269 | #define TARGET_INIT_BUILTINS xstormy16_init_builtins | |
2270 | #undef TARGET_EXPAND_BUILTIN | |
2271 | #define TARGET_EXPAND_BUILTIN xstormy16_expand_builtin | |
2272 | ||
2273 | static struct | |
2274 | { | |
2275 | const char * name; | |
2276 | int md_code; | |
2277 | const char * arg_ops; /* 0..9, t for temp register, r for return value. */ | |
2278 | const char * arg_types; /* s=short,l=long, upper case for unsigned. */ | |
2279 | } | |
2280 | s16builtins[] = | |
2281 | { | |
3d4b192a DD |
2282 | { "__sdivlh", CODE_FOR_sdivlh, "rt01", "sls" }, |
2283 | { "__smodlh", CODE_FOR_sdivlh, "tr01", "sls" }, | |
2284 | { "__udivlh", CODE_FOR_udivlh, "rt01", "SLS" }, | |
2285 | { "__umodlh", CODE_FOR_udivlh, "tr01", "SLS" }, | |
5ab9749e | 2286 | { NULL, 0, NULL, NULL } |
3d4b192a DD |
2287 | }; |
2288 | ||
2289 | static void | |
51c16b7e | 2290 | xstormy16_init_builtins (void) |
3d4b192a | 2291 | { |
2c67cf6e NF |
2292 | tree args[2], ret_type, arg = NULL_TREE, ftype; |
2293 | int i, a, n_args; | |
3d4b192a DD |
2294 | |
2295 | ret_type = void_type_node; | |
2296 | ||
5ab9749e | 2297 | for (i = 0; s16builtins[i].name; i++) |
3d4b192a | 2298 | { |
2c67cf6e NF |
2299 | n_args = strlen (s16builtins[i].arg_types) - 1; |
2300 | ||
2301 | gcc_assert (n_args <= (int) ARRAY_SIZE (args)); | |
2302 | ||
15f072f9 | 2303 | for (a = n_args - 1; a >= 0; a--) |
2c67cf6e NF |
2304 | args[a] = NULL_TREE; |
2305 | ||
2306 | for (a = n_args; a >= 0; a--) | |
3d4b192a DD |
2307 | { |
2308 | switch (s16builtins[i].arg_types[a]) | |
2309 | { | |
2310 | case 's': arg = short_integer_type_node; break; | |
2311 | case 'S': arg = short_unsigned_type_node; break; | |
2312 | case 'l': arg = long_integer_type_node; break; | |
2313 | case 'L': arg = long_unsigned_type_node; break; | |
4718bfd8 | 2314 | default: gcc_unreachable (); |
3d4b192a DD |
2315 | } |
2316 | if (a == 0) | |
2317 | ret_type = arg; | |
2318 | else | |
2c67cf6e | 2319 | args[a-1] = arg; |
3d4b192a | 2320 | } |
15f072f9 | 2321 | ftype = build_function_type_list (ret_type, args[0], args[1], NULL_TREE); |
2c67cf6e | 2322 | add_builtin_function (s16builtins[i].name, ftype, |
15f072f9 | 2323 | i, BUILT_IN_MD, NULL, NULL_TREE); |
3d4b192a DD |
2324 | } |
2325 | } | |
2326 | ||
2327 | static rtx | |
f84fe9b6 NC |
2328 | xstormy16_expand_builtin (tree exp, rtx target, |
2329 | rtx subtarget ATTRIBUTE_UNUSED, | |
ef4bddc2 | 2330 | machine_mode mode ATTRIBUTE_UNUSED, |
f84fe9b6 | 2331 | int ignore ATTRIBUTE_UNUSED) |
3d4b192a DD |
2332 | { |
2333 | rtx op[10], args[10], pat, copyto[10], retval = 0; | |
2334 | tree fndecl, argtree; | |
2335 | int i, a, o, code; | |
2336 | ||
2337 | fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); | |
2338 | argtree = TREE_OPERAND (exp, 1); | |
4d732405 | 2339 | i = DECL_MD_FUNCTION_CODE (fndecl); |
3d4b192a DD |
2340 | code = s16builtins[i].md_code; |
2341 | ||
2342 | for (a = 0; a < 10 && argtree; a++) | |
2343 | { | |
f1cb6795 | 2344 | args[a] = expand_normal (TREE_VALUE (argtree)); |
3d4b192a DD |
2345 | argtree = TREE_CHAIN (argtree); |
2346 | } | |
2347 | ||
2348 | for (o = 0; s16builtins[i].arg_ops[o]; o++) | |
2349 | { | |
2350 | char ao = s16builtins[i].arg_ops[o]; | |
2351 | char c = insn_data[code].operand[o].constraint[0]; | |
ef4bddc2 | 2352 | machine_mode omode; |
3d4b192a DD |
2353 | |
2354 | copyto[o] = 0; | |
2355 | ||
ef4bddc2 | 2356 | omode = (machine_mode) insn_data[code].operand[o].mode; |
3d4b192a DD |
2357 | if (ao == 'r') |
2358 | op[o] = target ? target : gen_reg_rtx (omode); | |
2359 | else if (ao == 't') | |
2360 | op[o] = gen_reg_rtx (omode); | |
2361 | else | |
2362 | op[o] = args[(int) hex_value (ao)]; | |
2363 | ||
2364 | if (! (*insn_data[code].operand[o].predicate) (op[o], GET_MODE (op[o]))) | |
2365 | { | |
2366 | if (c == '+' || c == '=') | |
2367 | { | |
2368 | copyto[o] = op[o]; | |
2369 | op[o] = gen_reg_rtx (omode); | |
2370 | } | |
2371 | else | |
2372 | op[o] = copy_to_mode_reg (omode, op[o]); | |
2373 | } | |
2374 | ||
2375 | if (ao == 'r') | |
2376 | retval = op[o]; | |
2377 | } | |
2378 | ||
2379 | pat = GEN_FCN (code) (op[0], op[1], op[2], op[3], op[4], | |
2380 | op[5], op[6], op[7], op[8], op[9]); | |
2381 | emit_insn (pat); | |
2382 | ||
2383 | for (o = 0; s16builtins[i].arg_ops[o]; o++) | |
2384 | if (copyto[o]) | |
2385 | { | |
2386 | emit_move_insn (copyto[o], op[o]); | |
2387 | if (op[o] == retval) | |
2388 | retval = copyto[o]; | |
2389 | } | |
2390 | ||
2391 | return retval; | |
2392 | } | |
54e9a19d | 2393 | \f |
54e9a19d DD |
2394 | /* Look for combinations of insns that can be converted to BN or BP |
2395 | opcodes. This is, unfortunately, too complex to do with MD | |
2396 | patterns. */ | |
5ab9749e | 2397 | |
54e9a19d | 2398 | static void |
b32d5189 | 2399 | combine_bnp (rtx_insn *insn) |
54e9a19d | 2400 | { |
f99652b5 NC |
2401 | int insn_code, regno, need_extend; |
2402 | unsigned int mask; | |
647d790d DM |
2403 | rtx cond, reg, qireg, mem; |
2404 | rtx_insn *and_insn, *load; | |
ef4bddc2 RS |
2405 | machine_mode load_mode = QImode; |
2406 | machine_mode and_mode = QImode; | |
647d790d | 2407 | rtx_insn *shift = NULL; |
54e9a19d DD |
2408 | |
2409 | insn_code = recog_memoized (insn); | |
2410 | if (insn_code != CODE_FOR_cbranchhi | |
2411 | && insn_code != CODE_FOR_cbranchhi_neg) | |
2412 | return; | |
2413 | ||
2414 | cond = XVECEXP (PATTERN (insn), 0, 0); /* set */ | |
2415 | cond = XEXP (cond, 1); /* if */ | |
2416 | cond = XEXP (cond, 0); /* cond */ | |
2417 | switch (GET_CODE (cond)) | |
2418 | { | |
2419 | case NE: | |
2420 | case EQ: | |
2421 | need_extend = 0; | |
2422 | break; | |
2423 | case LT: | |
2424 | case GE: | |
2425 | need_extend = 1; | |
2426 | break; | |
2427 | default: | |
2428 | return; | |
2429 | } | |
2430 | ||
2431 | reg = XEXP (cond, 0); | |
a21eaf5e | 2432 | if (! REG_P (reg)) |
54e9a19d DD |
2433 | return; |
2434 | regno = REGNO (reg); | |
2435 | if (XEXP (cond, 1) != const0_rtx) | |
2436 | return; | |
2437 | if (! find_regno_note (insn, REG_DEAD, regno)) | |
2438 | return; | |
2439 | qireg = gen_rtx_REG (QImode, regno); | |
2440 | ||
2441 | if (need_extend) | |
2442 | { | |
569b7f6a | 2443 | /* LT and GE conditionals should have a sign extend before |
54e9a19d | 2444 | them. */ |
15f072f9 NC |
2445 | for (and_insn = prev_real_insn (insn); |
2446 | and_insn != NULL_RTX; | |
f1cb6795 | 2447 | and_insn = prev_real_insn (and_insn)) |
54e9a19d | 2448 | { |
f1cb6795 | 2449 | int and_code = recog_memoized (and_insn); |
f99652b5 | 2450 | |
54e9a19d | 2451 | if (and_code == CODE_FOR_extendqihi2 |
f1cb6795 JR |
2452 | && rtx_equal_p (SET_DEST (PATTERN (and_insn)), reg) |
2453 | && rtx_equal_p (XEXP (SET_SRC (PATTERN (and_insn)), 0), qireg)) | |
f99652b5 | 2454 | break; |
5ab9749e | 2455 | |
54e9a19d | 2456 | if (and_code == CODE_FOR_movhi_internal |
f1cb6795 | 2457 | && rtx_equal_p (SET_DEST (PATTERN (and_insn)), reg)) |
54e9a19d DD |
2458 | { |
2459 | /* This is for testing bit 15. */ | |
f1cb6795 | 2460 | and_insn = insn; |
54e9a19d DD |
2461 | break; |
2462 | } | |
2463 | ||
f1cb6795 | 2464 | if (reg_mentioned_p (reg, and_insn)) |
54e9a19d | 2465 | return; |
f99652b5 | 2466 | |
b64925dc | 2467 | if (! NOTE_P (and_insn) && ! NONJUMP_INSN_P (and_insn)) |
54e9a19d DD |
2468 | return; |
2469 | } | |
2470 | } | |
2471 | else | |
2472 | { | |
2473 | /* EQ and NE conditionals have an AND before them. */ | |
15f072f9 NC |
2474 | for (and_insn = prev_real_insn (insn); |
2475 | and_insn != NULL_RTX; | |
f1cb6795 | 2476 | and_insn = prev_real_insn (and_insn)) |
54e9a19d | 2477 | { |
f1cb6795 JR |
2478 | if (recog_memoized (and_insn) == CODE_FOR_andhi3 |
2479 | && rtx_equal_p (SET_DEST (PATTERN (and_insn)), reg) | |
2480 | && rtx_equal_p (XEXP (SET_SRC (PATTERN (and_insn)), 0), reg)) | |
f99652b5 | 2481 | break; |
5ab9749e | 2482 | |
f1cb6795 | 2483 | if (reg_mentioned_p (reg, and_insn)) |
54e9a19d | 2484 | return; |
f99652b5 | 2485 | |
b64925dc | 2486 | if (! NOTE_P (and_insn) && ! NONJUMP_INSN_P (and_insn)) |
54e9a19d DD |
2487 | return; |
2488 | } | |
f99652b5 | 2489 | |
f1cb6795 | 2490 | if (and_insn) |
f99652b5 | 2491 | { |
aabcd309 | 2492 | /* Some mis-optimizations by GCC can generate a RIGHT-SHIFT |
f99652b5 NC |
2493 | followed by an AND like this: |
2494 | ||
2495 | (parallel [(set (reg:HI r7) (lshiftrt:HI (reg:HI r7) (const_int 3))) | |
2496 | (clobber (reg:BI carry))] | |
2497 | ||
2498 | (set (reg:HI r7) (and:HI (reg:HI r7) (const_int 1))) | |
5ab9749e | 2499 | |
f99652b5 | 2500 | Attempt to detect this here. */ |
f1cb6795 JR |
2501 | for (shift = prev_real_insn (and_insn); shift; |
2502 | shift = prev_real_insn (shift)) | |
f99652b5 NC |
2503 | { |
2504 | if (recog_memoized (shift) == CODE_FOR_lshrhi3 | |
2505 | && rtx_equal_p (SET_DEST (XVECEXP (PATTERN (shift), 0, 0)), reg) | |
2506 | && rtx_equal_p (XEXP (SET_SRC (XVECEXP (PATTERN (shift), 0, 0)), 0), reg)) | |
2507 | break; | |
5ab9749e | 2508 | |
f99652b5 | 2509 | if (reg_mentioned_p (reg, shift) |
b64925dc | 2510 | || (! NOTE_P (shift) && ! NONJUMP_INSN_P (shift))) |
f99652b5 | 2511 | { |
647d790d | 2512 | shift = NULL; |
f99652b5 NC |
2513 | break; |
2514 | } | |
2515 | } | |
2516 | } | |
54e9a19d | 2517 | } |
15f072f9 NC |
2518 | |
2519 | if (and_insn == NULL_RTX) | |
54e9a19d DD |
2520 | return; |
2521 | ||
f1cb6795 | 2522 | for (load = shift ? prev_real_insn (shift) : prev_real_insn (and_insn); |
f99652b5 NC |
2523 | load; |
2524 | load = prev_real_insn (load)) | |
54e9a19d DD |
2525 | { |
2526 | int load_code = recog_memoized (load); | |
f99652b5 | 2527 | |
54e9a19d | 2528 | if (load_code == CODE_FOR_movhi_internal |
f99652b5 NC |
2529 | && rtx_equal_p (SET_DEST (PATTERN (load)), reg) |
2530 | && xstormy16_below100_operand (SET_SRC (PATTERN (load)), HImode) | |
2531 | && ! MEM_VOLATILE_P (SET_SRC (PATTERN (load)))) | |
54e9a19d DD |
2532 | { |
2533 | load_mode = HImode; | |
2534 | break; | |
2535 | } | |
2536 | ||
2537 | if (load_code == CODE_FOR_movqi_internal | |
f99652b5 NC |
2538 | && rtx_equal_p (SET_DEST (PATTERN (load)), qireg) |
2539 | && xstormy16_below100_operand (SET_SRC (PATTERN (load)), QImode)) | |
54e9a19d DD |
2540 | { |
2541 | load_mode = QImode; | |
2542 | break; | |
2543 | } | |
f99652b5 NC |
2544 | |
2545 | if (load_code == CODE_FOR_zero_extendqihi2 | |
2546 | && rtx_equal_p (SET_DEST (PATTERN (load)), reg) | |
2547 | && xstormy16_below100_operand (XEXP (SET_SRC (PATTERN (load)), 0), QImode)) | |
2548 | { | |
2549 | load_mode = QImode; | |
2550 | and_mode = HImode; | |
2551 | break; | |
2552 | } | |
2553 | ||
54e9a19d DD |
2554 | if (reg_mentioned_p (reg, load)) |
2555 | return; | |
f99652b5 | 2556 | |
b64925dc | 2557 | if (! NOTE_P (load) && ! NONJUMP_INSN_P (load)) |
54e9a19d DD |
2558 | return; |
2559 | } | |
2560 | if (!load) | |
2561 | return; | |
2562 | ||
f99652b5 NC |
2563 | mem = SET_SRC (PATTERN (load)); |
2564 | ||
2565 | if (need_extend) | |
54e9a19d | 2566 | { |
f99652b5 NC |
2567 | mask = (load_mode == HImode) ? 0x8000 : 0x80; |
2568 | ||
2569 | /* If the mem includes a zero-extend operation and we are | |
2570 | going to generate a sign-extend operation then move the | |
2571 | mem inside the zero-extend. */ | |
2572 | if (GET_CODE (mem) == ZERO_EXTEND) | |
2573 | mem = XEXP (mem, 0); | |
54e9a19d DD |
2574 | } |
2575 | else | |
f99652b5 | 2576 | { |
f1cb6795 JR |
2577 | if (!xstormy16_onebit_set_operand (XEXP (SET_SRC (PATTERN (and_insn)), 1), |
2578 | load_mode)) | |
f99652b5 NC |
2579 | return; |
2580 | ||
f1cb6795 | 2581 | mask = (int) INTVAL (XEXP (SET_SRC (PATTERN (and_insn)), 1)); |
f99652b5 NC |
2582 | |
2583 | if (shift) | |
2584 | mask <<= INTVAL (XEXP (SET_SRC (XVECEXP (PATTERN (shift), 0, 0)), 1)); | |
2585 | } | |
54e9a19d | 2586 | |
54e9a19d DD |
2587 | if (load_mode == HImode) |
2588 | { | |
2589 | rtx addr = XEXP (mem, 0); | |
f99652b5 | 2590 | |
54e9a19d DD |
2591 | if (! (mask & 0xff)) |
2592 | { | |
0a81f074 | 2593 | addr = plus_constant (Pmode, addr, 1); |
54e9a19d DD |
2594 | mask >>= 8; |
2595 | } | |
2596 | mem = gen_rtx_MEM (QImode, addr); | |
2597 | } | |
2598 | ||
2599 | if (need_extend) | |
2600 | XEXP (cond, 0) = gen_rtx_SIGN_EXTEND (HImode, mem); | |
2601 | else | |
f99652b5 NC |
2602 | XEXP (cond, 0) = gen_rtx_AND (and_mode, mem, GEN_INT (mask)); |
2603 | ||
54e9a19d DD |
2604 | INSN_CODE (insn) = -1; |
2605 | delete_insn (load); | |
f99652b5 | 2606 | |
f1cb6795 JR |
2607 | if (and_insn != insn) |
2608 | delete_insn (and_insn); | |
f99652b5 NC |
2609 | |
2610 | if (shift != NULL_RTX) | |
2611 | delete_insn (shift); | |
54e9a19d DD |
2612 | } |
2613 | ||
2614 | static void | |
2615 | xstormy16_reorg (void) | |
2616 | { | |
b32d5189 | 2617 | rtx_insn *insn; |
54e9a19d DD |
2618 | |
2619 | for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) | |
2620 | { | |
2621 | if (! JUMP_P (insn)) | |
2622 | continue; | |
2623 | combine_bnp (insn); | |
2624 | } | |
2625 | } | |
7e43c821 | 2626 | \f |
78bc94a2 KH |
2627 | /* Worker function for TARGET_RETURN_IN_MEMORY. */ |
2628 | ||
7e43c821 | 2629 | static bool |
586de218 | 2630 | xstormy16_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) |
7e43c821 | 2631 | { |
586de218 | 2632 | const HOST_WIDE_INT size = int_size_in_bytes (type); |
78bc94a2 | 2633 | return (size == -1 || size > UNITS_PER_WORD * NUM_ARGUMENT_REGISTERS); |
7e43c821 | 2634 | } |
f939c3e6 RS |
2635 | |
2636 | /* Implement TARGET_HARD_REGNO_MODE_OK. */ | |
2637 | ||
2638 | static bool | |
2639 | xstormy16_hard_regno_mode_ok (unsigned int regno, machine_mode mode) | |
2640 | { | |
2641 | return regno != 16 || mode == BImode; | |
2642 | } | |
99e1629f RS |
2643 | |
2644 | /* Implement TARGET_MODES_TIEABLE_P. */ | |
2645 | ||
2646 | static bool | |
2647 | xstormy16_modes_tieable_p (machine_mode mode1, machine_mode mode2) | |
2648 | { | |
2649 | return mode1 != BImode && mode2 != BImode; | |
2650 | } | |
7b4df2bf RS |
2651 | |
2652 | /* Implement PUSH_ROUNDING. */ | |
2653 | ||
2654 | poly_int64 | |
2655 | xstormy16_push_rounding (poly_int64 bytes) | |
2656 | { | |
2657 | return (bytes + 1) & ~1; | |
2658 | } | |
4b58290f | 2659 | \f |
5ab9749e | 2660 | #undef TARGET_ASM_ALIGNED_HI_OP |
301d03af | 2661 | #define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t" |
5ab9749e | 2662 | #undef TARGET_ASM_ALIGNED_SI_OP |
301d03af | 2663 | #define TARGET_ASM_ALIGNED_SI_OP "\t.word\t" |
5ab9749e | 2664 | #undef TARGET_ENCODE_SECTION_INFO |
54e9a19d | 2665 | #define TARGET_ENCODE_SECTION_INFO xstormy16_encode_section_info |
301d03af | 2666 | |
5ab9749e | 2667 | /* Select_section doesn't handle .bss_below100. */ |
434aeebb RS |
2668 | #undef TARGET_HAVE_SWITCHABLE_BSS_SECTIONS |
2669 | #define TARGET_HAVE_SWITCHABLE_BSS_SECTIONS false | |
2670 | ||
5ab9749e | 2671 | #undef TARGET_ASM_OUTPUT_MI_THUNK |
c590b625 | 2672 | #define TARGET_ASM_OUTPUT_MI_THUNK xstormy16_asm_output_mi_thunk |
5ab9749e | 2673 | #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK |
3961e8fe | 2674 | #define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall |
c590b625 | 2675 | |
43070a6e AS |
2676 | #undef TARGET_PRINT_OPERAND |
2677 | #define TARGET_PRINT_OPERAND xstormy16_print_operand | |
2678 | #undef TARGET_PRINT_OPERAND_ADDRESS | |
2679 | #define TARGET_PRINT_OPERAND_ADDRESS xstormy16_print_operand_address | |
2680 | ||
6b1ce545 AS |
2681 | #undef TARGET_MEMORY_MOVE_COST |
2682 | #define TARGET_MEMORY_MOVE_COST xstormy16_memory_move_cost | |
5ab9749e | 2683 | #undef TARGET_RTX_COSTS |
3c50106f | 2684 | #define TARGET_RTX_COSTS xstormy16_rtx_costs |
5ab9749e | 2685 | #undef TARGET_ADDRESS_COST |
dcefdf67 | 2686 | #define TARGET_ADDRESS_COST xstormy16_address_cost |
3c50106f | 2687 | |
5ab9749e | 2688 | #undef TARGET_BUILD_BUILTIN_VA_LIST |
f2f61ee7 | 2689 | #define TARGET_BUILD_BUILTIN_VA_LIST xstormy16_build_builtin_va_list |
5ab9749e | 2690 | #undef TARGET_EXPAND_BUILTIN_VA_START |
d7bd8aeb | 2691 | #define TARGET_EXPAND_BUILTIN_VA_START xstormy16_expand_builtin_va_start |
5ab9749e | 2692 | #undef TARGET_GIMPLIFY_VA_ARG_EXPR |
f84fe9b6 | 2693 | #define TARGET_GIMPLIFY_VA_ARG_EXPR xstormy16_gimplify_va_arg_expr |
37cd4bca | 2694 | |
cde0f3fd PB |
2695 | #undef TARGET_PROMOTE_FUNCTION_MODE |
2696 | #define TARGET_PROMOTE_FUNCTION_MODE default_promote_function_mode_always_promote | |
5ab9749e | 2697 | #undef TARGET_PROMOTE_PROTOTYPES |
586de218 | 2698 | #define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true |
7e43c821 | 2699 | |
bf425ddd NF |
2700 | #undef TARGET_FUNCTION_ARG |
2701 | #define TARGET_FUNCTION_ARG xstormy16_function_arg | |
2702 | #undef TARGET_FUNCTION_ARG_ADVANCE | |
2703 | #define TARGET_FUNCTION_ARG_ADVANCE xstormy16_function_arg_advance | |
2704 | ||
5ab9749e | 2705 | #undef TARGET_RETURN_IN_MEMORY |
7e43c821 | 2706 | #define TARGET_RETURN_IN_MEMORY xstormy16_return_in_memory |
998871e9 AS |
2707 | #undef TARGET_FUNCTION_VALUE |
2708 | #define TARGET_FUNCTION_VALUE xstormy16_function_value | |
2709 | #undef TARGET_LIBCALL_VALUE | |
2710 | #define TARGET_LIBCALL_VALUE xstormy16_libcall_value | |
2711 | #undef TARGET_FUNCTION_VALUE_REGNO_P | |
2712 | #define TARGET_FUNCTION_VALUE_REGNO_P xstormy16_function_value_regno_p | |
7e43c821 | 2713 | |
5ab9749e | 2714 | #undef TARGET_MACHINE_DEPENDENT_REORG |
54e9a19d DD |
2715 | #define TARGET_MACHINE_DEPENDENT_REORG xstormy16_reorg |
2716 | ||
ef795fc2 AS |
2717 | #undef TARGET_PREFERRED_RELOAD_CLASS |
2718 | #define TARGET_PREFERRED_RELOAD_CLASS xstormy16_preferred_reload_class | |
2719 | #undef TARGET_PREFERRED_OUTPUT_RELOAD_CLASS | |
2720 | #define TARGET_PREFERRED_OUTPUT_RELOAD_CLASS xstormy16_preferred_reload_class | |
2721 | ||
d81db636 SB |
2722 | #undef TARGET_LRA_P |
2723 | #define TARGET_LRA_P hook_bool_void_false | |
2724 | ||
c6c3dba9 PB |
2725 | #undef TARGET_LEGITIMATE_ADDRESS_P |
2726 | #define TARGET_LEGITIMATE_ADDRESS_P xstormy16_legitimate_address_p | |
192997cf AS |
2727 | #undef TARGET_MODE_DEPENDENT_ADDRESS_P |
2728 | #define TARGET_MODE_DEPENDENT_ADDRESS_P xstormy16_mode_dependent_address_p | |
c6c3dba9 | 2729 | |
7b5cbb57 AS |
2730 | #undef TARGET_CAN_ELIMINATE |
2731 | #define TARGET_CAN_ELIMINATE xstormy16_can_eliminate | |
2732 | ||
fb8d0fac RH |
2733 | #undef TARGET_TRAMPOLINE_INIT |
2734 | #define TARGET_TRAMPOLINE_INIT xstormy16_trampoline_init | |
2735 | ||
f939c3e6 RS |
2736 | #undef TARGET_HARD_REGNO_MODE_OK |
2737 | #define TARGET_HARD_REGNO_MODE_OK xstormy16_hard_regno_mode_ok | |
99e1629f RS |
2738 | #undef TARGET_MODES_TIEABLE_P |
2739 | #define TARGET_MODES_TIEABLE_P xstormy16_modes_tieable_p | |
f939c3e6 | 2740 | |
58e17cf8 RS |
2741 | #undef TARGET_CONSTANT_ALIGNMENT |
2742 | #define TARGET_CONSTANT_ALIGNMENT constant_alignment_word_strings | |
2743 | ||
01557bd4 NC |
2744 | #undef TARGET_HAVE_SPECULATION_SAFE_VALUE |
2745 | #define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed | |
2746 | ||
4b58290f | 2747 | struct gcc_target targetm = TARGET_INITIALIZER; |
d6b5193b RS |
2748 | |
2749 | #include "gt-stormy16.h" |