1 /* Xstormy16 target functions.
2 Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002
3 Free Software Foundation, Inc.
4 Contributed by Red Hat, Inc.
6 This file is part of GNU CC.
8 GNU CC is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
13 GNU CC is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GNU CC; see the file COPYING. If not, write to
20 the Free Software Foundation, 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA. */
27 #include "hard-reg-set.h"
29 #include "insn-config.h"
30 #include "conditions.h"
31 #include "insn-flags.h"
33 #include "insn-attr.h"
45 #include "target-def.h"
47 #include "langhooks.h"
49 static rtx emit_addhi3_postreload PARAMS ((rtx, rtx, rtx));
50 static void xstormy16_asm_out_constructor PARAMS ((rtx, int));
51 static void xstormy16_asm_out_destructor PARAMS ((rtx, int));
52 static void xstormy16_encode_section_info PARAMS ((tree, int));
54 /* Define the information needed to generate branch and scc insns. This is
55 stored from the compare operation. */
56 struct rtx_def * xstormy16_compare_op0;
57 struct rtx_def * xstormy16_compare_op1;
59 /* Return 1 if this is a LT, GE, LTU, or GEU operator. */
62 xstormy16_ineqsi_operator (op, mode)
64 enum machine_mode mode;
66 enum rtx_code code = GET_CODE (op);
68 return ((mode == VOIDmode || GET_MODE (op) == mode)
69 && (code == LT || code == GE || code == LTU || code == GEU));
72 /* Return 1 if this is an EQ or NE operator. */
75 equality_operator (op, mode)
77 enum machine_mode mode;
79 return ((mode == VOIDmode || GET_MODE (op) == mode)
80 && (GET_CODE (op) == EQ || GET_CODE (op) == NE));
83 /* Return 1 if this is a comparison operator but not an EQ or NE operator. */
86 inequality_operator (op, mode)
88 enum machine_mode mode;
90 return comparison_operator (op, mode) && ! equality_operator (op, mode);
93 /* Branches are handled as follows:
95 1. HImode compare-and-branches. The machine supports these
96 natively, so the appropriate pattern is emitted directly.
98 2. SImode EQ and NE. These are emitted as pairs of HImode
101 3. SImode LT, GE, LTU and GEU. These are emitted as a sequence
102 of a SImode subtract followed by a branch (not a compare-and-branch),
108 4. SImode GT, LE, GTU, LEU. These are emitted as a sequence like:
116 /* Emit a branch of kind CODE to location LOC. */
119 xstormy16_emit_cbranch (code, loc)
123 rtx op0 = xstormy16_compare_op0;
124 rtx op1 = xstormy16_compare_op1;
125 rtx condition_rtx, loc_ref, branch, cy_clobber;
127 enum machine_mode mode;
129 mode = GET_MODE (op0);
130 if (mode != HImode && mode != SImode)
134 && (code == GT || code == LE || code == GTU || code == LEU))
136 int unsigned_p = (code == GTU || code == LEU);
137 int gt_p = (code == GT || code == GTU);
141 lab = gen_label_rtx ();
142 xstormy16_emit_cbranch (unsigned_p ? LTU : LT, gt_p ? lab : loc);
143 /* This should be generated as a comparison against the temporary
144 created by the previous insn, but reload can't handle that. */
145 xstormy16_emit_cbranch (gt_p ? NE : EQ, loc);
150 else if (mode == SImode
151 && (code == NE || code == EQ)
152 && op1 != const0_rtx)
155 int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
159 lab = gen_label_rtx ();
161 for (i = 0; i < num_words - 1; i++)
163 xstormy16_compare_op0 = simplify_gen_subreg (word_mode, op0, mode,
165 xstormy16_compare_op1 = simplify_gen_subreg (word_mode, op1, mode,
167 xstormy16_emit_cbranch (NE, code == EQ ? lab : loc);
169 xstormy16_compare_op0 = simplify_gen_subreg (word_mode, op0, mode,
171 xstormy16_compare_op1 = simplify_gen_subreg (word_mode, op1, mode,
173 xstormy16_emit_cbranch (code, loc);
180 /* We can't allow reload to try to generate any reload after a branch,
181 so when some register must match we must make the temporary ourselves. */
185 tmp = gen_reg_rtx (mode);
186 emit_move_insn (tmp, op0);
190 condition_rtx = gen_rtx (code, mode, op0, op1);
191 loc_ref = gen_rtx_LABEL_REF (VOIDmode, loc);
192 branch = gen_rtx_SET (VOIDmode, pc_rtx,
193 gen_rtx_IF_THEN_ELSE (VOIDmode, condition_rtx,
196 cy_clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (BImode));
199 vec = gen_rtvec (2, branch, cy_clobber);
200 else if (code == NE || code == EQ)
201 vec = gen_rtvec (2, branch, gen_rtx_CLOBBER (VOIDmode, op0));
206 sub = gen_rtx_SET (VOIDmode, op0, gen_rtx_MINUS (SImode, op0, op1));
208 sub = gen_rtx_CLOBBER (SImode, op0);
210 vec = gen_rtvec (3, branch, sub, cy_clobber);
213 emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec));
216 /* Take a SImode conditional branch, one of GT/LE/GTU/LEU, and split
217 the arithmetic operation. Most of the work is done by
218 xstormy16_expand_arith. */
221 xstormy16_split_cbranch (mode, label, comparison, dest, carry)
222 enum machine_mode mode;
228 rtx op0 = XEXP (comparison, 0);
229 rtx op1 = XEXP (comparison, 1);
234 xstormy16_expand_arith (mode, COMPARE, dest, op0, op1, carry);
242 while (NEXT_INSN (last_insn) != NULL_RTX)
243 last_insn = NEXT_INSN (last_insn);
245 compare = SET_SRC (XVECEXP (PATTERN (last_insn), 0, 0));
246 PUT_CODE (XEXP (compare, 0), GET_CODE (comparison));
247 XEXP (compare, 1) = gen_rtx_LABEL_REF (VOIDmode, label);
252 /* Return the string to output a conditional branch to LABEL, which is
253 the operand number of the label.
255 OP is the conditional expression, or NULL for branch-always.
257 REVERSED is non-zero if we should reverse the sense of the comparison.
262 xstormy16_output_cbranch_hi (op, label, reversed, insn)
268 static char string[64];
269 int need_longbranch = (op != NULL_RTX
270 ? get_attr_length (insn) == 8
271 : get_attr_length (insn) == 4);
272 int really_reversed = reversed ^ need_longbranch;
274 const char *template;
275 const char *operands;
284 sprintf (string, "%s %s", ccode, label);
288 code = GET_CODE (op);
290 if (GET_CODE (XEXP (op, 0)) != REG)
292 code = swap_condition (code);
298 /* Work out which way this really branches. */
300 code = reverse_condition (code);
304 case EQ: ccode = "z"; break;
305 case NE: ccode = "nz"; break;
306 case GE: ccode = "ge"; break;
307 case LT: ccode = "lt"; break;
308 case GT: ccode = "gt"; break;
309 case LE: ccode = "le"; break;
310 case GEU: ccode = "nc"; break;
311 case LTU: ccode = "c"; break;
312 case GTU: ccode = "hi"; break;
313 case LEU: ccode = "ls"; break;
320 template = "b%s %s,.+8 | jmpf %s";
322 template = "b%s %s,%s";
323 sprintf (string, template, ccode, operands, label);
328 /* Return the string to output a conditional branch to LABEL, which is
329 the operand number of the label, but suitable for the tail of a
332 OP is the conditional expression (OP is never NULL_RTX).
334 REVERSED is non-zero if we should reverse the sense of the comparison.
339 xstormy16_output_cbranch_si (op, label, reversed, insn)
345 static char string[64];
346 int need_longbranch = get_attr_length (insn) >= 8;
347 int really_reversed = reversed ^ need_longbranch;
349 const char *template;
353 code = GET_CODE (op);
355 /* Work out which way this really branches. */
357 code = reverse_condition (code);
361 case EQ: ccode = "z"; break;
362 case NE: ccode = "nz"; break;
363 case GE: ccode = "ge"; break;
364 case LT: ccode = "lt"; break;
365 case GEU: ccode = "nc"; break;
366 case LTU: ccode = "c"; break;
368 /* The missing codes above should never be generated. */
379 if (GET_CODE (XEXP (op, 0)) != REG)
382 regnum = REGNO (XEXP (op, 0));
383 sprintf (prevop, "or %s,%s", reg_names[regnum], reg_names[regnum+1]);
387 case GE: case LT: case GEU: case LTU:
388 strcpy (prevop, "sbc %2,%3");
396 template = "%s | b%s .+6 | jmpf %s";
398 template = "%s | b%s %s";
399 sprintf (string, template, prevop, ccode, label);
404 /* Many machines have some registers that cannot be copied directly to or from
405 memory or even from other types of registers. An example is the `MQ'
406 register, which on most machines, can only be copied to or from general
407 registers, but not memory. Some machines allow copying all registers to and
408 from memory, but require a scratch register for stores to some memory
409 locations (e.g., those with symbolic address on the RT, and those with
410 certain symbolic address on the SPARC when compiling PIC). In some cases,
411 both an intermediate and a scratch register are required.
413 You should define these macros to indicate to the reload phase that it may
414 need to allocate at least one register for a reload in addition to the
415 register to contain the data. Specifically, if copying X to a register
416 CLASS in MODE requires an intermediate register, you should define
417 `SECONDARY_INPUT_RELOAD_CLASS' to return the largest register class all of
418 whose registers can be used as intermediate registers or scratch registers.
420 If copying a register CLASS in MODE to X requires an intermediate or scratch
421 register, `SECONDARY_OUTPUT_RELOAD_CLASS' should be defined to return the
422 largest register class required. If the requirements for input and output
423 reloads are the same, the macro `SECONDARY_RELOAD_CLASS' should be used
424 instead of defining both macros identically.
426 The values returned by these macros are often `GENERAL_REGS'. Return
427 `NO_REGS' if no spare register is needed; i.e., if X can be directly copied
428 to or from a register of CLASS in MODE without requiring a scratch register.
429 Do not define this macro if it would always return `NO_REGS'.
431 If a scratch register is required (either with or without an intermediate
432 register), you should define patterns for `reload_inM' or `reload_outM', as
433 required.. These patterns, which will normally be implemented with a
434 `define_expand', should be similar to the `movM' patterns, except that
435 operand 2 is the scratch register.
437 Define constraints for the reload register and scratch register that contain
438 a single register class. If the original reload register (whose class is
439 CLASS) can meet the constraint given in the pattern, the value returned by
440 these macros is used for the class of the scratch register. Otherwise, two
441 additional reload registers are required. Their classes are obtained from
442 the constraints in the insn pattern.
444 X might be a pseudo-register or a `subreg' of a pseudo-register, which could
445 either be in a hard register or in memory. Use `true_regnum' to find out;
446 it will return -1 if the pseudo is in memory and the hard register number if
449 These macros should not be used in the case where a particular class of
450 registers can only be copied to memory and not to another class of
451 registers. In that case, secondary reload registers are not needed and
452 would not be helpful. Instead, a stack location must be used to perform the
453 copy and the `movM' pattern should use memory as an intermediate storage.
454 This case often occurs between floating-point and general registers. */
457 xstormy16_secondary_reload_class (class, mode, x)
458 enum reg_class class;
459 enum machine_mode mode;
462 /* This chip has the interesting property that only the first eight
463 registers can be moved to/from memory. */
464 if ((GET_CODE (x) == MEM
465 || ((GET_CODE (x) == SUBREG || GET_CODE (x) == REG)
466 && (true_regnum (x) == -1
467 || true_regnum (x) >= FIRST_PSEUDO_REGISTER)))
468 && ! reg_class_subset_p (class, EIGHT_REGS))
471 /* When reloading a PLUS, the carry register will be required
472 unless the inc or dec instructions can be used. */
473 if (xstormy16_carry_plus_operand (x, mode))
479 /* Recognize a PLUS that needs the carry register. */
481 xstormy16_carry_plus_operand (x, mode)
483 enum machine_mode mode ATTRIBUTE_UNUSED;
485 return (GET_CODE (x) == PLUS
486 && GET_CODE (XEXP (x, 1)) == CONST_INT
487 && (INTVAL (XEXP (x, 1)) < -4 || INTVAL (XEXP (x, 1)) > 4));
492 xstormy16_preferred_reload_class (x, class)
493 enum reg_class class;
496 if (class == GENERAL_REGS
497 && GET_CODE (x) == MEM)
503 #define LEGITIMATE_ADDRESS_INTEGER_P(X, OFFSET) \
504 (GET_CODE (X) == CONST_INT \
505 && (unsigned HOST_WIDE_INT) (INTVAL (X) + (OFFSET) + 2048) < 4096)
507 #define LEGITIMATE_ADDRESS_CONST_INT_P(X, OFFSET) \
508 (GET_CODE (X) == CONST_INT \
509 && INTVAL (X) + (OFFSET) >= 0 \
510 && INTVAL (X) + (OFFSET) < 0x8000 \
511 && (INTVAL (X) + (OFFSET) < 0x100 || INTVAL (X) + (OFFSET) >= 0x7F00))
514 xstormy16_legitimate_address_p (mode, x, strict)
515 enum machine_mode mode ATTRIBUTE_UNUSED;
519 if (LEGITIMATE_ADDRESS_CONST_INT_P (x, 0))
522 if (GET_CODE (x) == PLUS
523 && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 0))
526 if (GET_CODE (x) == POST_INC
527 || GET_CODE (x) == PRE_DEC)
530 if (GET_CODE (x) == REG && REGNO_OK_FOR_BASE_P (REGNO (x))
531 && (! strict || REGNO (x) < FIRST_PSEUDO_REGISTER))
537 /* Return nonzero if memory address X (an RTX) can have different
538 meanings depending on the machine mode of the memory reference it
539 is used for or if the address is valid for some modes but not
542 Autoincrement and autodecrement addresses typically have mode-dependent
543 effects because the amount of the increment or decrement is the size of the
544 operand being addressed. Some machines have other mode-dependent addresses.
545 Many RISC machines have no mode-dependent addresses.
547 You may assume that ADDR is a valid address for the machine.
549 On this chip, this is true if the address is valid with an offset
550 of 0 but not of 6, because in that case it cannot be used as an
551 address for DImode or DFmode, or if the address is a post-increment
552 or pre-decrement address. */
554 xstormy16_mode_dependent_address_p (x)
557 if (LEGITIMATE_ADDRESS_CONST_INT_P (x, 0)
558 && ! LEGITIMATE_ADDRESS_CONST_INT_P (x, 6))
561 if (GET_CODE (x) == PLUS
562 && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 0)
563 && ! LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 6))
566 if (GET_CODE (x) == PLUS)
569 if (GET_CODE (x) == POST_INC
570 || GET_CODE (x) == PRE_DEC)
576 /* A C expression that defines the optional machine-dependent constraint
577 letters (`Q', `R', `S', `T', `U') that can be used to segregate specific
578 types of operands, usually memory references, for the target machine.
579 Normally this macro will not be defined. If it is required for a particular
580 target machine, it should return 1 if VALUE corresponds to the operand type
581 represented by the constraint letter C. If C is not defined as an extra
582 constraint, the value returned should be 0 regardless of VALUE. */
584 xstormy16_extra_constraint_p (x, c)
590 /* 'Q' is for pushes. */
592 return (GET_CODE (x) == MEM
593 && GET_CODE (XEXP (x, 0)) == POST_INC
594 && XEXP (XEXP (x, 0), 0) == stack_pointer_rtx);
596 /* 'R' is for pops. */
598 return (GET_CODE (x) == MEM
599 && GET_CODE (XEXP (x, 0)) == PRE_DEC
600 && XEXP (XEXP (x, 0), 0) == stack_pointer_rtx);
602 /* 'S' is for immediate memory addresses. */
604 return (GET_CODE (x) == MEM
605 && GET_CODE (XEXP (x, 0)) == CONST_INT
606 && xstormy16_legitimate_address_p (VOIDmode, XEXP (x, 0), 0));
610 /* Not implemented yet. */
613 /* 'U' is for CONST_INT values not between 2 and 15 inclusive,
614 for allocating a scratch register for 32-bit shifts. */
616 return (GET_CODE (x) == CONST_INT
617 && (INTVAL (x) < 2 || INTVAL (x) > 15));
625 short_memory_operand (x, mode)
627 enum machine_mode mode;
629 if (! memory_operand (x, mode))
631 return (GET_CODE (XEXP (x, 0)) != PLUS);
635 nonimmediate_nonstack_operand (op, mode)
637 enum machine_mode mode;
639 /* 'Q' is for pushes, 'R' for pops. */
640 return (nonimmediate_operand (op, mode)
641 && ! xstormy16_extra_constraint_p (op, 'Q')
642 && ! xstormy16_extra_constraint_p (op, 'R'));
645 /* Splitter for the 'move' patterns, for modes not directly implemeted
646 by hardware. Emit insns to copy a value of mode MODE from SRC to
649 This function is only called when reload_completed.
653 xstormy16_split_move (mode, dest, src)
654 enum machine_mode mode;
658 int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
659 int direction, end, i;
660 int src_modifies = 0;
661 int dest_modifies = 0;
662 int src_volatile = 0;
663 int dest_volatile = 0;
665 rtx auto_inc_reg_rtx = NULL_RTX;
667 /* Check initial conditions. */
668 if (! reload_completed
669 || mode == QImode || mode == HImode
670 || ! nonimmediate_operand (dest, mode)
671 || ! general_operand (src, mode))
674 /* This case is not supported below, and shouldn't be generated. */
675 if (GET_CODE (dest) == MEM
676 && GET_CODE (src) == MEM)
679 /* This case is very very bad after reload, so trap it now. */
680 if (GET_CODE (dest) == SUBREG
681 || GET_CODE (src) == SUBREG)
684 /* The general idea is to copy by words, offsetting the source and
685 destination. Normally the least-significant word will be copied
686 first, but for pre-dec operations it's better to copy the
687 most-significant word first. Only one operand can be a pre-dec
690 It's also possible that the copy overlaps so that the direction
694 if (GET_CODE (dest) == MEM)
696 mem_operand = XEXP (dest, 0);
697 dest_modifies = side_effects_p (mem_operand);
698 if (auto_inc_p (mem_operand))
699 auto_inc_reg_rtx = XEXP (mem_operand, 0);
700 dest_volatile = MEM_VOLATILE_P (dest);
703 dest = copy_rtx (dest);
704 MEM_VOLATILE_P (dest) = 0;
707 else if (GET_CODE (src) == MEM)
709 mem_operand = XEXP (src, 0);
710 src_modifies = side_effects_p (mem_operand);
711 if (auto_inc_p (mem_operand))
712 auto_inc_reg_rtx = XEXP (mem_operand, 0);
713 src_volatile = MEM_VOLATILE_P (src);
716 src = copy_rtx (src);
717 MEM_VOLATILE_P (src) = 0;
721 mem_operand = NULL_RTX;
723 if (mem_operand == NULL_RTX)
725 if (GET_CODE (src) == REG
726 && GET_CODE (dest) == REG
727 && reg_overlap_mentioned_p (dest, src)
728 && REGNO (dest) > REGNO (src))
731 else if (GET_CODE (mem_operand) == PRE_DEC
732 || (GET_CODE (mem_operand) == PLUS
733 && GET_CODE (XEXP (mem_operand, 0)) == PRE_DEC))
735 else if (GET_CODE (src) == MEM
736 && reg_overlap_mentioned_p (dest, src))
739 if (GET_CODE (dest) != REG)
741 regno = REGNO (dest);
743 if (! refers_to_regno_p (regno, regno + num_words, mem_operand, 0))
746 if (refers_to_regno_p (regno, regno + 1, mem_operand, 0))
748 else if (refers_to_regno_p (regno + num_words - 1, regno + num_words,
752 /* This means something like
753 (set (reg:DI r0) (mem:DI (reg:HI r1)))
754 which we'd need to support by doing the set of the second word
759 end = direction < 0 ? -1 : num_words;
760 for (i = direction < 0 ? num_words - 1 : 0; i != end; i += direction)
762 rtx w_src, w_dest, insn;
765 w_src = gen_rtx_MEM (word_mode, mem_operand);
767 w_src = simplify_gen_subreg (word_mode, src, mode, i * UNITS_PER_WORD);
769 MEM_VOLATILE_P (w_src) = 1;
771 w_dest = gen_rtx_MEM (word_mode, mem_operand);
773 w_dest = simplify_gen_subreg (word_mode, dest, mode,
776 MEM_VOLATILE_P (w_dest) = 1;
778 /* The simplify_subreg calls must always be able to simplify. */
779 if (GET_CODE (w_src) == SUBREG
780 || GET_CODE (w_dest) == SUBREG)
783 insn = emit_insn (gen_rtx_SET (VOIDmode, w_dest, w_src));
784 if (auto_inc_reg_rtx)
785 REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
791 /* Expander for the 'move' patterns. Emit insns to copy a value of
792 mode MODE from SRC to DEST. */
795 xstormy16_expand_move (mode, dest, src)
796 enum machine_mode mode;
800 /* There are only limited immediate-to-memory move instructions. */
801 if (! reload_in_progress
802 && ! reload_completed
803 && GET_CODE (dest) == MEM
804 && (GET_CODE (XEXP (dest, 0)) != CONST_INT
805 || ! xstormy16_legitimate_address_p (mode, XEXP (dest, 0), 0))
806 && GET_CODE (src) != REG
807 && GET_CODE (src) != SUBREG)
808 src = copy_to_mode_reg (mode, src);
810 /* Don't emit something we would immediately split. */
812 && mode != HImode && mode != QImode)
814 xstormy16_split_move (mode, dest, src);
818 emit_insn (gen_rtx_SET (VOIDmode, dest, src));
824 The stack is laid out as follows:
828 Register save area (up to 4 words)
829 Argument register save area for stdarg (NUM_ARGUMENT_REGISTERS words)
831 AP-> Return address (two words)
832 9th procedure parameter word
833 10th procedure parameter word
835 last procedure parameter word
837 The frame pointer location is tuned to make it most likely that all
838 parameters and local variables can be accessed using a load-indexed
841 /* A structure to describe the layout. */
842 struct xstormy16_stack_layout
844 /* Size of the topmost three items on the stack. */
846 int register_save_size;
847 int stdarg_save_size;
848 /* Sum of the above items. */
850 /* Various offsets. */
851 int first_local_minus_ap;
856 /* Does REGNO need to be saved? */
857 #define REG_NEEDS_SAVE(REGNUM, IFUN) \
858 ((regs_ever_live[REGNUM] && ! call_used_regs[REGNUM]) \
859 || (IFUN && ! fixed_regs[REGNUM] && call_used_regs[REGNUM] \
860 && (regs_ever_live[REGNUM] || ! current_function_is_leaf)))
862 /* Compute the stack layout. */
863 struct xstormy16_stack_layout
864 xstormy16_compute_stack_layout ()
866 struct xstormy16_stack_layout layout;
868 const int ifun = xstormy16_interrupt_function_p ();
870 layout.locals_size = get_frame_size ();
872 layout.register_save_size = 0;
873 for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
874 if (REG_NEEDS_SAVE (regno, ifun))
875 layout.register_save_size += UNITS_PER_WORD;
877 if (current_function_stdarg)
878 layout.stdarg_save_size = NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD;
880 layout.stdarg_save_size = 0;
882 layout.frame_size = (layout.locals_size
883 + layout.register_save_size
884 + layout.stdarg_save_size);
886 if (current_function_args_size <= 2048 && current_function_args_size != -1)
888 if (layout.frame_size + INCOMING_FRAME_SP_OFFSET
889 + current_function_args_size <= 2048)
890 layout.fp_minus_ap = layout.frame_size + INCOMING_FRAME_SP_OFFSET;
892 layout.fp_minus_ap = 2048 - current_function_args_size;
895 layout.fp_minus_ap = (layout.stdarg_save_size
896 + layout.register_save_size
897 + INCOMING_FRAME_SP_OFFSET);
898 layout.sp_minus_fp = (layout.frame_size + INCOMING_FRAME_SP_OFFSET
899 - layout.fp_minus_ap);
900 layout.first_local_minus_ap = layout.sp_minus_fp - layout.locals_size;
904 /* Determine how all the special registers get eliminated. */
906 xstormy16_initial_elimination_offset (from, to)
909 struct xstormy16_stack_layout layout;
912 layout = xstormy16_compute_stack_layout ();
914 if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
915 result = layout.sp_minus_fp - layout.locals_size;
916 else if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
917 result = -layout.locals_size;
918 else if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
919 result = -layout.fp_minus_ap;
920 else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
921 result = -(layout.sp_minus_fp + layout.fp_minus_ap);
929 emit_addhi3_postreload (dest, src0, src1)
934 rtx set, clobber, insn;
936 set = gen_rtx_SET (VOIDmode, dest, gen_rtx_PLUS (HImode, src0, src1));
937 clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (BImode, 16));
938 insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber)));
942 /* Called after register allocation to add any instructions needed for
943 the prologue. Using a prologue insn is favored compared to putting
944 all of the instructions in the TARGET_ASM_FUNCTION_PROLOGUE macro,
945 since it allows the scheduler to intermix instructions with the
946 saves of the caller saved registers. In some cases, it might be
947 necessary to emit a barrier instruction as the last insn to prevent
950 Also any insns generated here should have RTX_FRAME_RELATED_P(insn) = 1
951 so that the debug info generation code can handle them properly. */
953 xstormy16_expand_prologue ()
955 struct xstormy16_stack_layout layout;
959 rtx mem_fake_push_rtx;
960 const int ifun = xstormy16_interrupt_function_p ();
962 mem_push_rtx = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
963 mem_push_rtx = gen_rtx_MEM (HImode, mem_push_rtx);
964 mem_fake_push_rtx = gen_rtx_PRE_INC (Pmode, stack_pointer_rtx);
965 mem_fake_push_rtx = gen_rtx_MEM (HImode, mem_fake_push_rtx);
967 layout = xstormy16_compute_stack_layout ();
969 /* Save the argument registers if necessary. */
970 if (layout.stdarg_save_size)
971 for (regno = FIRST_ARGUMENT_REGISTER;
972 regno < FIRST_ARGUMENT_REGISTER + NUM_ARGUMENT_REGISTERS;
975 rtx reg = gen_rtx_REG (HImode, regno);
976 insn = emit_move_insn (mem_push_rtx, reg);
977 RTX_FRAME_RELATED_P (insn) = 1;
978 REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
979 gen_rtx_SET (VOIDmode,
985 /* Push each of the registers to save. */
986 for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
987 if (REG_NEEDS_SAVE (regno, ifun))
989 rtx reg = gen_rtx_REG (HImode, regno);
990 insn = emit_move_insn (mem_push_rtx, reg);
991 RTX_FRAME_RELATED_P (insn) = 1;
992 REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
993 gen_rtx_SET (VOIDmode,
999 /* It's just possible that the SP here might be what we need for
1001 if (frame_pointer_needed && layout.sp_minus_fp == layout.locals_size)
1003 insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
1004 RTX_FRAME_RELATED_P (insn) = 1;
1007 /* Allocate space for local variables. */
1008 if (layout.locals_size)
1010 insn = emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx,
1011 GEN_INT (layout.locals_size));
1012 RTX_FRAME_RELATED_P (insn) = 1;
1015 /* Set up the frame pointer, if required. */
1016 if (frame_pointer_needed && layout.sp_minus_fp != layout.locals_size)
1018 insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
1019 RTX_FRAME_RELATED_P (insn) = 1;
1020 if (layout.sp_minus_fp)
1022 insn = emit_addhi3_postreload (hard_frame_pointer_rtx,
1023 hard_frame_pointer_rtx,
1024 GEN_INT (-layout.sp_minus_fp));
1025 RTX_FRAME_RELATED_P (insn) = 1;
1030 /* Do we need an epilogue at all? */
1034 return (reload_completed
1035 && xstormy16_compute_stack_layout ().frame_size == 0);
1038 /* Called after register allocation to add any instructions needed for
1039 the epilogue. Using an epilogue insn is favored compared to putting
1040 all of the instructions in the TARGET_ASM_FUNCTION_PROLOGUE macro,
1041 since it allows the scheduler to intermix instructions with the
1042 saves of the caller saved registers. In some cases, it might be
1043 necessary to emit a barrier instruction as the last insn to prevent
1047 xstormy16_expand_epilogue ()
1049 struct xstormy16_stack_layout layout;
1052 const int ifun = xstormy16_interrupt_function_p ();
1054 mem_pop_rtx = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
1055 mem_pop_rtx = gen_rtx_MEM (HImode, mem_pop_rtx);
1057 layout = xstormy16_compute_stack_layout ();
1059 /* Pop the stack for the locals. */
1060 if (layout.locals_size)
1062 if (frame_pointer_needed && layout.sp_minus_fp == layout.locals_size)
1063 emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
1065 emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx,
1066 GEN_INT (- layout.locals_size));
1069 /* Restore any call-saved registers. */
1070 for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno--)
1071 if (REG_NEEDS_SAVE (regno, ifun))
1072 emit_move_insn (gen_rtx_REG (HImode, regno), mem_pop_rtx);
1074 /* Pop the stack for the stdarg save area. */
1075 if (layout.stdarg_save_size)
1076 emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx,
1077 GEN_INT (- layout.stdarg_save_size));
1081 emit_jump_insn (gen_return_internal_interrupt ());
1083 emit_jump_insn (gen_return_internal ());
1087 xstormy16_epilogue_uses (regno)
1090 if (reload_completed && call_used_regs[regno])
1092 const int ifun = xstormy16_interrupt_function_p ();
1093 return REG_NEEDS_SAVE (regno, ifun);
1098 /* Return an updated summarizer variable CUM to advance past an
1099 argument in the argument list. The values MODE, TYPE and NAMED
1100 describe that argument. Once this is done, the variable CUM is
1101 suitable for analyzing the *following* argument with
1102 `FUNCTION_ARG', etc.
1104 This function need not do anything if the argument in question was
1105 passed on the stack. The compiler knows how to track the amount of
1106 stack space used for arguments without any special help. However,
1107 it makes life easier for xstormy16_build_va_list if it does update
1110 xstormy16_function_arg_advance (cum, mode, type, named)
1111 CUMULATIVE_ARGS cum;
1112 enum machine_mode mode;
1114 int named ATTRIBUTE_UNUSED;
1116 /* If an argument would otherwise be passed partially in registers,
1117 and partially on the stack, the whole of it is passed on the
1119 if (cum < NUM_ARGUMENT_REGISTERS
1120 && cum + XSTORMY16_WORD_SIZE (type, mode) > NUM_ARGUMENT_REGISTERS)
1121 cum = NUM_ARGUMENT_REGISTERS;
1123 cum += XSTORMY16_WORD_SIZE (type, mode);
1128 /* Do any needed setup for a variadic function. CUM has not been updated
1129 for the last named argument which has type TYPE and mode MODE. */
1131 xstormy16_setup_incoming_varargs (cum, int_mode, type, pretend_size)
1132 CUMULATIVE_ARGS cum ATTRIBUTE_UNUSED;
1133 int int_mode ATTRIBUTE_UNUSED;
1134 tree type ATTRIBUTE_UNUSED;
1135 int * pretend_size ATTRIBUTE_UNUSED;
1139 /* Build the va_list type.
1141 For this chip, va_list is a record containing a counter and a pointer.
1142 The counter is of type 'int' and indicates how many bytes
1143 have been used to date. The pointer indicates the stack position
1144 for arguments that have not been passed in registers.
1145 To keep the layout nice, the pointer is first in the structure. */
1148 xstormy16_build_va_list ()
1150 tree f_1, f_2, record, type_decl;
1152 record = (*lang_hooks.types.make_type) (RECORD_TYPE);
1153 type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
1155 f_1 = build_decl (FIELD_DECL, get_identifier ("base"),
1157 f_2 = build_decl (FIELD_DECL, get_identifier ("count"),
1158 unsigned_type_node);
1160 DECL_FIELD_CONTEXT (f_1) = record;
1161 DECL_FIELD_CONTEXT (f_2) = record;
1163 TREE_CHAIN (record) = type_decl;
1164 TYPE_NAME (record) = type_decl;
1165 TYPE_FIELDS (record) = f_1;
1166 TREE_CHAIN (f_1) = f_2;
1168 layout_type (record);
1173 /* Implement the stdarg/varargs va_start macro. STDARG_P is non-zero if this
1174 is stdarg.h instead of varargs.h. VALIST is the tree of the va_list
1175 variable to initialize. NEXTARG is the machine independent notion of the
1176 'next' argument after the variable arguments. */
1178 xstormy16_expand_builtin_va_start (valist, nextarg)
1180 rtx nextarg ATTRIBUTE_UNUSED;
1182 tree f_base, f_count;
1186 if (xstormy16_interrupt_function_p ())
1187 error ("cannot use va_start in interrupt function");
1189 f_base = TYPE_FIELDS (va_list_type_node);
1190 f_count = TREE_CHAIN (f_base);
1192 base = build (COMPONENT_REF, TREE_TYPE (f_base), valist, f_base);
1193 count = build (COMPONENT_REF, TREE_TYPE (f_count), valist, f_count);
1195 t = make_tree (TREE_TYPE (base), virtual_incoming_args_rtx);
1196 t = build (PLUS_EXPR, TREE_TYPE (base), t,
1197 build_int_2 (INCOMING_FRAME_SP_OFFSET, 0));
1198 t = build (MODIFY_EXPR, TREE_TYPE (base), base, t);
1199 TREE_SIDE_EFFECTS (t) = 1;
1200 expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1202 t = build (MODIFY_EXPR, TREE_TYPE (count), count,
1203 build_int_2 (current_function_args_info * UNITS_PER_WORD, 0));
1204 TREE_SIDE_EFFECTS (t) = 1;
1205 expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1208 /* Implement the stdarg/varargs va_arg macro. VALIST is the variable
1209 of type va_list as a tree, TYPE is the type passed to va_arg.
1210 Note: This algorithm is documented in stormy-abi. */
1213 xstormy16_expand_builtin_va_arg (valist, type)
1217 tree f_base, f_count;
1219 rtx count_rtx, addr_rtx, r;
1220 rtx lab_gotaddr, lab_fromstack;
1222 int size, size_of_reg_args;
1223 tree size_tree, count_plus_size;
1224 rtx count_plus_size_rtx;
1226 f_base = TYPE_FIELDS (va_list_type_node);
1227 f_count = TREE_CHAIN (f_base);
1229 base = build (COMPONENT_REF, TREE_TYPE (f_base), valist, f_base);
1230 count = build (COMPONENT_REF, TREE_TYPE (f_count), valist, f_count);
1232 size = PUSH_ROUNDING (int_size_in_bytes (type));
1233 size_tree = round_up (size_in_bytes (type), UNITS_PER_WORD);
1235 size_of_reg_args = NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD;
1237 count_rtx = expand_expr (count, NULL_RTX, HImode, EXPAND_NORMAL);
1238 lab_gotaddr = gen_label_rtx ();
1239 lab_fromstack = gen_label_rtx ();
1240 addr_rtx = gen_reg_rtx (Pmode);
1242 count_plus_size = build (PLUS_EXPR, TREE_TYPE (count), count, size_tree);
1243 count_plus_size_rtx = expand_expr (count_plus_size, NULL_RTX, HImode, EXPAND_NORMAL);
1244 emit_cmp_and_jump_insns (count_plus_size_rtx, GEN_INT (size_of_reg_args),
1245 GTU, const1_rtx, HImode, 1, lab_fromstack);
1247 t = build (PLUS_EXPR, ptr_type_node, base, count);
1248 r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
1250 emit_move_insn (addr_rtx, r);
1252 emit_jump_insn (gen_jump (lab_gotaddr));
1254 emit_label (lab_fromstack);
1256 /* Arguments larger than a word might need to skip over some
1257 registers, since arguments are either passed entirely in
1258 registers or entirely on the stack. */
1259 if (size > 2 || size < 0)
1261 rtx lab_notransition = gen_label_rtx ();
1262 emit_cmp_and_jump_insns (count_rtx, GEN_INT (NUM_ARGUMENT_REGISTERS
1264 GEU, const1_rtx, HImode, 1, lab_notransition);
1266 t = build (MODIFY_EXPR, TREE_TYPE (count), count,
1267 build_int_2 (NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD, 0));
1268 TREE_SIDE_EFFECTS (t) = 1;
1269 expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1271 emit_label (lab_notransition);
1274 t = build (PLUS_EXPR, sizetype, size_tree,
1275 build_int_2 ((- NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD
1276 + INCOMING_FRAME_SP_OFFSET),
1278 t = build (PLUS_EXPR, TREE_TYPE (count), count, fold (t));
1279 t = build (MINUS_EXPR, TREE_TYPE (base), base, t);
1280 r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
1282 emit_move_insn (addr_rtx, r);
1284 emit_label (lab_gotaddr);
1286 count_plus_size = build (PLUS_EXPR, TREE_TYPE (count), count, size_tree);
1287 t = build (MODIFY_EXPR, TREE_TYPE (count), count, count_plus_size);
1288 TREE_SIDE_EFFECTS (t) = 1;
1289 expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1294 /* Initialize the variable parts of a trampoline. ADDR is an RTX for
1295 the address of the trampoline; FNADDR is an RTX for the address of
1296 the nested function; STATIC_CHAIN is an RTX for the static chain
1297 value that should be passed to the function when it is called. */
1299 xstormy16_initialize_trampoline (addr, fnaddr, static_chain)
1304 rtx reg_addr = gen_reg_rtx (Pmode);
1305 rtx temp = gen_reg_rtx (HImode);
1306 rtx reg_fnaddr = gen_reg_rtx (HImode);
1309 reg_addr_mem = gen_rtx_MEM (HImode, reg_addr);
1311 emit_move_insn (reg_addr, addr);
1312 emit_move_insn (temp, GEN_INT (0x3130 | STATIC_CHAIN_REGNUM));
1313 emit_move_insn (reg_addr_mem, temp);
1314 emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx));
1315 emit_move_insn (temp, static_chain);
1316 emit_move_insn (reg_addr_mem, temp);
1317 emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx));
1318 emit_move_insn (reg_fnaddr, fnaddr);
1319 emit_move_insn (temp, reg_fnaddr);
1320 emit_insn (gen_andhi3 (temp, temp, GEN_INT (0xFF)));
1321 emit_insn (gen_iorhi3 (temp, temp, GEN_INT (0x0200)));
1322 emit_move_insn (reg_addr_mem, temp);
1323 emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx));
1324 emit_insn (gen_lshrhi3 (reg_fnaddr, reg_fnaddr, GEN_INT (8)));
1325 emit_move_insn (reg_addr_mem, reg_fnaddr);
1328 /* Create an RTX representing the place where a function returns a
1329 value of data type VALTYPE. VALTYPE is a tree node representing a
1330 data type. Write `TYPE_MODE (VALTYPE)' to get the machine mode
1331 used to represent that type. On many machines, only the mode is
1332 relevant. (Actually, on most machines, scalar values are returned
1333 in the same place regardless of mode).
1335 If `PROMOTE_FUNCTION_RETURN' is defined, you must apply the same promotion
1336 rules specified in `PROMOTE_MODE' if VALTYPE is a scalar type.
1338 If the precise function being called is known, FUNC is a tree node
1339 (`FUNCTION_DECL') for it; otherwise, FUNC is a null pointer. This makes it
1340 possible to use a different value-returning convention for specific
1341 functions when all their calls are known.
1343 `FUNCTION_VALUE' is not used for return vales with aggregate data types,
1344 because these are returned in another way. See `STRUCT_VALUE_REGNUM' and
1347 xstormy16_function_value (valtype, func)
1349 tree func ATTRIBUTE_UNUSED;
1351 enum machine_mode mode;
1352 mode = TYPE_MODE (valtype);
1353 PROMOTE_MODE (mode, 0, valtype);
1354 return gen_rtx_REG (mode, RETURN_VALUE_REGNUM);
1357 /* A C compound statement that outputs the assembler code for a thunk function,
1358 used to implement C++ virtual function calls with multiple inheritance. The
1359 thunk acts as a wrapper around a virtual function, adjusting the implicit
1360 object parameter before handing control off to the real function.
1362 First, emit code to add the integer DELTA to the location that contains the
1363 incoming first argument. Assume that this argument contains a pointer, and
1364 is the one used to pass the `this' pointer in C++. This is the incoming
1365 argument *before* the function prologue, e.g. `%o0' on a sparc. The
1366 addition must preserve the values of all other incoming arguments.
1368 After the addition, emit code to jump to FUNCTION, which is a
1369 `FUNCTION_DECL'. This is a direct pure jump, not a call, and does not touch
1370 the return address. Hence returning from FUNCTION will return to whoever
1371 called the current `thunk'.
1373 The effect must be as if @var{function} had been called directly
1374 with the adjusted first argument. This macro is responsible for
1375 emitting all of the code for a thunk function;
1376 TARGET_ASM_FUNCTION_PROLOGUE and TARGET_ASM_FUNCTION_EPILOGUE are
1379 The THUNK_FNDECL is redundant. (DELTA and FUNCTION have already been
1380 extracted from it.) It might possibly be useful on some targets, but
1384 xstormy16_asm_output_mi_thunk (file, thunk_fndecl, delta, function)
1386 tree thunk_fndecl ATTRIBUTE_UNUSED;
1390 int regnum = FIRST_ARGUMENT_REGISTER;
1392 /* There might be a hidden first argument for a returned structure. */
1393 if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function))))
1396 fprintf (file, "\tadd %s,#0x%x\n", reg_names[regnum], (delta) & 0xFFFF);
1397 fputs ("\tjmpf ", file);
1398 assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
1402 /* Mark functions with SYMBOL_REF_FLAG. */
1405 xstormy16_encode_section_info (decl, first)
1407 int first ATTRIBUTE_UNUSED;
1409 if (TREE_CODE (decl) == FUNCTION_DECL)
1410 SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
1413 /* Output constructors and destructors. Just like
1414 default_named_section_asm_out_* but don't set the sections writable. */
1415 #undef TARGET_ASM_CONSTRUCTOR
1416 #define TARGET_ASM_CONSTRUCTOR xstormy16_asm_out_constructor
1417 #undef TARGET_ASM_DESTRUCTOR
1418 #define TARGET_ASM_DESTRUCTOR xstormy16_asm_out_destructor
1421 xstormy16_asm_out_destructor (symbol, priority)
1425 const char *section = ".dtors";
1428 /* ??? This only works reliably with the GNU linker. */
1429 if (priority != DEFAULT_INIT_PRIORITY)
1431 sprintf (buf, ".dtors.%.5u",
1432 /* Invert the numbering so the linker puts us in the proper
1433 order; constructors are run from right to left, and the
1434 linker sorts in increasing order. */
1435 MAX_INIT_PRIORITY - priority);
1439 named_section_flags (section, 0);
1440 assemble_align (POINTER_SIZE);
1441 assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
1445 xstormy16_asm_out_constructor (symbol, priority)
1449 const char *section = ".ctors";
1452 /* ??? This only works reliably with the GNU linker. */
1453 if (priority != DEFAULT_INIT_PRIORITY)
1455 sprintf (buf, ".ctors.%.5u",
1456 /* Invert the numbering so the linker puts us in the proper
1457 order; constructors are run from right to left, and the
1458 linker sorts in increasing order. */
1459 MAX_INIT_PRIORITY - priority);
1463 named_section_flags (section, 0);
1464 assemble_align (POINTER_SIZE);
1465 assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
1468 /* Print a memory address as an operand to reference that memory location. */
1470 xstormy16_print_operand_address (file, address)
1474 HOST_WIDE_INT offset;
1475 int pre_dec, post_inc;
1477 /* There are a few easy cases. */
1478 if (GET_CODE (address) == CONST_INT)
1480 fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (address) & 0xFFFF);
1484 if (CONSTANT_P (address) || GET_CODE (address) == CODE_LABEL)
1486 output_addr_const (file, address);
1490 /* Otherwise, it's hopefully something of the form
1491 (plus:HI (pre_dec:HI (reg:HI ...)) (const_int ...))
1494 if (GET_CODE (address) == PLUS)
1496 if (GET_CODE (XEXP (address, 1)) != CONST_INT)
1498 offset = INTVAL (XEXP (address, 1));
1499 address = XEXP (address, 0);
1504 pre_dec = (GET_CODE (address) == PRE_DEC);
1505 post_inc = (GET_CODE (address) == POST_INC);
1506 if (pre_dec || post_inc)
1507 address = XEXP (address, 0);
1509 if (GET_CODE (address) != REG)
1515 fputs (reg_names [REGNO (address)], file);
1521 fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset);
1526 /* Print an operand to an assembler instruction. */
1528 xstormy16_print_operand (file, x, code)
1536 /* There is either one bit set, or one bit clear, in X.
1537 Print it preceded by '#'. */
1539 HOST_WIDE_INT xx = 1;
1542 if (GET_CODE (x) == CONST_INT)
1545 output_operand_lossage ("`B' operand is not constant");
1547 l = exact_log2 (xx);
1549 l = exact_log2 (~xx);
1551 output_operand_lossage ("`B' operand has multiple bits set");
1553 fputs (IMMEDIATE_PREFIX, file);
1554 fprintf (file, HOST_WIDE_INT_PRINT_DEC, l);
1559 /* Print the symbol without a surrounding @fptr(). */
1560 if (GET_CODE (x) == SYMBOL_REF)
1561 assemble_name (file, XSTR (x, 0));
1562 else if (GET_CODE (x) == LABEL_REF)
1563 output_asm_label (x);
1565 xstormy16_print_operand_address (file, x);
1570 /* Print the immediate operand less one, preceded by '#'.
1571 For 'O', negate it first. */
1573 HOST_WIDE_INT xx = 0;
1575 if (GET_CODE (x) == CONST_INT)
1578 output_operand_lossage ("`o' operand is not constant");
1583 fputs (IMMEDIATE_PREFIX, file);
1584 fprintf (file, HOST_WIDE_INT_PRINT_DEC, xx - 1);
1589 /* Handled below. */
1593 output_operand_lossage ("xstormy16_print_operand: unknown code");
1597 switch (GET_CODE (x))
1600 fputs (reg_names [REGNO (x)], file);
1604 xstormy16_print_operand_address (file, XEXP (x, 0));
1608 /* Some kind of constant or label; an immediate operand,
1609 so prefix it with '#' for the assembler. */
1610 fputs (IMMEDIATE_PREFIX, file);
1611 output_addr_const (file, x);
1619 /* Expander for the `casesi' pattern.
1620 INDEX is the index of the switch statement.
1621 LOWER_BOUND is a CONST_INT that is the value of INDEX corresponding
1622 to the first table entry.
1623 RANGE is the number of table entries.
1624 TABLE is an ADDR_VEC that is the jump table.
1625 DEFAULT_LABEL is the address to branch to if INDEX is outside the
1626 range LOWER_BOUND to LOWER_BOUND+RANGE-1.
1630 xstormy16_expand_casesi (index, lower_bound, range, table, default_label)
1637 HOST_WIDE_INT range_i = INTVAL (range);
1640 /* This code uses 'br', so it can deal only with tables of size up to
1642 if (range_i >= 8192)
1643 sorry ("switch statement of size %lu entries too large",
1644 (unsigned long) range_i);
1646 index = expand_binop (SImode, sub_optab, index, lower_bound, NULL_RTX, 0,
1648 emit_cmp_and_jump_insns (index, range, GTU, NULL_RTX, SImode, 1,
1650 int_index = gen_lowpart_common (HImode, index);
1651 emit_insn (gen_ashlhi3 (int_index, int_index, GEN_INT (2)));
1652 emit_jump_insn (gen_tablejump_pcrel (int_index, table));
1655 /* Output an ADDR_VEC. It is output as a sequence of 'jmpf'
1656 instructions, without label or alignment or any other special
1657 constructs. We know that the previous instruction will be the
1658 `tablejump_pcrel' output above.
1660 TODO: it might be nice to output 'br' instructions if they could
1664 xstormy16_output_addr_vec (file, label, table)
1666 rtx label ATTRIBUTE_UNUSED;
1671 function_section (current_function_decl);
1673 vlen = XVECLEN (table, 0);
1674 for (idx = 0; idx < vlen; idx++)
1676 fputs ("\tjmpf ", file);
1677 output_asm_label (XEXP (XVECEXP (table, 0, idx), 0));
1683 /* Expander for the `call' patterns.
1684 INDEX is the index of the switch statement.
1685 LOWER_BOUND is a CONST_INT that is the value of INDEX corresponding
1686 to the first table entry.
1687 RANGE is the number of table entries.
1688 TABLE is an ADDR_VEC that is the jump table.
1689 DEFAULT_LABEL is the address to branch to if INDEX is outside the
1690 range LOWER_BOUND to LOWER_BOUND+RANGE-1.
1694 xstormy16_expand_call (retval, dest, counter)
1700 enum machine_mode mode;
1702 if (GET_CODE (dest) != MEM)
1704 dest = XEXP (dest, 0);
1706 if (! CONSTANT_P (dest)
1707 && GET_CODE (dest) != REG)
1708 dest = force_reg (Pmode, dest);
1713 mode = GET_MODE (retval);
1715 call = gen_rtx_CALL (mode, gen_rtx_MEM (FUNCTION_MODE, dest),
1718 call = gen_rtx_SET (VOIDmode, retval, call);
1720 if (! CONSTANT_P (dest))
1722 temp = gen_reg_rtx (HImode);
1723 emit_move_insn (temp, const0_rtx);
1728 call = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, call,
1729 gen_rtx_USE (VOIDmode, temp)));
1730 emit_call_insn (call);
1733 /* Expanders for multiword computational operations. */
1735 /* Expander for arithmetic operations; emit insns to compute
1737 (set DEST (CODE:MODE SRC0 SRC1))
1739 using CARRY as a temporary. When CODE is COMPARE, a branch
1740 template is generated (this saves duplicating code in
1741 xstormy16_split_cbranch). */
1744 xstormy16_expand_arith (mode, code, dest, src0, src1, carry)
1745 enum machine_mode mode;
1752 int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
1758 rtx zero_reg = gen_reg_rtx (word_mode);
1759 emit_move_insn (zero_reg, src0);
1763 for (i = 0; i < num_words; i++)
1765 rtx w_src0, w_src1, w_dest;
1771 w_src0 = simplify_gen_subreg (word_mode, src0, mode,
1772 i * UNITS_PER_WORD);
1773 w_src1 = simplify_gen_subreg (word_mode, src1, mode, i * UNITS_PER_WORD);
1774 w_dest = simplify_gen_subreg (word_mode, dest, mode, i * UNITS_PER_WORD);
1780 && GET_CODE (w_src1) == CONST_INT && INTVAL (w_src1) == 0)
1784 insn = gen_addchi4 (w_dest, w_src0, w_src1, carry);
1786 insn = gen_addchi5 (w_dest, w_src0, w_src1, carry, carry);
1792 if (code == COMPARE && i == num_words - 1)
1794 rtx branch, sub, clobber, sub_1;
1796 sub_1 = gen_rtx_MINUS (HImode, w_src0,
1797 gen_rtx_ZERO_EXTEND (HImode, carry));
1798 sub = gen_rtx_SET (VOIDmode, w_dest,
1799 gen_rtx_MINUS (HImode, sub_1, w_src1));
1800 clobber = gen_rtx_CLOBBER (VOIDmode, carry);
1801 branch = gen_rtx_SET (VOIDmode, pc_rtx,
1802 gen_rtx_IF_THEN_ELSE (VOIDmode,
1808 insn = gen_rtx_PARALLEL (VOIDmode,
1809 gen_rtvec (3, branch, sub, clobber));
1813 && GET_CODE (w_src1) == CONST_INT && INTVAL (w_src1) == 0)
1816 insn = gen_subchi4 (w_dest, w_src0, w_src1, carry);
1818 insn = gen_subchi5 (w_dest, w_src0, w_src1, carry, carry);
1824 if (GET_CODE (w_src1) == CONST_INT
1825 && INTVAL (w_src1) == -(code == AND))
1828 insn = gen_rtx_SET (VOIDmode, w_dest, gen_rtx (code, mode,
1833 insn = gen_rtx_SET (VOIDmode, w_dest, gen_rtx_NOT (mode, w_src0));
1845 /* Return 1 if OP is a shift operator. */
1848 shift_operator (op, mode)
1850 enum machine_mode mode ATTRIBUTE_UNUSED;
1852 enum rtx_code code = GET_CODE (op);
1854 return (code == ASHIFT
1856 || code == LSHIFTRT);
1859 /* The shift operations are split at output time for constant values;
1860 variable-width shifts get handed off to a library routine.
1862 Generate an output string to do (set X (CODE:MODE X SIZE_R))
1863 SIZE_R will be a CONST_INT, X will be a hard register. */
1866 xstormy16_output_shift (mode, code, x, size_r, temp)
1867 enum machine_mode mode;
1874 const char *r0, *r1, *rt;
1877 if (GET_CODE (size_r) != CONST_INT
1878 || GET_CODE (x) != REG
1881 size = INTVAL (size_r) & (GET_MODE_BITSIZE (mode) - 1);
1886 r0 = reg_names [REGNO (x)];
1887 r1 = reg_names [REGNO (x) + 1];
1889 /* For shifts of size 1, we can use the rotate instructions. */
1895 sprintf (r, "shl %s,#1 | rlc %s,#1", r0, r1);
1898 sprintf (r, "asr %s,#1 | rrc %s,#1", r1, r0);
1901 sprintf (r, "shr %s,#1 | rrc %s,#1", r1, r0);
1909 /* For large shifts, there are easy special cases. */
1915 sprintf (r, "mov %s,%s | mov %s,#0", r1, r0, r0);
1918 sprintf (r, "mov %s,%s | asr %s,#15", r0, r1, r1);
1921 sprintf (r, "mov %s,%s | mov %s,#0", r0, r1, r1);
1933 sprintf (r, "mov %s,%s | mov %s,#0 | shl %s,#%d",
1934 r1, r0, r0, r1, (int) size - 16);
1937 sprintf (r, "mov %s,%s | asr %s,#15 | asr %s,#%d",
1938 r0, r1, r1, r0, (int) size - 16);
1941 sprintf (r, "mov %s,%s | mov %s,#0 | shr %s,#%d",
1942 r0, r1, r1, r0, (int) size - 16);
1950 /* For the rest, we have to do more work. In particular, we
1951 need a temporary. */
1952 rt = reg_names [REGNO (temp)];
1957 "mov %s,%s | shl %s,#%d | shl %s,#%d | shr %s,#%d | or %s,%s",
1958 rt, r0, r0, (int) size, r1, (int) size, rt, (int) 16-size,
1963 "mov %s,%s | asr %s,#%d | shr %s,#%d | shl %s,#%d | or %s,%s",
1964 rt, r1, r1, (int) size, r0, (int) size, rt, (int) 16-size,
1969 "mov %s,%s | shr %s,#%d | shr %s,#%d | shl %s,#%d | or %s,%s",
1970 rt, r1, r1, (int) size, r0, (int) size, rt, (int) 16-size,
1979 /* Attribute handling. */
1981 /* Return nonzero if the function is an interrupt function. */
1983 xstormy16_interrupt_function_p ()
1987 /* The dwarf2 mechanism asks for INCOMING_FRAME_SP_OFFSET before
1988 any functions are declared, which is demonstrably wrong, but
1989 it is worked around here. FIXME. */
1993 attributes = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
1994 return lookup_attribute ("interrupt", attributes) != NULL_TREE;
1997 #undef TARGET_ATTRIBUTE_TABLE
1998 #define TARGET_ATTRIBUTE_TABLE xstormy16_attribute_table
1999 static tree xstormy16_handle_interrupt_attribute PARAMS ((tree *, tree, tree, int, bool *));
2000 static const struct attribute_spec xstormy16_attribute_table[] =
2002 /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
2003 { "interrupt", 0, 0, false, true, true, xstormy16_handle_interrupt_attribute },
2004 { NULL, 0, 0, false, false, false, NULL }
2007 /* Handle an "interrupt" attribute;
2008 arguments as in struct attribute_spec.handler. */
2010 xstormy16_handle_interrupt_attribute (node, name, args, flags, no_add_attrs)
2013 tree args ATTRIBUTE_UNUSED;
2014 int flags ATTRIBUTE_UNUSED;
2017 if (TREE_CODE (*node) != FUNCTION_TYPE)
2019 warning ("`%s' attribute only applies to functions",
2020 IDENTIFIER_POINTER (name));
2021 *no_add_attrs = true;
2027 #undef TARGET_ASM_ALIGNED_HI_OP
2028 #define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
2029 #undef TARGET_ASM_ALIGNED_SI_OP
2030 #define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
2031 #undef TARGET_ENCODE_SECTION_INFO
2032 #define TARGET_ENCODE_SECTION_INFO xstormy16_encode_section_info
2034 struct gcc_target targetm = TARGET_INITIALIZER;