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