1 /* Xstormy16 target functions.
2 Copyright (C) 1997-2023 Free Software Foundation, Inc.
3 Contributed by Red Hat, Inc.
5 This file is part of GCC.
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)
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.
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/>. */
21 #define IN_TARGET_CODE 1
25 #include "coretypes.h"
30 #include "stringpool.h"
36 #include "stringpool.h"
40 #include "diagnostic-core.h"
42 #include "fold-const.h"
43 #include "stor-layout.h"
48 #include "langhooks.h"
54 /* This file should be included last. */
55 #include "target-def.h"
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
,
63 static void xstormy16_init_builtins (void);
64 static rtx
xstormy16_expand_builtin (tree
, rtx
, rtx
, machine_mode
, int);
65 static int xstormy16_address_cost (rtx
, machine_mode
, addr_space_t
, bool);
66 static bool xstormy16_return_in_memory (const_tree
, const_tree
);
68 static GTY(()) section
*bss100_section
;
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. */
75 xstormy16_rtx_costs (rtx x
, machine_mode mode ATTRIBUTE_UNUSED
,
76 int outer_code ATTRIBUTE_UNUSED
,
77 int opno ATTRIBUTE_UNUSED
, int *total
,
78 bool speed ATTRIBUTE_UNUSED
)
80 int code
= GET_CODE (x
);
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);
90 *total
= COSTS_N_INSNS (2);
97 *total
= COSTS_N_INSNS (2);
101 *total
= COSTS_N_INSNS (35 + 6);
104 *total
= COSTS_N_INSNS (51 - 6);
113 xstormy16_address_cost (rtx x
, machine_mode mode ATTRIBUTE_UNUSED
,
114 addr_space_t as ATTRIBUTE_UNUSED
,
115 bool speed ATTRIBUTE_UNUSED
)
117 return (CONST_INT_P (x
) ? 2
118 : GET_CODE (x
) == PLUS
? 7
122 /* Worker function for TARGET_MEMORY_MOVE_COST. */
125 xstormy16_memory_move_cost (machine_mode mode
, reg_class_t rclass
,
128 return (5 + memory_move_secondary_cost (mode
, rclass
, in
));
131 /* Branches are handled as follows:
133 1. HImode compare-and-branches. The machine supports these
134 natively, so the appropriate pattern is emitted directly.
136 2. SImode EQ and NE. These are emitted as pairs of HImode
137 compare-and-branches.
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),
146 4. SImode GT, LE, GTU, LEU. These are emitted as a sequence like:
153 /* Emit a branch of kind CODE to location LOC. */
156 xstormy16_emit_cbranch (enum rtx_code code
, rtx op0
, rtx op1
, rtx loc
)
158 rtx condition_rtx
, loc_ref
, branch
, cy_clobber
;
162 mode
= GET_MODE (op0
);
163 gcc_assert (mode
== HImode
|| mode
== SImode
);
166 && (code
== GT
|| code
== LE
|| code
== GTU
|| code
== LEU
))
168 int unsigned_p
= (code
== GTU
|| code
== LEU
);
169 int gt_p
= (code
== GT
|| code
== GTU
);
173 lab
= gen_label_rtx ();
174 xstormy16_emit_cbranch (unsigned_p
? LTU
: LT
, op0
, op1
, gt_p
? lab
: loc
);
175 /* This should be generated as a comparison against the temporary
176 created by the previous insn, but reload can't handle that. */
177 xstormy16_emit_cbranch (gt_p
? NE
: EQ
, op0
, op1
, loc
);
182 else if (mode
== SImode
183 && (code
== NE
|| code
== EQ
)
184 && op1
!= const0_rtx
)
186 rtx op0_word
, op1_word
;
188 int num_words
= GET_MODE_BITSIZE (mode
) / BITS_PER_WORD
;
192 lab
= gen_label_rtx ();
194 for (i
= 0; i
< num_words
- 1; i
++)
196 op0_word
= simplify_gen_subreg (word_mode
, op0
, mode
,
198 op1_word
= simplify_gen_subreg (word_mode
, op1
, mode
,
200 xstormy16_emit_cbranch (NE
, op0_word
, op1_word
, code
== EQ
? lab
: loc
);
202 op0_word
= simplify_gen_subreg (word_mode
, op0
, mode
,
204 op1_word
= simplify_gen_subreg (word_mode
, op1
, mode
,
206 xstormy16_emit_cbranch (code
, op0_word
, op1_word
, loc
);
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. */
218 tmp
= gen_reg_rtx (mode
);
219 emit_move_insn (tmp
, op0
);
223 condition_rtx
= gen_rtx_fmt_ee (code
, mode
, op0
, op1
);
224 loc_ref
= gen_rtx_LABEL_REF (VOIDmode
, loc
);
225 branch
= gen_rtx_SET (pc_rtx
,
226 gen_rtx_IF_THEN_ELSE (VOIDmode
, condition_rtx
,
229 cy_clobber
= gen_rtx_CLOBBER (VOIDmode
, gen_rtx_REG (BImode
, CARRY_REGNUM
));
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
));
239 sub
= gen_rtx_SET (op0
, gen_rtx_MINUS (SImode
, op0
, op1
));
241 sub
= gen_rtx_CLOBBER (SImode
, op0
);
243 vec
= gen_rtvec (3, branch
, sub
, cy_clobber
);
246 emit_jump_insn (gen_rtx_PARALLEL (VOIDmode
, vec
));
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
251 xstormy16_expand_arith. */
254 xstormy16_split_cbranch (machine_mode mode
, rtx label
, rtx comparison
,
257 rtx op0
= XEXP (comparison
, 0);
258 rtx op1
= XEXP (comparison
, 1);
259 rtx_insn
*seq
, *last_insn
;
263 xstormy16_expand_arith (mode
, COMPARE
, dest
, op0
, op1
);
267 gcc_assert (INSN_P (seq
));
270 while (NEXT_INSN (last_insn
) != NULL_RTX
)
271 last_insn
= NEXT_INSN (last_insn
);
273 compare
= SET_SRC (XVECEXP (PATTERN (last_insn
), 0, 0));
274 PUT_CODE (XEXP (compare
, 0), GET_CODE (comparison
));
275 XEXP (compare
, 1) = gen_rtx_LABEL_REF (VOIDmode
, label
);
280 /* Return the string to output a conditional branch to LABEL, which is
281 the operand number of the label.
283 OP is the conditional expression, or NULL for branch-always.
285 REVERSED is nonzero if we should reverse the sense of the comparison.
290 xstormy16_output_cbranch_hi (rtx op
, const char *label
, int reversed
,
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
;
300 const char *operands
;
309 sprintf (string
, "%s %s", ccode
, label
);
313 code
= GET_CODE (op
);
315 if (! REG_P (XEXP (op
, 0)))
317 code
= swap_condition (code
);
323 /* Work out which way this really branches. */
325 code
= reverse_condition (code
);
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;
345 templ
= "b%s %s,.+8 | jmpf %s";
348 sprintf (string
, templ
, ccode
, operands
, label
);
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
357 OP is the conditional expression (OP is never NULL_RTX).
359 REVERSED is nonzero if we should reverse the sense of the comparison.
364 xstormy16_output_cbranch_si (rtx op
, const char *label
, int reversed
,
367 static char string
[64];
368 int need_longbranch
= get_attr_length (insn
) >= 8;
369 int really_reversed
= reversed
^ need_longbranch
;
375 code
= GET_CODE (op
);
377 /* Work out which way this really branches. */
379 code
= reverse_condition (code
);
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;
390 /* The missing codes above should never be generated. */
401 gcc_assert (REG_P (XEXP (op
, 0)));
403 regnum
= REGNO (XEXP (op
, 0));
404 sprintf (prevop
, "or %s,%s", reg_names
[regnum
], reg_names
[regnum
+1]);
408 case GE
: case LT
: case GEU
: case LTU
:
409 strcpy (prevop
, "sbc %2,%3");
417 templ
= "%s | b%s .+6 | jmpf %s";
419 templ
= "%s | b%s %s";
420 sprintf (string
, templ
, prevop
, ccode
, label
);
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
431 certain symbolic address on the SPARC when compiling PIC). In some cases,
432 both an intermediate and a scratch register are required.
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
437 RCLASS in MODE requires an intermediate register, you should define
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.
441 If copying a register RCLASS in MODE to X requires an intermediate or scratch
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.
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
449 to or from a register of RCLASS in MODE without requiring a scratch register.
450 Do not define this macro if it would always return `NO_REGS'.
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.
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
460 RCLASS) can meet the constraint given in the pattern, the value returned by
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.
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
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
474 copy and the `movM' pattern should use memory as an intermediate storage.
475 This case often occurs between floating-point and general registers. */
478 xstormy16_secondary_reload_class (enum reg_class rclass
,
479 machine_mode mode ATTRIBUTE_UNUSED
,
482 /* This chip has the interesting property that only the first eight
483 registers can be moved to/from memory. */
485 || ((GET_CODE (x
) == SUBREG
|| REG_P (x
))
486 && (true_regnum (x
) == -1
487 || true_regnum (x
) >= FIRST_PSEUDO_REGISTER
)))
488 && ! reg_class_subset_p (rclass
, EIGHT_REGS
))
494 /* Worker function for TARGET_PREFERRED_RELOAD_CLASS
495 and TARGET_PREFERRED_OUTPUT_RELOAD_CLASS. */
498 xstormy16_preferred_reload_class (rtx x
, reg_class_t rclass
)
500 /* Only the first eight registers can be moved to/from memory.
501 So those prefer EIGHT_REGS.
503 Similarly reloading an auto-increment address is going to
504 require loads and stores, so we must use EIGHT_REGS for those
506 if (rclass
== GENERAL_REGS
508 || GET_CODE (x
) == POST_INC
509 || GET_CODE (x
) == PRE_DEC
510 || GET_CODE (x
) == PRE_MODIFY
))
516 /* Predicate for symbols and addresses that reflect special 8-bit
520 xstormy16_below100_symbol (rtx x
,
521 machine_mode mode ATTRIBUTE_UNUSED
)
523 if (GET_CODE (x
) == CONST
)
525 if (GET_CODE (x
) == PLUS
&& CONST_INT_P (XEXP (x
, 1)))
528 if (GET_CODE (x
) == SYMBOL_REF
)
529 return (SYMBOL_REF_FLAGS (x
) & SYMBOL_FLAG_XSTORMY16_BELOW100
) != 0;
533 HOST_WIDE_INT i
= INTVAL (x
);
535 if ((i
>= 0x0000 && i
<= 0x00ff)
536 || (i
>= 0x7f00 && i
<= 0x7fff))
542 /* Likewise, but only for non-volatile MEMs, for patterns where the
543 MEM will get split into smaller sized accesses. */
546 xstormy16_splittable_below100_operand (rtx x
, machine_mode mode
)
548 if (MEM_P (x
) && MEM_VOLATILE_P (x
))
550 return xstormy16_below100_operand (x
, mode
);
553 /* Expand an 8-bit IOR. This either detects the one case we can
554 actually do, or uses a 16-bit IOR. */
557 xstormy16_expand_iorqi3 (rtx
*operands
)
559 rtx in
, out
, outsub
, val
;
565 if (xstormy16_onebit_set_operand (val
, QImode
))
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
);
578 in
= copy_to_mode_reg (QImode
, in
);
580 if (! REG_P (val
) && ! CONST_INT_P (val
))
581 val
= copy_to_mode_reg (QImode
, val
);
584 out
= gen_reg_rtx (QImode
);
586 in
= simplify_gen_subreg (HImode
, in
, QImode
, 0);
587 outsub
= simplify_gen_subreg (HImode
, out
, QImode
, 0);
589 if (! CONST_INT_P (val
))
590 val
= simplify_gen_subreg (HImode
, val
, QImode
, 0);
592 emit_insn (gen_iorhi3 (outsub
, in
, val
));
594 if (out
!= operands
[0])
595 emit_move_insn (operands
[0], out
);
598 /* Expand an 8-bit AND. This either detects the one case we can
599 actually do, or uses a 16-bit AND. */
602 xstormy16_expand_andqi3 (rtx
*operands
)
604 rtx in
, out
, outsub
, val
;
610 if (xstormy16_onebit_clr_operand (val
, QImode
))
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
);
623 in
= copy_to_mode_reg (QImode
, in
);
625 if (! REG_P (val
) && ! CONST_INT_P (val
))
626 val
= copy_to_mode_reg (QImode
, val
);
629 out
= gen_reg_rtx (QImode
);
631 in
= simplify_gen_subreg (HImode
, in
, QImode
, 0);
632 outsub
= simplify_gen_subreg (HImode
, out
, QImode
, 0);
634 if (! CONST_INT_P (val
))
635 val
= simplify_gen_subreg (HImode
, val
, QImode
, 0);
637 emit_insn (gen_andhi3 (outsub
, in
, val
));
639 if (out
!= operands
[0])
640 emit_move_insn (operands
[0], out
);
643 #define LEGITIMATE_ADDRESS_INTEGER_P(X, OFFSET) \
645 && (unsigned HOST_WIDE_INT) (INTVAL (X) + (OFFSET) + 2048) < 4096)
647 #define LEGITIMATE_ADDRESS_CONST_INT_P(X, OFFSET) \
649 && INTVAL (X) + (OFFSET) >= 0 \
650 && INTVAL (X) + (OFFSET) < 0x8000 \
651 && (INTVAL (X) + (OFFSET) < 0x100 || INTVAL (X) + (OFFSET) >= 0x7F00))
654 xstormy16_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED
,
657 if (LEGITIMATE_ADDRESS_CONST_INT_P (x
, 0))
660 if (GET_CODE (x
) == PLUS
661 && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x
, 1), 0))
664 /* PR 31232: Do not allow INT+INT as an address. */
669 if ((GET_CODE (x
) == PRE_MODIFY
&& CONST_INT_P (XEXP (XEXP (x
, 1), 1)))
670 || GET_CODE (x
) == POST_INC
671 || GET_CODE (x
) == PRE_DEC
)
675 && REGNO_OK_FOR_BASE_P (REGNO (x
))
676 && (! strict
|| REGNO (x
) < FIRST_PSEUDO_REGISTER
))
679 if (xstormy16_below100_symbol (x
, mode
))
685 /* Worker function for TARGET_MODE_DEPENDENT_ADDRESS_P.
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. */
693 xstormy16_mode_dependent_address_p (const_rtx x
,
694 addr_space_t as ATTRIBUTE_UNUSED
)
696 if (LEGITIMATE_ADDRESS_CONST_INT_P (x
, 0)
697 && ! LEGITIMATE_ADDRESS_CONST_INT_P (x
, 6))
700 if (GET_CODE (x
) == PLUS
701 && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x
, 1), 0)
702 && ! LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x
, 1), 6))
705 /* Auto-increment addresses are now treated generically in recog.cc. */
710 short_memory_operand (rtx x
, machine_mode mode
)
712 if (! memory_operand (x
, mode
))
714 return (GET_CODE (XEXP (x
, 0)) != PLUS
);
717 /* Splitter for the 'move' patterns, for modes not directly implemented
718 by hardware. Emit insns to copy a value of mode MODE from SRC to
721 This function is only called when reload_completed. */
724 xstormy16_split_move (machine_mode mode
, rtx dest
, rtx src
)
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;
733 rtx auto_inc_reg_rtx
= NULL_RTX
;
735 /* Check initial conditions. */
736 gcc_assert (reload_completed
737 && mode
!= QImode
&& mode
!= HImode
738 && nonimmediate_operand (dest
, mode
)
739 && general_operand (src
, mode
));
741 /* This case is not supported below, and shouldn't be generated. */
742 gcc_assert (! MEM_P (dest
) || ! MEM_P (src
));
744 /* This case is very very bad after reload, so trap it now. */
745 gcc_assert (GET_CODE (dest
) != SUBREG
&& GET_CODE (src
) != SUBREG
);
747 /* The general idea is to copy by words, offsetting the source and
748 destination. Normally the least-significant word will be copied
749 first, but for pre-dec operations it's better to copy the
750 most-significant word first. Only one operand can be a pre-dec
753 It's also possible that the copy overlaps so that the direction
759 mem_operand
= XEXP (dest
, 0);
760 dest_modifies
= side_effects_p (mem_operand
);
761 if (auto_inc_p (mem_operand
))
762 auto_inc_reg_rtx
= XEXP (mem_operand
, 0);
763 dest_volatile
= MEM_VOLATILE_P (dest
);
766 dest
= copy_rtx (dest
);
767 MEM_VOLATILE_P (dest
) = 0;
770 else if (MEM_P (src
))
772 mem_operand
= XEXP (src
, 0);
773 src_modifies
= side_effects_p (mem_operand
);
774 if (auto_inc_p (mem_operand
))
775 auto_inc_reg_rtx
= XEXP (mem_operand
, 0);
776 src_volatile
= MEM_VOLATILE_P (src
);
779 src
= copy_rtx (src
);
780 MEM_VOLATILE_P (src
) = 0;
784 mem_operand
= NULL_RTX
;
786 if (mem_operand
== NULL_RTX
)
790 && reg_overlap_mentioned_p (dest
, src
)
791 && REGNO (dest
) > REGNO (src
))
794 else if (GET_CODE (mem_operand
) == PRE_DEC
795 || (GET_CODE (mem_operand
) == PLUS
796 && GET_CODE (XEXP (mem_operand
, 0)) == PRE_DEC
))
798 else if (MEM_P (src
) && reg_overlap_mentioned_p (dest
, src
))
802 gcc_assert (REG_P (dest
));
803 regno
= REGNO (dest
);
805 gcc_assert (refers_to_regno_p (regno
, regno
+ num_words
,
808 if (refers_to_regno_p (regno
, mem_operand
))
810 else if (refers_to_regno_p (regno
+ num_words
- 1, regno
+ num_words
,
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
821 end
= direction
< 0 ? -1 : num_words
;
822 for (i
= direction
< 0 ? num_words
- 1 : 0; i
!= end
; i
+= direction
)
824 rtx w_src
, w_dest
, insn
;
827 w_src
= gen_rtx_MEM (word_mode
, mem_operand
);
829 w_src
= simplify_gen_subreg (word_mode
, src
, mode
, i
* UNITS_PER_WORD
);
831 MEM_VOLATILE_P (w_src
) = 1;
833 w_dest
= gen_rtx_MEM (word_mode
, mem_operand
);
835 w_dest
= simplify_gen_subreg (word_mode
, dest
, mode
,
838 MEM_VOLATILE_P (w_dest
) = 1;
840 /* The simplify_subreg calls must always be able to simplify. */
841 gcc_assert (GET_CODE (w_src
) != SUBREG
842 && GET_CODE (w_dest
) != SUBREG
);
844 insn
= emit_insn (gen_rtx_SET (w_dest
, w_src
));
845 if (auto_inc_reg_rtx
)
846 REG_NOTES (insn
) = alloc_EXPR_LIST (REG_INC
,
852 /* Expander for the 'move' patterns. Emit insns to copy a value of
853 mode MODE from SRC to DEST. */
856 xstormy16_expand_move (machine_mode mode
, rtx dest
, rtx src
)
858 if (MEM_P (dest
) && (GET_CODE (XEXP (dest
, 0)) == PRE_MODIFY
))
860 rtx pmv
= XEXP (dest
, 0);
861 rtx dest_reg
= XEXP (pmv
, 0);
862 rtx dest_mod
= XEXP (pmv
, 1);
863 rtx set
= gen_rtx_SET (dest_reg
, dest_mod
);
864 rtx clobber
= gen_rtx_CLOBBER (VOIDmode
, gen_rtx_REG (BImode
, CARRY_REGNUM
));
866 dest
= gen_rtx_MEM (mode
, dest_reg
);
867 emit_insn (gen_rtx_PARALLEL (VOIDmode
, gen_rtvec (2, set
, clobber
)));
869 else if (MEM_P (src
) && (GET_CODE (XEXP (src
, 0)) == PRE_MODIFY
))
871 rtx pmv
= XEXP (src
, 0);
872 rtx src_reg
= XEXP (pmv
, 0);
873 rtx src_mod
= XEXP (pmv
, 1);
874 rtx set
= gen_rtx_SET (src_reg
, src_mod
);
875 rtx clobber
= gen_rtx_CLOBBER (VOIDmode
, gen_rtx_REG (BImode
, CARRY_REGNUM
));
877 src
= gen_rtx_MEM (mode
, src_reg
);
878 emit_insn (gen_rtx_PARALLEL (VOIDmode
, gen_rtvec (2, set
, clobber
)));
881 /* There are only limited immediate-to-memory move instructions. */
882 if (! reload_in_progress
883 && ! reload_completed
885 && (! CONST_INT_P (XEXP (dest
, 0))
886 || ! xstormy16_legitimate_address_p (mode
, XEXP (dest
, 0), 0))
887 && ! xstormy16_below100_operand (dest
, mode
)
889 && GET_CODE (src
) != SUBREG
)
890 src
= copy_to_mode_reg (mode
, src
);
892 /* Don't emit something we would immediately split. */
894 && mode
!= HImode
&& mode
!= QImode
)
896 xstormy16_split_move (mode
, dest
, src
);
900 emit_insn (gen_rtx_SET (dest
, src
));
905 The stack is laid out as follows:
909 Register save area (up to 4 words)
910 Argument register save area for stdarg (NUM_ARGUMENT_REGISTERS words)
912 AP-> Return address (two words)
913 9th procedure parameter word
914 10th procedure parameter word
916 last procedure parameter word
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
922 /* A structure to describe the layout. */
923 struct xstormy16_stack_layout
925 /* Size of the topmost three items on the stack. */
927 int register_save_size
;
928 int stdarg_save_size
;
929 /* Sum of the above items. */
931 /* Various offsets. */
932 int first_local_minus_ap
;
937 /* Does REGNO need to be saved? */
938 #define REG_NEEDS_SAVE(REGNUM, IFUN) \
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) \
941 && (REGNUM != CARRY_REGNUM) \
942 && (df_regs_ever_live_p (REGNUM) || ! crtl->is_leaf)))
944 /* Compute the stack layout. */
946 struct xstormy16_stack_layout
947 xstormy16_compute_stack_layout (void)
949 struct xstormy16_stack_layout layout
;
951 const int ifun
= xstormy16_interrupt_function_p ();
953 layout
.locals_size
= get_frame_size ();
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
;
961 layout
.stdarg_save_size
= NUM_ARGUMENT_REGISTERS
* UNITS_PER_WORD
;
963 layout
.stdarg_save_size
= 0;
965 layout
.frame_size
= (layout
.locals_size
966 + layout
.register_save_size
967 + layout
.stdarg_save_size
);
969 if (crtl
->args
.size
<= 2048 && crtl
->args
.size
!= -1)
971 if (layout
.frame_size
- INCOMING_FRAME_SP_OFFSET
972 + crtl
->args
.size
<= 2048)
973 layout
.fp_minus_ap
= layout
.frame_size
- INCOMING_FRAME_SP_OFFSET
;
975 layout
.fp_minus_ap
= 2048 - crtl
->args
.size
;
978 layout
.fp_minus_ap
= (layout
.stdarg_save_size
979 + layout
.register_save_size
980 - INCOMING_FRAME_SP_OFFSET
);
981 layout
.sp_minus_fp
= (layout
.frame_size
- INCOMING_FRAME_SP_OFFSET
982 - layout
.fp_minus_ap
);
983 layout
.first_local_minus_ap
= layout
.sp_minus_fp
- layout
.locals_size
;
987 /* Worker function for TARGET_CAN_ELIMINATE. */
990 xstormy16_can_eliminate (const int from
, const int to
)
992 return (from
== ARG_POINTER_REGNUM
&& to
== STACK_POINTER_REGNUM
993 ? ! frame_pointer_needed
997 /* Determine how all the special registers get eliminated. */
1000 xstormy16_initial_elimination_offset (int from
, int to
)
1002 struct xstormy16_stack_layout layout
;
1005 layout
= xstormy16_compute_stack_layout ();
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
)
1010 result
= - layout
.locals_size
;
1011 else if (from
== ARG_POINTER_REGNUM
&& to
== HARD_FRAME_POINTER_REGNUM
)
1012 result
= - layout
.fp_minus_ap
;
1013 else if (from
== ARG_POINTER_REGNUM
&& to
== STACK_POINTER_REGNUM
)
1014 result
= - (layout
.sp_minus_fp
+ layout
.fp_minus_ap
);
1022 emit_addhi3_postreload (rtx dest
, rtx src0
, rtx src1
)
1024 rtx set
, clobber
, insn
;
1026 set
= gen_rtx_SET (dest
, gen_rtx_PLUS (HImode
, src0
, src1
));
1027 clobber
= gen_rtx_CLOBBER (VOIDmode
, gen_rtx_REG (BImode
, CARRY_REGNUM
));
1028 insn
= emit_insn (gen_rtx_PARALLEL (VOIDmode
, gen_rtvec (2, set
, clobber
)));
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
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. */
1044 xstormy16_expand_prologue (void)
1046 struct xstormy16_stack_layout layout
;
1050 const int ifun
= xstormy16_interrupt_function_p ();
1052 mem_push_rtx
= gen_rtx_POST_INC (Pmode
, stack_pointer_rtx
);
1053 mem_push_rtx
= gen_rtx_MEM (HImode
, mem_push_rtx
);
1055 layout
= xstormy16_compute_stack_layout ();
1057 if (layout
.locals_size
>= 32768)
1058 error ("local variable memory requirements exceed capacity");
1060 if (flag_stack_usage_info
)
1061 current_function_static_stack_size
= layout
.frame_size
;
1063 /* Save the argument registers if necessary. */
1064 if (layout
.stdarg_save_size
)
1065 for (regno
= FIRST_ARGUMENT_REGISTER
;
1066 regno
< FIRST_ARGUMENT_REGISTER
+ NUM_ARGUMENT_REGISTERS
;
1070 rtx reg
= gen_rtx_REG (HImode
, regno
);
1072 insn
= emit_move_insn (mem_push_rtx
, reg
);
1073 RTX_FRAME_RELATED_P (insn
) = 1;
1075 dwarf
= gen_rtx_SEQUENCE (VOIDmode
, rtvec_alloc (2));
1077 XVECEXP (dwarf
, 0, 0) = gen_rtx_SET (gen_rtx_MEM (Pmode
, stack_pointer_rtx
),
1079 XVECEXP (dwarf
, 0, 1) = gen_rtx_SET (stack_pointer_rtx
,
1080 plus_constant (Pmode
,
1082 GET_MODE_SIZE (Pmode
)));
1083 add_reg_note (insn
, REG_FRAME_RELATED_EXPR
, dwarf
);
1084 RTX_FRAME_RELATED_P (XVECEXP (dwarf
, 0, 0)) = 1;
1085 RTX_FRAME_RELATED_P (XVECEXP (dwarf
, 0, 1)) = 1;
1088 /* Push each of the registers to save. */
1089 for (regno
= 0; regno
< FIRST_PSEUDO_REGISTER
; regno
++)
1090 if (REG_NEEDS_SAVE (regno
, ifun
))
1093 rtx reg
= gen_rtx_REG (HImode
, regno
);
1095 insn
= emit_move_insn (mem_push_rtx
, reg
);
1096 RTX_FRAME_RELATED_P (insn
) = 1;
1098 dwarf
= gen_rtx_SEQUENCE (VOIDmode
, rtvec_alloc (2));
1100 XVECEXP (dwarf
, 0, 0) = gen_rtx_SET (gen_rtx_MEM (Pmode
, stack_pointer_rtx
),
1102 XVECEXP (dwarf
, 0, 1) = gen_rtx_SET (stack_pointer_rtx
,
1103 plus_constant (Pmode
,
1105 GET_MODE_SIZE (Pmode
)));
1106 add_reg_note (insn
, REG_FRAME_RELATED_EXPR
, dwarf
);
1107 RTX_FRAME_RELATED_P (XVECEXP (dwarf
, 0, 0)) = 1;
1108 RTX_FRAME_RELATED_P (XVECEXP (dwarf
, 0, 1)) = 1;
1111 /* It's just possible that the SP here might be what we need for
1113 if (frame_pointer_needed
&& layout
.sp_minus_fp
== layout
.locals_size
)
1115 insn
= emit_move_insn (hard_frame_pointer_rtx
, stack_pointer_rtx
);
1116 RTX_FRAME_RELATED_P (insn
) = 1;
1119 /* Allocate space for local variables. */
1120 if (layout
.locals_size
)
1122 insn
= emit_addhi3_postreload (stack_pointer_rtx
, stack_pointer_rtx
,
1123 GEN_INT (layout
.locals_size
));
1124 RTX_FRAME_RELATED_P (insn
) = 1;
1127 /* Set up the frame pointer, if required. */
1128 if (frame_pointer_needed
&& layout
.sp_minus_fp
!= layout
.locals_size
)
1130 insn
= emit_move_insn (hard_frame_pointer_rtx
, stack_pointer_rtx
);
1131 RTX_FRAME_RELATED_P (insn
) = 1;
1133 if (layout
.sp_minus_fp
)
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;
1143 /* Do we need an epilogue at all? */
1146 direct_return (void)
1148 return (reload_completed
1149 && xstormy16_compute_stack_layout ().frame_size
== 0
1150 && ! xstormy16_interrupt_function_p ());
1153 /* Called after register allocation to add any instructions needed for
1154 the epilogue. Using an epilogue insn is favored compared to putting
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
1162 xstormy16_expand_epilogue (void)
1164 struct xstormy16_stack_layout layout
;
1167 const int ifun
= xstormy16_interrupt_function_p ();
1169 mem_pop_rtx
= gen_rtx_PRE_DEC (Pmode
, stack_pointer_rtx
);
1170 mem_pop_rtx
= gen_rtx_MEM (HImode
, mem_pop_rtx
);
1172 layout
= xstormy16_compute_stack_layout ();
1174 /* Pop the stack for the locals. */
1175 if (layout
.locals_size
)
1177 if (frame_pointer_needed
&& layout
.sp_minus_fp
== layout
.locals_size
)
1178 emit_move_insn (stack_pointer_rtx
, hard_frame_pointer_rtx
);
1180 emit_addhi3_postreload (stack_pointer_rtx
, stack_pointer_rtx
,
1181 GEN_INT (- layout
.locals_size
));
1184 /* Restore any call-saved registers. */
1185 for (regno
= FIRST_PSEUDO_REGISTER
- 1; regno
>= 0; regno
--)
1186 if (REG_NEEDS_SAVE (regno
, ifun
))
1187 emit_move_insn (gen_rtx_REG (HImode
, regno
), mem_pop_rtx
);
1189 /* Pop the stack for the stdarg save area. */
1190 if (layout
.stdarg_save_size
)
1191 emit_addhi3_postreload (stack_pointer_rtx
, stack_pointer_rtx
,
1192 GEN_INT (- layout
.stdarg_save_size
));
1196 emit_jump_insn (gen_return_internal_interrupt ());
1198 emit_jump_insn (gen_return_internal ());
1202 xstormy16_epilogue_uses (int regno
)
1204 if (reload_completed
&& call_used_or_fixed_reg_p (regno
))
1206 const int ifun
= xstormy16_interrupt_function_p ();
1207 return REG_NEEDS_SAVE (regno
, ifun
);
1213 xstormy16_function_profiler (void)
1215 sorry ("%<function_profiler%> support");
1218 /* Update CUM to advance past argument ARG. Once this is done,
1219 the variable CUM is suitable for analyzing the *following*
1220 argument with `TARGET_FUNCTION_ARG', etc.
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,
1225 it makes life easier for xstormy16_build_va_list if it does update
1229 xstormy16_function_arg_advance (cumulative_args_t cum_v
,
1230 const function_arg_info
&arg
)
1232 CUMULATIVE_ARGS
*cum
= get_cumulative_args (cum_v
);
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
1237 if (*cum
< NUM_ARGUMENT_REGISTERS
1238 && (*cum
+ XSTORMY16_WORD_SIZE (arg
.type
, arg
.mode
)
1239 > NUM_ARGUMENT_REGISTERS
))
1240 *cum
= NUM_ARGUMENT_REGISTERS
;
1242 *cum
+= XSTORMY16_WORD_SIZE (arg
.type
, arg
.mode
);
1246 xstormy16_function_arg (cumulative_args_t cum_v
, const function_arg_info
&arg
)
1248 CUMULATIVE_ARGS
*cum
= get_cumulative_args (cum_v
);
1250 if (arg
.end_marker_p ())
1252 if (targetm
.calls
.must_pass_in_stack (arg
)
1253 || (*cum
+ XSTORMY16_WORD_SIZE (arg
.type
, arg
.mode
)
1254 > NUM_ARGUMENT_REGISTERS
))
1256 return gen_rtx_REG (arg
.mode
, *cum
+ FIRST_ARGUMENT_REGISTER
);
1259 /* Build the va_list type.
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
1264 for arguments that have not been passed in registers.
1265 To keep the layout nice, the pointer is first in the structure. */
1268 xstormy16_build_builtin_va_list (void)
1270 tree f_1
, f_2
, record
, type_decl
;
1272 record
= (*lang_hooks
.types
.make_type
) (RECORD_TYPE
);
1273 type_decl
= build_decl (BUILTINS_LOCATION
,
1274 TYPE_DECL
, get_identifier ("__va_list_tag"), record
);
1276 f_1
= build_decl (BUILTINS_LOCATION
,
1277 FIELD_DECL
, get_identifier ("base"),
1279 f_2
= build_decl (BUILTINS_LOCATION
,
1280 FIELD_DECL
, get_identifier ("count"),
1281 unsigned_type_node
);
1283 DECL_FIELD_CONTEXT (f_1
) = record
;
1284 DECL_FIELD_CONTEXT (f_2
) = record
;
1286 TYPE_STUB_DECL (record
) = type_decl
;
1287 TYPE_NAME (record
) = type_decl
;
1288 TYPE_FIELDS (record
) = f_1
;
1289 DECL_CHAIN (f_1
) = f_2
;
1291 layout_type (record
);
1296 /* Implement the stdarg/varargs va_start macro. STDARG_P is nonzero if this
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. */
1302 xstormy16_expand_builtin_va_start (tree valist
, rtx nextarg ATTRIBUTE_UNUSED
)
1304 tree f_base
, f_count
;
1308 if (xstormy16_interrupt_function_p ())
1309 error ("cannot use %<va_start%> in interrupt function");
1311 f_base
= TYPE_FIELDS (va_list_type_node
);
1312 f_count
= DECL_CHAIN (f_base
);
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
,
1318 t
= make_tree (TREE_TYPE (base
), virtual_incoming_args_rtx
);
1319 u
= build_int_cst (NULL_TREE
, - INCOMING_FRAME_SP_OFFSET
);
1320 u
= fold_convert (TREE_TYPE (count
), u
);
1321 t
= fold_build_pointer_plus (t
, u
);
1322 t
= build2 (MODIFY_EXPR
, TREE_TYPE (base
), base
, t
);
1323 TREE_SIDE_EFFECTS (t
) = 1;
1324 expand_expr (t
, const0_rtx
, VOIDmode
, EXPAND_NORMAL
);
1326 t
= build2 (MODIFY_EXPR
, TREE_TYPE (count
), count
,
1327 build_int_cst (NULL_TREE
,
1328 crtl
->args
.info
* UNITS_PER_WORD
));
1329 TREE_SIDE_EFFECTS (t
) = 1;
1330 expand_expr (t
, const0_rtx
, VOIDmode
, EXPAND_NORMAL
);
1333 /* Implement the stdarg/varargs va_arg macro. VALIST is the variable
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. */
1338 xstormy16_gimplify_va_arg_expr (tree valist
, tree type
, gimple_seq
*pre_p
,
1339 gimple_seq
*post_p ATTRIBUTE_UNUSED
)
1341 tree f_base
, f_count
;
1343 tree count_tmp
, addr
, t
;
1344 tree lab_gotaddr
, lab_fromstack
;
1345 int size
, size_of_reg_args
, must_stack
;
1348 f_base
= TYPE_FIELDS (va_list_type_node
);
1349 f_count
= DECL_CHAIN (f_base
);
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
,
1355 must_stack
= must_pass_va_arg_in_stack (type
);
1356 size_tree
= round_up (size_in_bytes (type
), UNITS_PER_WORD
);
1357 gimplify_expr (&size_tree
, pre_p
, NULL
, is_gimple_val
, fb_rvalue
);
1359 size_of_reg_args
= NUM_ARGUMENT_REGISTERS
* UNITS_PER_WORD
;
1361 count_tmp
= get_initialized_tmp_var (count
, pre_p
, NULL
);
1362 lab_gotaddr
= create_artificial_label (UNKNOWN_LOCATION
);
1363 lab_fromstack
= create_artificial_label (UNKNOWN_LOCATION
);
1364 addr
= create_tmp_var (ptr_type_node
);
1370 t
= fold_convert (TREE_TYPE (count
), size_tree
);
1371 t
= build2 (PLUS_EXPR
, TREE_TYPE (count
), count_tmp
, t
);
1372 r
= fold_convert (TREE_TYPE (count
), size_int (size_of_reg_args
));
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
),
1377 gimplify_and_add (t
, pre_p
);
1379 t
= fold_build_pointer_plus (base
, count_tmp
);
1380 gimplify_assign (addr
, t
, pre_p
);
1382 t
= build1 (GOTO_EXPR
, void_type_node
, lab_gotaddr
);
1383 gimplify_and_add (t
, pre_p
);
1385 t
= build1 (LABEL_EXPR
, void_type_node
, lab_fromstack
);
1386 gimplify_and_add (t
, pre_p
);
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. */
1392 size
= PUSH_ROUNDING (int_size_in_bytes (type
));
1393 if (size
> 2 || size
< 0 || must_stack
)
1397 r
= size_int (NUM_ARGUMENT_REGISTERS
* UNITS_PER_WORD
);
1398 u
= build2 (MODIFY_EXPR
, TREE_TYPE (count_tmp
), count_tmp
, r
);
1400 t
= fold_convert (TREE_TYPE (count
), r
);
1401 t
= build2 (GE_EXPR
, boolean_type_node
, count_tmp
, t
);
1402 t
= build3 (COND_EXPR
, void_type_node
, t
, NULL_TREE
, u
);
1403 gimplify_and_add (t
, pre_p
);
1406 t
= size_int (NUM_ARGUMENT_REGISTERS
* UNITS_PER_WORD
1407 + INCOMING_FRAME_SP_OFFSET
);
1408 t
= fold_convert (TREE_TYPE (count
), t
);
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
));
1412 t
= fold_convert (TREE_TYPE (t
), fold (t
));
1413 t
= fold_build1 (NEGATE_EXPR
, TREE_TYPE (t
), t
);
1414 t
= fold_build_pointer_plus (base
, t
);
1415 gimplify_assign (addr
, t
, pre_p
);
1417 t
= build1 (LABEL_EXPR
, void_type_node
, lab_gotaddr
);
1418 gimplify_and_add (t
, pre_p
);
1420 t
= fold_convert (TREE_TYPE (count
), size_tree
);
1421 t
= build2 (PLUS_EXPR
, TREE_TYPE (count
), count_tmp
, t
);
1422 gimplify_assign (count
, t
, pre_p
);
1424 addr
= fold_convert (build_pointer_type (type
), addr
);
1425 return build_va_arg_indirect_ref (addr
);
1428 /* Worker function for TARGET_TRAMPOLINE_INIT. */
1431 xstormy16_trampoline_init (rtx m_tramp
, tree fndecl
, rtx static_chain
)
1433 rtx temp
= gen_reg_rtx (HImode
);
1434 rtx reg_fnaddr
= gen_reg_rtx (HImode
);
1435 rtx reg_addr
, reg_addr_mem
;
1437 reg_addr
= copy_to_reg (XEXP (m_tramp
, 0));
1438 reg_addr_mem
= adjust_automodify_address (m_tramp
, HImode
, reg_addr
, 0);
1440 emit_move_insn (temp
, GEN_INT (0x3130 | STATIC_CHAIN_REGNUM
));
1441 emit_move_insn (reg_addr_mem
, temp
);
1442 emit_insn (gen_addhi3 (reg_addr
, reg_addr
, const2_rtx
));
1443 reg_addr_mem
= adjust_automodify_address (reg_addr_mem
, VOIDmode
, NULL
, 2);
1445 emit_move_insn (temp
, static_chain
);
1446 emit_move_insn (reg_addr_mem
, temp
);
1447 emit_insn (gen_addhi3 (reg_addr
, reg_addr
, const2_rtx
));
1448 reg_addr_mem
= adjust_automodify_address (reg_addr_mem
, VOIDmode
, NULL
, 2);
1450 emit_move_insn (reg_fnaddr
, XEXP (DECL_RTL (fndecl
), 0));
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
);
1455 emit_insn (gen_addhi3 (reg_addr
, reg_addr
, const2_rtx
));
1456 reg_addr_mem
= adjust_automodify_address (reg_addr_mem
, VOIDmode
, NULL
, 2);
1458 emit_insn (gen_lshrhi3 (reg_fnaddr
, reg_fnaddr
, GEN_INT (8)));
1459 emit_move_insn (reg_addr_mem
, reg_fnaddr
);
1462 /* Worker function for TARGET_FUNCTION_VALUE. */
1465 xstormy16_function_value (const_tree valtype
,
1466 const_tree func ATTRIBUTE_UNUSED
,
1467 bool outgoing ATTRIBUTE_UNUSED
)
1470 mode
= TYPE_MODE (valtype
);
1471 PROMOTE_MODE (mode
, 0, valtype
);
1472 return gen_rtx_REG (mode
, RETURN_VALUE_REGNUM
);
1475 /* Worker function for TARGET_LIBCALL_VALUE. */
1478 xstormy16_libcall_value (machine_mode mode
,
1479 const_rtx fun ATTRIBUTE_UNUSED
)
1481 return gen_rtx_REG (mode
, RETURN_VALUE_REGNUM
);
1484 /* Worker function for TARGET_FUNCTION_VALUE_REGNO_P. */
1487 xstormy16_function_value_regno_p (const unsigned int regno
)
1489 return (regno
== RETURN_VALUE_REGNUM
);
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.
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.
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'.
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
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
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
,
1525 const char *fnname
= IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk_fndecl
));
1526 int regnum
= FIRST_ARGUMENT_REGISTER
;
1528 assemble_start_function (thunk_fndecl
, fnname
);
1529 /* There might be a hidden first argument for a returned structure. */
1530 if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function
)), function
))
1533 fprintf (file
, "\tadd %s,#0x%x\n", reg_names
[regnum
], (int) delta
& 0xFFFF);
1534 fputs ("\tjmpf ", file
);
1535 assemble_name (file
, XSTR (XEXP (DECL_RTL (function
), 0), 0));
1537 assemble_end_function (thunk_fndecl
, fnname
);
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. */
1548 xstormy16_asm_output_aligned_common (FILE *stream
,
1555 rtx mem
= decl
== NULL_TREE
? NULL_RTX
: DECL_RTL (decl
);
1560 && GET_CODE (symbol
= XEXP (mem
, 0)) == SYMBOL_REF
1561 && SYMBOL_REF_FLAGS (symbol
) & SYMBOL_FLAG_XSTORMY16_BELOW100
)
1566 switch_to_section (bss100_section
);
1574 name2
= default_strip_name_encoding (name
);
1576 fprintf (stream
, "\t.globl\t%s\n", name2
);
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
);
1587 fprintf (stream
, "\t.local\t");
1588 assemble_name (stream
, name
);
1589 fprintf (stream
, "\n");
1591 fprintf (stream
, "\t.comm\t");
1592 assemble_name (stream
, name
);
1593 fprintf (stream
, ",%u,%u\n", size
, align
/ BITS_PER_UNIT
);
1596 /* Implement TARGET_ASM_INIT_SECTIONS. */
1599 xstormy16_asm_init_sections (void)
1602 = get_unnamed_section (SECTION_WRITE
| SECTION_BSS
,
1603 output_section_asm_op
,
1604 "\t.section \".bss_below100\",\"aw\",@nobits");
1607 /* Mark symbols with the "below100" attribute so that we can use the
1608 special addressing modes for them. */
1611 xstormy16_encode_section_info (tree decl
, rtx r
, int first
)
1613 default_encode_section_info (decl
, r
, first
);
1615 if (TREE_CODE (decl
) == VAR_DECL
1616 && (lookup_attribute ("below100", DECL_ATTRIBUTES (decl
))
1617 || lookup_attribute ("BELOW100", DECL_ATTRIBUTES (decl
))))
1619 rtx symbol
= XEXP (r
, 0);
1621 gcc_assert (GET_CODE (symbol
) == SYMBOL_REF
);
1622 SYMBOL_REF_FLAGS (symbol
) |= SYMBOL_FLAG_XSTORMY16_BELOW100
;
1626 #undef TARGET_ASM_CONSTRUCTOR
1627 #define TARGET_ASM_CONSTRUCTOR xstormy16_asm_out_constructor
1628 #undef TARGET_ASM_DESTRUCTOR
1629 #define TARGET_ASM_DESTRUCTOR xstormy16_asm_out_destructor
1631 /* Output constructors and destructors. Just like
1632 default_named_section_asm_out_* but don't set the sections writable. */
1635 xstormy16_asm_out_destructor (rtx symbol
, int priority
)
1637 const char *section
= ".dtors";
1640 /* ??? This only works reliably with the GNU linker. */
1641 if (priority
!= DEFAULT_INIT_PRIORITY
)
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
);
1651 switch_to_section (get_section (section
, 0, NULL
));
1652 assemble_align (POINTER_SIZE
);
1653 assemble_integer (symbol
, POINTER_SIZE
/ BITS_PER_UNIT
, POINTER_SIZE
, 1);
1657 xstormy16_asm_out_constructor (rtx symbol
, int priority
)
1659 const char *section
= ".ctors";
1662 /* ??? This only works reliably with the GNU linker. */
1663 if (priority
!= DEFAULT_INIT_PRIORITY
)
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
);
1673 switch_to_section (get_section (section
, 0, NULL
));
1674 assemble_align (POINTER_SIZE
);
1675 assemble_integer (symbol
, POINTER_SIZE
/ BITS_PER_UNIT
, POINTER_SIZE
, 1);
1678 /* Worker function for TARGET_PRINT_OPERAND_ADDRESS.
1680 Print a memory address as an operand to reference that memory location. */
1683 xstormy16_print_operand_address (FILE *file
, machine_mode
/*mode*/,
1686 HOST_WIDE_INT offset
;
1687 int pre_dec
, post_inc
;
1689 /* There are a few easy cases. */
1690 if (CONST_INT_P (address
))
1692 fprintf (file
, HOST_WIDE_INT_PRINT_DEC
, INTVAL (address
) & 0xFFFF);
1696 if (CONSTANT_P (address
) || LABEL_P (address
))
1698 output_addr_const (file
, address
);
1702 /* Otherwise, it's hopefully something of the form
1703 (plus:HI (pre_dec:HI (reg:HI ...)) (const_int ...)). */
1704 if (GET_CODE (address
) == PLUS
)
1706 gcc_assert (CONST_INT_P (XEXP (address
, 1)));
1707 offset
= INTVAL (XEXP (address
, 1));
1708 address
= XEXP (address
, 0);
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);
1718 gcc_assert (REG_P (address
));
1723 fputs (reg_names
[REGNO (address
)], file
);
1727 fprintf (file
, "," HOST_WIDE_INT_PRINT_DEC
, offset
);
1731 /* Worker function for TARGET_PRINT_OPERAND.
1733 Print an operand to an assembler instruction. */
1736 xstormy16_print_operand (FILE *file
, rtx x
, int code
)
1741 /* There is either one bit set, or one bit clear, in X.
1742 Print it preceded by '#'. */
1744 static int bits_set
[8] = { 0, 1, 1, 2, 1, 2, 2, 3 };
1745 HOST_WIDE_INT xx
= 1;
1748 if (CONST_INT_P (x
))
1751 output_operand_lossage ("'B' operand is not constant");
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)
1760 /* Remove sign extension bits. */
1761 if ((~xx
& ~(HOST_WIDE_INT
)0xff) == 0)
1763 else if ((~xx
& ~(HOST_WIDE_INT
)0xffff) == 0)
1765 l
= exact_log2 (xx
);
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
);
1778 output_operand_lossage ("'B' operand has multiple bits set");
1780 fprintf (file
, IMMEDIATE_PREFIX HOST_WIDE_INT_PRINT_DEC
, l
);
1785 /* Print the symbol without a surrounding @fptr(). */
1786 if (GET_CODE (x
) == SYMBOL_REF
)
1787 assemble_name (file
, XSTR (x
, 0));
1788 else if (LABEL_P (x
))
1789 output_asm_label (x
);
1791 xstormy16_print_operand_address (file
, VOIDmode
, x
);
1796 /* Print the immediate operand less one, preceded by '#'.
1797 For 'O', negate it first. */
1799 HOST_WIDE_INT xx
= 0;
1801 if (CONST_INT_P (x
))
1804 output_operand_lossage ("'o' operand is not constant");
1809 fprintf (file
, IMMEDIATE_PREFIX HOST_WIDE_INT_PRINT_DEC
, xx
- 1);
1814 /* Print the shift mask for bp/bn. */
1816 HOST_WIDE_INT xx
= 1;
1819 if (CONST_INT_P (x
))
1822 output_operand_lossage ("'B' operand is not constant");
1826 fputs (IMMEDIATE_PREFIX
, file
);
1827 fprintf (file
, HOST_WIDE_INT_PRINT_DEC
, l
);
1832 /* Handled below. */
1836 output_operand_lossage ("xstormy16_print_operand: unknown code");
1840 switch (GET_CODE (x
))
1843 fputs (reg_names
[REGNO (x
)], file
);
1847 xstormy16_print_operand_address (file
, GET_MODE (x
), XEXP (x
, 0));
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
);
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
1868 range LOWER_BOUND to LOWER_BOUND + RANGE - 1. */
1871 xstormy16_expand_casesi (rtx index
, rtx lower_bound
, rtx range
,
1872 rtx table
, rtx default_label
)
1874 HOST_WIDE_INT range_i
= INTVAL (range
);
1877 /* This code uses 'br', so it can deal only with tables of size up to
1879 if (range_i
>= 8192)
1880 sorry ("switch statement of size %lu entries too large",
1881 (unsigned long) range_i
);
1883 index
= expand_binop (SImode
, sub_optab
, index
, lower_bound
, NULL_RTX
, 0,
1885 emit_cmp_and_jump_insns (index
, range
, GTU
, NULL_RTX
, SImode
, 1,
1887 int_index
= gen_lowpart_common (HImode
, index
);
1888 emit_insn (gen_ashlhi3 (int_index
, int_index
, const2_rtx
));
1889 emit_jump_insn (gen_tablejump_pcrel (int_index
, table
));
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.
1897 TODO: it might be nice to output 'br' instructions if they could
1901 xstormy16_output_addr_vec (FILE *file
, rtx label ATTRIBUTE_UNUSED
, rtx table
)
1905 switch_to_section (current_function_section ());
1907 vlen
= XVECLEN (table
, 0);
1908 for (idx
= 0; idx
< vlen
; idx
++)
1910 fputs ("\tjmpf ", file
);
1911 output_asm_label (XEXP (XVECEXP (table
, 0, idx
), 0));
1916 /* Expander for the `call' patterns.
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. */
1922 xstormy16_expand_call (rtx retval
, rtx dest
, rtx counter
)
1927 gcc_assert (MEM_P (dest
));
1928 dest
= XEXP (dest
, 0);
1930 if (! CONSTANT_P (dest
) && ! REG_P (dest
))
1931 dest
= force_reg (Pmode
, dest
);
1936 mode
= GET_MODE (retval
);
1938 call
= gen_rtx_CALL (mode
, gen_rtx_MEM (FUNCTION_MODE
, dest
),
1941 call
= gen_rtx_SET (retval
, call
);
1943 if (! CONSTANT_P (dest
))
1945 temp
= gen_reg_rtx (HImode
);
1946 emit_move_insn (temp
, const0_rtx
);
1951 call
= gen_rtx_PARALLEL (VOIDmode
, gen_rtvec (2, call
,
1952 gen_rtx_USE (VOIDmode
, temp
)));
1953 emit_call_insn (call
);
1956 /* Expanders for multiword computational operations. */
1958 /* Expander for arithmetic operations; emit insns to compute
1960 (set DEST (CODE:MODE SRC0 SRC1))
1962 When CODE is COMPARE, a branch template is generated
1963 (this saves duplicating code in xstormy16_split_cbranch). */
1966 xstormy16_expand_arith (machine_mode mode
, enum rtx_code code
,
1967 rtx dest
, rtx src0
, rtx src1
)
1969 int num_words
= GET_MODE_BITSIZE (mode
) / BITS_PER_WORD
;
1974 emit_move_insn (src0
, const0_rtx
);
1976 for (i
= 0; i
< num_words
; i
++)
1978 rtx w_src0
, w_src1
, w_dest
;
1981 w_src0
= simplify_gen_subreg (word_mode
, src0
, mode
,
1982 i
* UNITS_PER_WORD
);
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
);
1990 && CONST_INT_P (w_src1
)
1991 && INTVAL (w_src1
) == 0)
1995 insn
= gen_addchi4 (w_dest
, w_src0
, w_src1
);
1997 insn
= gen_addchi5 (w_dest
, w_src0
, w_src1
);
2003 if (code
== COMPARE
&& i
== num_words
- 1)
2005 rtx branch
, sub
, clobber
, sub_1
;
2007 sub_1
= gen_rtx_MINUS (HImode
, w_src0
,
2008 gen_rtx_ZERO_EXTEND (HImode
, gen_rtx_REG (BImode
, CARRY_REGNUM
)));
2009 sub
= gen_rtx_SET (w_dest
,
2010 gen_rtx_MINUS (HImode
, sub_1
, w_src1
));
2011 clobber
= gen_rtx_CLOBBER (VOIDmode
, gen_rtx_REG (BImode
, CARRY_REGNUM
));
2012 branch
= gen_rtx_SET (pc_rtx
,
2013 gen_rtx_IF_THEN_ELSE (VOIDmode
,
2019 insn
= gen_rtx_PARALLEL (VOIDmode
,
2020 gen_rtvec (3, branch
, sub
, clobber
));
2024 && CONST_INT_P (w_src1
)
2025 && INTVAL (w_src1
) == 0)
2028 insn
= gen_subchi4 (w_dest
, w_src0
, w_src1
);
2030 insn
= gen_subchi5 (w_dest
, w_src0
, w_src1
);
2036 if (CONST_INT_P (w_src1
)
2037 && INTVAL (w_src1
) == -(code
== AND
))
2040 insn
= gen_rtx_SET (w_dest
, gen_rtx_fmt_ee (code
, mode
,
2045 insn
= gen_rtx_SET (w_dest
, gen_rtx_NOT (mode
, w_src0
));
2056 /* If we emit nothing, try_split() will think we failed. So emit
2057 something that does nothing and can be optimized away. */
2062 /* The shift operations are split at output time for constant values;
2063 variable-width shifts get handed off to a library routine.
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. */
2069 xstormy16_output_shift (machine_mode mode
, enum rtx_code code
,
2070 rtx x
, rtx size_r
, rtx temp
)
2073 const char *r0
, *r1
, *rt
;
2076 gcc_assert (CONST_INT_P (size_r
)
2080 size
= INTVAL (size_r
) & (GET_MODE_BITSIZE (mode
) - 1);
2085 r0
= reg_names
[REGNO (x
)];
2086 r1
= reg_names
[REGNO (x
) + 1];
2088 /* For shifts of size 1, we can use the rotate instructions. */
2094 sprintf (r
, "shl %s,#1 | rlc %s,#1", r0
, r1
);
2097 sprintf (r
, "asr %s,#1 | rrc %s,#1", r1
, r0
);
2100 sprintf (r
, "shr %s,#1 | rrc %s,#1", r1
, r0
);
2108 /* For large shifts, there are easy special cases. */
2114 sprintf (r
, "mov %s,%s | mov %s,#0", r1
, r0
, r0
);
2117 sprintf (r
, "mov %s,%s | asr %s,#15", r0
, r1
, r1
);
2120 sprintf (r
, "mov %s,%s | mov %s,#0", r0
, r1
, r1
);
2132 sprintf (r
, "mov %s,%s | mov %s,#0 | shl %s,#%d",
2133 r1
, r0
, r0
, r1
, (int) size
- 16);
2136 sprintf (r
, "mov %s,%s | asr %s,#15 | asr %s,#%d",
2137 r0
, r1
, r1
, r0
, (int) size
- 16);
2140 sprintf (r
, "mov %s,%s | mov %s,#0 | shr %s,#%d",
2141 r0
, r1
, r1
, r0
, (int) size
- 16);
2149 /* For the rest, we have to do more work. In particular, we
2150 need a temporary. */
2151 rt
= reg_names
[REGNO (temp
)];
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
),
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
),
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
),
2178 /* Attribute handling. */
2180 /* Return nonzero if the function is an interrupt function. */
2183 xstormy16_interrupt_function_p (void)
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. */
2193 attributes
= TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl
));
2194 return lookup_attribute ("interrupt", attributes
) != NULL_TREE
;
2197 #undef TARGET_ATTRIBUTE_TABLE
2198 #define TARGET_ATTRIBUTE_TABLE xstormy16_attribute_table
2200 static tree xstormy16_handle_interrupt_attribute
2201 (tree
*, tree
, tree
, int, bool *);
2202 static tree xstormy16_handle_below100_attribute
2203 (tree
*, tree
, tree
, int, bool *);
2205 static const struct attribute_spec xstormy16_attribute_table
[] =
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
}
2218 /* Handle an "interrupt" attribute;
2219 arguments as in struct attribute_spec.handler. */
2222 xstormy16_handle_interrupt_attribute (tree
*node
, tree name
,
2223 tree args ATTRIBUTE_UNUSED
,
2224 int flags ATTRIBUTE_UNUSED
,
2227 if (TREE_CODE (*node
) != FUNCTION_TYPE
)
2229 warning (OPT_Wattributes
, "%qE attribute only applies to functions",
2231 *no_add_attrs
= true;
2237 /* Handle an "below" attribute;
2238 arguments as in struct attribute_spec.handler. */
2241 xstormy16_handle_below100_attribute (tree
*node
,
2242 tree name ATTRIBUTE_UNUSED
,
2243 tree args ATTRIBUTE_UNUSED
,
2244 int flags ATTRIBUTE_UNUSED
,
2247 if (TREE_CODE (*node
) != VAR_DECL
2248 && TREE_CODE (*node
) != POINTER_TYPE
2249 && TREE_CODE (*node
) != TYPE_DECL
)
2251 warning (OPT_Wattributes
,
2252 "%<__BELOW100__%> attribute only applies to variables");
2253 *no_add_attrs
= true;
2255 else if (args
== NULL_TREE
&& TREE_CODE (*node
) == VAR_DECL
)
2257 if (! (TREE_PUBLIC (*node
) || TREE_STATIC (*node
)))
2259 warning (OPT_Wattributes
, "%<__BELOW100__%> attribute not allowed "
2260 "with auto storage class");
2261 *no_add_attrs
= true;
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
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. */
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" },
2286 { NULL
, 0, NULL
, NULL
}
2290 xstormy16_init_builtins (void)
2292 tree args
[2], ret_type
, arg
= NULL_TREE
, ftype
;
2295 ret_type
= void_type_node
;
2297 for (i
= 0; s16builtins
[i
].name
; i
++)
2299 n_args
= strlen (s16builtins
[i
].arg_types
) - 1;
2301 gcc_assert (n_args
<= (int) ARRAY_SIZE (args
));
2303 for (a
= n_args
- 1; a
>= 0; a
--)
2304 args
[a
] = NULL_TREE
;
2306 for (a
= n_args
; a
>= 0; a
--)
2308 switch (s16builtins
[i
].arg_types
[a
])
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;
2314 default: gcc_unreachable ();
2321 ftype
= build_function_type_list (ret_type
, args
[0], args
[1], NULL_TREE
);
2322 add_builtin_function (s16builtins
[i
].name
, ftype
,
2323 i
, BUILT_IN_MD
, NULL
, NULL_TREE
);
2328 xstormy16_expand_builtin (tree exp
, rtx target
,
2329 rtx subtarget ATTRIBUTE_UNUSED
,
2330 machine_mode mode ATTRIBUTE_UNUSED
,
2331 int ignore ATTRIBUTE_UNUSED
)
2333 rtx op
[10], args
[10], pat
, copyto
[10], retval
= 0;
2334 tree fndecl
, argtree
;
2337 fndecl
= TREE_OPERAND (TREE_OPERAND (exp
, 0), 0);
2338 argtree
= TREE_OPERAND (exp
, 1);
2339 i
= DECL_MD_FUNCTION_CODE (fndecl
);
2340 code
= s16builtins
[i
].md_code
;
2342 for (a
= 0; a
< 10 && argtree
; a
++)
2344 args
[a
] = expand_normal (TREE_VALUE (argtree
));
2345 argtree
= TREE_CHAIN (argtree
);
2348 for (o
= 0; s16builtins
[i
].arg_ops
[o
]; o
++)
2350 char ao
= s16builtins
[i
].arg_ops
[o
];
2351 char c
= insn_data
[code
].operand
[o
].constraint
[0];
2356 omode
= (machine_mode
) insn_data
[code
].operand
[o
].mode
;
2358 op
[o
] = target
? target
: gen_reg_rtx (omode
);
2360 op
[o
] = gen_reg_rtx (omode
);
2362 op
[o
] = args
[(int) hex_value (ao
)];
2364 if (! (*insn_data
[code
].operand
[o
].predicate
) (op
[o
], GET_MODE (op
[o
])))
2366 if (c
== '+' || c
== '=')
2369 op
[o
] = gen_reg_rtx (omode
);
2372 op
[o
] = copy_to_mode_reg (omode
, op
[o
]);
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]);
2383 for (o
= 0; s16builtins
[i
].arg_ops
[o
]; o
++)
2386 emit_move_insn (copyto
[o
], op
[o
]);
2387 if (op
[o
] == retval
)
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
2399 combine_bnp (rtx_insn
*insn
)
2401 int insn_code
, regno
, need_extend
;
2403 rtx cond
, reg
, qireg
, mem
;
2404 rtx_insn
*and_insn
, *load
;
2405 machine_mode load_mode
= QImode
;
2406 machine_mode and_mode
= QImode
;
2407 rtx_insn
*shift
= NULL
;
2409 insn_code
= recog_memoized (insn
);
2410 if (insn_code
!= CODE_FOR_cbranchhi
2411 && insn_code
!= CODE_FOR_cbranchhi_neg
)
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
))
2431 reg
= XEXP (cond
, 0);
2434 regno
= REGNO (reg
);
2435 if (XEXP (cond
, 1) != const0_rtx
)
2437 if (! find_regno_note (insn
, REG_DEAD
, regno
))
2439 qireg
= gen_rtx_REG (QImode
, regno
);
2443 /* LT and GE conditionals should have a sign extend before
2445 for (and_insn
= prev_real_insn (insn
);
2446 and_insn
!= NULL_RTX
;
2447 and_insn
= prev_real_insn (and_insn
))
2449 int and_code
= recog_memoized (and_insn
);
2451 if (and_code
== CODE_FOR_extendqihi2
2452 && rtx_equal_p (SET_DEST (PATTERN (and_insn
)), reg
)
2453 && rtx_equal_p (XEXP (SET_SRC (PATTERN (and_insn
)), 0), qireg
))
2456 if (and_code
== CODE_FOR_movhi_internal
2457 && rtx_equal_p (SET_DEST (PATTERN (and_insn
)), reg
))
2459 /* This is for testing bit 15. */
2464 if (reg_mentioned_p (reg
, and_insn
))
2467 if (! NOTE_P (and_insn
) && ! NONJUMP_INSN_P (and_insn
))
2473 /* EQ and NE conditionals have an AND before them. */
2474 for (and_insn
= prev_real_insn (insn
);
2475 and_insn
!= NULL_RTX
;
2476 and_insn
= prev_real_insn (and_insn
))
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
))
2483 if (reg_mentioned_p (reg
, and_insn
))
2486 if (! NOTE_P (and_insn
) && ! NONJUMP_INSN_P (and_insn
))
2492 /* Some mis-optimizations by GCC can generate a RIGHT-SHIFT
2493 followed by an AND like this:
2495 (parallel [(set (reg:HI r7) (lshiftrt:HI (reg:HI r7) (const_int 3)))
2496 (clobber (reg:BI carry))]
2498 (set (reg:HI r7) (and:HI (reg:HI r7) (const_int 1)))
2500 Attempt to detect this here. */
2501 for (shift
= prev_real_insn (and_insn
); shift
;
2502 shift
= prev_real_insn (shift
))
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
))
2509 if (reg_mentioned_p (reg
, shift
)
2510 || (! NOTE_P (shift
) && ! NONJUMP_INSN_P (shift
)))
2519 if (and_insn
== NULL_RTX
)
2522 for (load
= shift
? prev_real_insn (shift
) : prev_real_insn (and_insn
);
2524 load
= prev_real_insn (load
))
2526 int load_code
= recog_memoized (load
);
2528 if (load_code
== CODE_FOR_movhi_internal
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
))))
2537 if (load_code
== CODE_FOR_movqi_internal
2538 && rtx_equal_p (SET_DEST (PATTERN (load
)), qireg
)
2539 && xstormy16_below100_operand (SET_SRC (PATTERN (load
)), QImode
))
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
))
2554 if (reg_mentioned_p (reg
, load
))
2557 if (! NOTE_P (load
) && ! NONJUMP_INSN_P (load
))
2563 mem
= SET_SRC (PATTERN (load
));
2567 mask
= (load_mode
== HImode
) ? 0x8000 : 0x80;
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);
2577 if (!xstormy16_onebit_set_operand (XEXP (SET_SRC (PATTERN (and_insn
)), 1),
2581 mask
= (int) INTVAL (XEXP (SET_SRC (PATTERN (and_insn
)), 1));
2584 mask
<<= INTVAL (XEXP (SET_SRC (XVECEXP (PATTERN (shift
), 0, 0)), 1));
2587 if (load_mode
== HImode
)
2589 rtx addr
= XEXP (mem
, 0);
2591 if (! (mask
& 0xff))
2593 addr
= plus_constant (Pmode
, addr
, 1);
2596 mem
= gen_rtx_MEM (QImode
, addr
);
2600 XEXP (cond
, 0) = gen_rtx_SIGN_EXTEND (HImode
, mem
);
2602 XEXP (cond
, 0) = gen_rtx_AND (and_mode
, mem
, GEN_INT (mask
));
2604 INSN_CODE (insn
) = -1;
2607 if (and_insn
!= insn
)
2608 delete_insn (and_insn
);
2610 if (shift
!= NULL_RTX
)
2611 delete_insn (shift
);
2615 xstormy16_reorg (void)
2619 for (insn
= get_insns (); insn
; insn
= NEXT_INSN (insn
))
2621 if (! JUMP_P (insn
))
2627 /* Worker function for TARGET_RETURN_IN_MEMORY. */
2630 xstormy16_return_in_memory (const_tree type
, const_tree fntype ATTRIBUTE_UNUSED
)
2632 const HOST_WIDE_INT size
= int_size_in_bytes (type
);
2633 return (size
== -1 || size
> UNITS_PER_WORD
* NUM_ARGUMENT_REGISTERS
);
2636 /* Implement TARGET_HARD_REGNO_MODE_OK. */
2639 xstormy16_hard_regno_mode_ok (unsigned int regno
, machine_mode mode
)
2641 return regno
!= 16 || mode
== BImode
;
2644 /* Implement TARGET_MODES_TIEABLE_P. */
2647 xstormy16_modes_tieable_p (machine_mode mode1
, machine_mode mode2
)
2649 return mode1
!= BImode
&& mode2
!= BImode
;
2652 /* Implement PUSH_ROUNDING. */
2655 xstormy16_push_rounding (poly_int64 bytes
)
2657 return (bytes
+ 1) & ~1;
2660 #undef TARGET_ASM_ALIGNED_HI_OP
2661 #define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
2662 #undef TARGET_ASM_ALIGNED_SI_OP
2663 #define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
2664 #undef TARGET_ENCODE_SECTION_INFO
2665 #define TARGET_ENCODE_SECTION_INFO xstormy16_encode_section_info
2667 /* Select_section doesn't handle .bss_below100. */
2668 #undef TARGET_HAVE_SWITCHABLE_BSS_SECTIONS
2669 #define TARGET_HAVE_SWITCHABLE_BSS_SECTIONS false
2671 #undef TARGET_ASM_OUTPUT_MI_THUNK
2672 #define TARGET_ASM_OUTPUT_MI_THUNK xstormy16_asm_output_mi_thunk
2673 #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
2674 #define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
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
2681 #undef TARGET_MEMORY_MOVE_COST
2682 #define TARGET_MEMORY_MOVE_COST xstormy16_memory_move_cost
2683 #undef TARGET_RTX_COSTS
2684 #define TARGET_RTX_COSTS xstormy16_rtx_costs
2685 #undef TARGET_ADDRESS_COST
2686 #define TARGET_ADDRESS_COST xstormy16_address_cost
2688 #undef TARGET_BUILD_BUILTIN_VA_LIST
2689 #define TARGET_BUILD_BUILTIN_VA_LIST xstormy16_build_builtin_va_list
2690 #undef TARGET_EXPAND_BUILTIN_VA_START
2691 #define TARGET_EXPAND_BUILTIN_VA_START xstormy16_expand_builtin_va_start
2692 #undef TARGET_GIMPLIFY_VA_ARG_EXPR
2693 #define TARGET_GIMPLIFY_VA_ARG_EXPR xstormy16_gimplify_va_arg_expr
2695 #undef TARGET_PROMOTE_FUNCTION_MODE
2696 #define TARGET_PROMOTE_FUNCTION_MODE default_promote_function_mode_always_promote
2697 #undef TARGET_PROMOTE_PROTOTYPES
2698 #define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
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
2705 #undef TARGET_RETURN_IN_MEMORY
2706 #define TARGET_RETURN_IN_MEMORY xstormy16_return_in_memory
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
2714 #undef TARGET_MACHINE_DEPENDENT_REORG
2715 #define TARGET_MACHINE_DEPENDENT_REORG xstormy16_reorg
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
2723 #define TARGET_LRA_P hook_bool_void_false
2725 #undef TARGET_LEGITIMATE_ADDRESS_P
2726 #define TARGET_LEGITIMATE_ADDRESS_P xstormy16_legitimate_address_p
2727 #undef TARGET_MODE_DEPENDENT_ADDRESS_P
2728 #define TARGET_MODE_DEPENDENT_ADDRESS_P xstormy16_mode_dependent_address_p
2730 #undef TARGET_CAN_ELIMINATE
2731 #define TARGET_CAN_ELIMINATE xstormy16_can_eliminate
2733 #undef TARGET_TRAMPOLINE_INIT
2734 #define TARGET_TRAMPOLINE_INIT xstormy16_trampoline_init
2736 #undef TARGET_HARD_REGNO_MODE_OK
2737 #define TARGET_HARD_REGNO_MODE_OK xstormy16_hard_regno_mode_ok
2738 #undef TARGET_MODES_TIEABLE_P
2739 #define TARGET_MODES_TIEABLE_P xstormy16_modes_tieable_p
2741 #undef TARGET_CONSTANT_ALIGNMENT
2742 #define TARGET_CONSTANT_ALIGNMENT constant_alignment_word_strings
2744 #undef TARGET_HAVE_SPECULATION_SAFE_VALUE
2745 #define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed
2747 struct gcc_target targetm
= TARGET_INITIALIZER
;
2749 #include "gt-stormy16.h"