1 /* Xstormy16 target functions.
2 Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003
3 Free Software Foundation, Inc.
4 Contributed by Red Hat, Inc.
6 This file is part of GCC.
8 GCC 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 GCC 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 GCC; see the file COPYING. If not, write to
20 the Free Software Foundation, 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA. */
25 #include "coretypes.h"
29 #include "hard-reg-set.h"
31 #include "insn-config.h"
32 #include "conditions.h"
33 #include "insn-flags.h"
35 #include "insn-attr.h"
46 #include "target-def.h"
48 #include "langhooks.h"
50 static rtx
emit_addhi3_postreload (rtx
, rtx
, rtx
);
51 static void xstormy16_asm_out_constructor (rtx
, int);
52 static void xstormy16_asm_out_destructor (rtx
, int);
53 static void xstormy16_asm_output_mi_thunk (FILE *, tree
, HOST_WIDE_INT
,
56 static void xstormy16_init_builtins (void);
57 static rtx
xstormy16_expand_builtin (tree
, rtx
, rtx
, enum machine_mode
, int);
58 static bool xstormy16_rtx_costs (rtx
, int, int, int *);
59 static int xstormy16_address_cost (rtx
);
61 /* Define the information needed to generate branch and scc insns. This is
62 stored from the compare operation. */
63 struct rtx_def
* xstormy16_compare_op0
;
64 struct rtx_def
* xstormy16_compare_op1
;
66 /* Return 1 if this is a LT, GE, LTU, or GEU operator. */
69 xstormy16_ineqsi_operator (register rtx op
, enum machine_mode mode
)
71 enum rtx_code code
= GET_CODE (op
);
73 return ((mode
== VOIDmode
|| GET_MODE (op
) == mode
)
74 && (code
== LT
|| code
== GE
|| code
== LTU
|| code
== GEU
));
77 /* Return 1 if this is an EQ or NE operator. */
80 equality_operator (register rtx op
, enum machine_mode mode
)
82 return ((mode
== VOIDmode
|| GET_MODE (op
) == mode
)
83 && (GET_CODE (op
) == EQ
|| GET_CODE (op
) == NE
));
86 /* Return 1 if this is a comparison operator but not an EQ or NE operator. */
89 inequality_operator (register rtx op
, enum machine_mode mode
)
91 return comparison_operator (op
, mode
) && ! equality_operator (op
, mode
);
94 /* Compute a (partial) cost for rtx X. Return true if the complete
95 cost has been computed, and false if subexpressions should be
96 scanned. In either case, *TOTAL contains the cost result. */
99 xstormy16_rtx_costs (rtx x
, int code
, int outer_code ATTRIBUTE_UNUSED
,
105 if (INTVAL (x
) < 16 && INTVAL (x
) >= 0)
106 *total
= COSTS_N_INSNS (1) / 2;
107 else if (INTVAL (x
) < 256 && INTVAL (x
) >= 0)
108 *total
= COSTS_N_INSNS (1);
110 *total
= COSTS_N_INSNS (2);
117 *total
= COSTS_N_INSNS(2);
121 *total
= COSTS_N_INSNS (35 + 6);
124 *total
= COSTS_N_INSNS (51 - 6);
133 xstormy16_address_cost (rtx x
)
135 return (GET_CODE (x
) == CONST_INT
? 2
136 : GET_CODE (x
) == PLUS
? 7
140 /* Branches are handled as follows:
142 1. HImode compare-and-branches. The machine supports these
143 natively, so the appropriate pattern is emitted directly.
145 2. SImode EQ and NE. These are emitted as pairs of HImode
146 compare-and-branches.
148 3. SImode LT, GE, LTU and GEU. These are emitted as a sequence
149 of a SImode subtract followed by a branch (not a compare-and-branch),
155 4. SImode GT, LE, GTU, LEU. These are emitted as a sequence like:
163 /* Emit a branch of kind CODE to location LOC. */
166 xstormy16_emit_cbranch (enum rtx_code code
, rtx loc
)
168 rtx op0
= xstormy16_compare_op0
;
169 rtx op1
= xstormy16_compare_op1
;
170 rtx condition_rtx
, loc_ref
, branch
, cy_clobber
;
172 enum machine_mode mode
;
174 mode
= GET_MODE (op0
);
175 if (mode
!= HImode
&& mode
!= SImode
)
179 && (code
== GT
|| code
== LE
|| code
== GTU
|| code
== LEU
))
181 int unsigned_p
= (code
== GTU
|| code
== LEU
);
182 int gt_p
= (code
== GT
|| code
== GTU
);
186 lab
= gen_label_rtx ();
187 xstormy16_emit_cbranch (unsigned_p
? LTU
: LT
, gt_p
? lab
: loc
);
188 /* This should be generated as a comparison against the temporary
189 created by the previous insn, but reload can't handle that. */
190 xstormy16_emit_cbranch (gt_p
? NE
: EQ
, loc
);
195 else if (mode
== SImode
196 && (code
== NE
|| code
== EQ
)
197 && op1
!= const0_rtx
)
200 int num_words
= GET_MODE_BITSIZE (mode
) / BITS_PER_WORD
;
204 lab
= gen_label_rtx ();
206 for (i
= 0; i
< num_words
- 1; i
++)
208 xstormy16_compare_op0
= simplify_gen_subreg (word_mode
, op0
, mode
,
210 xstormy16_compare_op1
= simplify_gen_subreg (word_mode
, op1
, mode
,
212 xstormy16_emit_cbranch (NE
, code
== EQ
? lab
: loc
);
214 xstormy16_compare_op0
= simplify_gen_subreg (word_mode
, op0
, mode
,
216 xstormy16_compare_op1
= simplify_gen_subreg (word_mode
, op1
, mode
,
218 xstormy16_emit_cbranch (code
, loc
);
225 /* We can't allow reload to try to generate any reload after a branch,
226 so when some register must match we must make the temporary ourselves. */
230 tmp
= gen_reg_rtx (mode
);
231 emit_move_insn (tmp
, op0
);
235 condition_rtx
= gen_rtx (code
, mode
, op0
, op1
);
236 loc_ref
= gen_rtx_LABEL_REF (VOIDmode
, loc
);
237 branch
= gen_rtx_SET (VOIDmode
, pc_rtx
,
238 gen_rtx_IF_THEN_ELSE (VOIDmode
, condition_rtx
,
241 cy_clobber
= gen_rtx_CLOBBER (VOIDmode
, gen_rtx_SCRATCH (BImode
));
244 vec
= gen_rtvec (2, branch
, cy_clobber
);
245 else if (code
== NE
|| code
== EQ
)
246 vec
= gen_rtvec (2, branch
, gen_rtx_CLOBBER (VOIDmode
, op0
));
251 sub
= gen_rtx_SET (VOIDmode
, op0
, gen_rtx_MINUS (SImode
, op0
, op1
));
253 sub
= gen_rtx_CLOBBER (SImode
, op0
);
255 vec
= gen_rtvec (3, branch
, sub
, cy_clobber
);
258 emit_jump_insn (gen_rtx_PARALLEL (VOIDmode
, vec
));
261 /* Take a SImode conditional branch, one of GT/LE/GTU/LEU, and split
262 the arithmetic operation. Most of the work is done by
263 xstormy16_expand_arith. */
266 xstormy16_split_cbranch (enum machine_mode mode
, rtx label
, rtx comparison
,
269 rtx op0
= XEXP (comparison
, 0);
270 rtx op1
= XEXP (comparison
, 1);
275 xstormy16_expand_arith (mode
, COMPARE
, dest
, op0
, op1
, carry
);
283 while (NEXT_INSN (last_insn
) != NULL_RTX
)
284 last_insn
= NEXT_INSN (last_insn
);
286 compare
= SET_SRC (XVECEXP (PATTERN (last_insn
), 0, 0));
287 PUT_CODE (XEXP (compare
, 0), GET_CODE (comparison
));
288 XEXP (compare
, 1) = gen_rtx_LABEL_REF (VOIDmode
, label
);
293 /* Return the string to output a conditional branch to LABEL, which is
294 the operand number of the label.
296 OP is the conditional expression, or NULL for branch-always.
298 REVERSED is nonzero if we should reverse the sense of the comparison.
303 xstormy16_output_cbranch_hi (rtx op
, const char *label
, int reversed
, rtx insn
)
305 static char string
[64];
306 int need_longbranch
= (op
!= NULL_RTX
307 ? get_attr_length (insn
) == 8
308 : get_attr_length (insn
) == 4);
309 int really_reversed
= reversed
^ need_longbranch
;
311 const char *template;
312 const char *operands
;
321 sprintf (string
, "%s %s", ccode
, label
);
325 code
= GET_CODE (op
);
327 if (GET_CODE (XEXP (op
, 0)) != REG
)
329 code
= swap_condition (code
);
335 /* Work out which way this really branches. */
337 code
= reverse_condition (code
);
341 case EQ
: ccode
= "z"; break;
342 case NE
: ccode
= "nz"; break;
343 case GE
: ccode
= "ge"; break;
344 case LT
: ccode
= "lt"; break;
345 case GT
: ccode
= "gt"; break;
346 case LE
: ccode
= "le"; break;
347 case GEU
: ccode
= "nc"; break;
348 case LTU
: ccode
= "c"; break;
349 case GTU
: ccode
= "hi"; break;
350 case LEU
: ccode
= "ls"; break;
357 template = "b%s %s,.+8 | jmpf %s";
359 template = "b%s %s,%s";
360 sprintf (string
, template, ccode
, operands
, label
);
365 /* Return the string to output a conditional branch to LABEL, which is
366 the operand number of the label, but suitable for the tail of a
369 OP is the conditional expression (OP is never NULL_RTX).
371 REVERSED is nonzero if we should reverse the sense of the comparison.
376 xstormy16_output_cbranch_si (rtx op
, const char *label
, int reversed
, rtx insn
)
378 static char string
[64];
379 int need_longbranch
= get_attr_length (insn
) >= 8;
380 int really_reversed
= reversed
^ need_longbranch
;
382 const char *template;
386 code
= GET_CODE (op
);
388 /* Work out which way this really branches. */
390 code
= reverse_condition (code
);
394 case EQ
: ccode
= "z"; break;
395 case NE
: ccode
= "nz"; break;
396 case GE
: ccode
= "ge"; break;
397 case LT
: ccode
= "lt"; break;
398 case GEU
: ccode
= "nc"; break;
399 case LTU
: ccode
= "c"; break;
401 /* The missing codes above should never be generated. */
412 if (GET_CODE (XEXP (op
, 0)) != REG
)
415 regnum
= REGNO (XEXP (op
, 0));
416 sprintf (prevop
, "or %s,%s", reg_names
[regnum
], reg_names
[regnum
+1]);
420 case GE
: case LT
: case GEU
: case LTU
:
421 strcpy (prevop
, "sbc %2,%3");
429 template = "%s | b%s .+6 | jmpf %s";
431 template = "%s | b%s %s";
432 sprintf (string
, template, prevop
, ccode
, label
);
437 /* Many machines have some registers that cannot be copied directly to or from
438 memory or even from other types of registers. An example is the `MQ'
439 register, which on most machines, can only be copied to or from general
440 registers, but not memory. Some machines allow copying all registers to and
441 from memory, but require a scratch register for stores to some memory
442 locations (e.g., those with symbolic address on the RT, and those with
443 certain symbolic address on the SPARC when compiling PIC). In some cases,
444 both an intermediate and a scratch register are required.
446 You should define these macros to indicate to the reload phase that it may
447 need to allocate at least one register for a reload in addition to the
448 register to contain the data. Specifically, if copying X to a register
449 CLASS in MODE requires an intermediate register, you should define
450 `SECONDARY_INPUT_RELOAD_CLASS' to return the largest register class all of
451 whose registers can be used as intermediate registers or scratch registers.
453 If copying a register CLASS in MODE to X requires an intermediate or scratch
454 register, `SECONDARY_OUTPUT_RELOAD_CLASS' should be defined to return the
455 largest register class required. If the requirements for input and output
456 reloads are the same, the macro `SECONDARY_RELOAD_CLASS' should be used
457 instead of defining both macros identically.
459 The values returned by these macros are often `GENERAL_REGS'. Return
460 `NO_REGS' if no spare register is needed; i.e., if X can be directly copied
461 to or from a register of CLASS in MODE without requiring a scratch register.
462 Do not define this macro if it would always return `NO_REGS'.
464 If a scratch register is required (either with or without an intermediate
465 register), you should define patterns for `reload_inM' or `reload_outM', as
466 required.. These patterns, which will normally be implemented with a
467 `define_expand', should be similar to the `movM' patterns, except that
468 operand 2 is the scratch register.
470 Define constraints for the reload register and scratch register that contain
471 a single register class. If the original reload register (whose class is
472 CLASS) can meet the constraint given in the pattern, the value returned by
473 these macros is used for the class of the scratch register. Otherwise, two
474 additional reload registers are required. Their classes are obtained from
475 the constraints in the insn pattern.
477 X might be a pseudo-register or a `subreg' of a pseudo-register, which could
478 either be in a hard register or in memory. Use `true_regnum' to find out;
479 it will return -1 if the pseudo is in memory and the hard register number if
482 These macros should not be used in the case where a particular class of
483 registers can only be copied to memory and not to another class of
484 registers. In that case, secondary reload registers are not needed and
485 would not be helpful. Instead, a stack location must be used to perform the
486 copy and the `movM' pattern should use memory as an intermediate storage.
487 This case often occurs between floating-point and general registers. */
490 xstormy16_secondary_reload_class (enum reg_class
class,
491 enum machine_mode mode
,
494 /* This chip has the interesting property that only the first eight
495 registers can be moved to/from memory. */
496 if ((GET_CODE (x
) == MEM
497 || ((GET_CODE (x
) == SUBREG
|| GET_CODE (x
) == REG
)
498 && (true_regnum (x
) == -1
499 || true_regnum (x
) >= FIRST_PSEUDO_REGISTER
)))
500 && ! reg_class_subset_p (class, EIGHT_REGS
))
503 /* When reloading a PLUS, the carry register will be required
504 unless the inc or dec instructions can be used. */
505 if (xstormy16_carry_plus_operand (x
, mode
))
511 /* Recognize a PLUS that needs the carry register. */
513 xstormy16_carry_plus_operand (rtx x
, enum machine_mode mode ATTRIBUTE_UNUSED
)
515 return (GET_CODE (x
) == PLUS
516 && GET_CODE (XEXP (x
, 1)) == CONST_INT
517 && (INTVAL (XEXP (x
, 1)) < -4 || INTVAL (XEXP (x
, 1)) > 4));
520 /* Detect and error out on out-of-range constants for movhi. */
522 xs_hi_general_operand (rtx x
, enum machine_mode mode ATTRIBUTE_UNUSED
)
524 if ((GET_CODE (x
) == CONST_INT
)
525 && ((INTVAL (x
) >= 32768) || (INTVAL (x
) < -32768)))
526 error ("Constant halfword load operand out of range.");
527 return general_operand (x
, mode
);
530 /* Detect and error out on out-of-range constants for addhi and subhi. */
532 xs_hi_nonmemory_operand (rtx x
, enum machine_mode mode ATTRIBUTE_UNUSED
)
534 if ((GET_CODE (x
) == CONST_INT
)
535 && ((INTVAL (x
) >= 32768) || (INTVAL (x
) < -32768)))
536 error ("Constant arithmetic operand out of range.");
537 return nonmemory_operand (x
, mode
);
541 xstormy16_preferred_reload_class (rtx x
, enum reg_class
class)
543 if (class == GENERAL_REGS
544 && GET_CODE (x
) == MEM
)
550 #define LEGITIMATE_ADDRESS_INTEGER_P(X, OFFSET) \
551 (GET_CODE (X) == CONST_INT \
552 && (unsigned HOST_WIDE_INT) (INTVAL (X) + (OFFSET) + 2048) < 4096)
554 #define LEGITIMATE_ADDRESS_CONST_INT_P(X, OFFSET) \
555 (GET_CODE (X) == CONST_INT \
556 && INTVAL (X) + (OFFSET) >= 0 \
557 && INTVAL (X) + (OFFSET) < 0x8000 \
558 && (INTVAL (X) + (OFFSET) < 0x100 || INTVAL (X) + (OFFSET) >= 0x7F00))
561 xstormy16_legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED
,
564 if (LEGITIMATE_ADDRESS_CONST_INT_P (x
, 0))
567 if (GET_CODE (x
) == PLUS
568 && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x
, 1), 0))
571 if ((GET_CODE (x
) == PRE_MODIFY
572 && GET_CODE (XEXP (XEXP (x
, 1), 1)) == CONST_INT
)
573 || GET_CODE (x
) == POST_INC
574 || GET_CODE (x
) == PRE_DEC
)
577 if (GET_CODE (x
) == REG
&& REGNO_OK_FOR_BASE_P (REGNO (x
))
578 && (! strict
|| REGNO (x
) < FIRST_PSEUDO_REGISTER
))
584 /* Return nonzero if memory address X (an RTX) can have different
585 meanings depending on the machine mode of the memory reference it
586 is used for or if the address is valid for some modes but not
589 Autoincrement and autodecrement addresses typically have mode-dependent
590 effects because the amount of the increment or decrement is the size of the
591 operand being addressed. Some machines have other mode-dependent addresses.
592 Many RISC machines have no mode-dependent addresses.
594 You may assume that ADDR is a valid address for the machine.
596 On this chip, this is true if the address is valid with an offset
597 of 0 but not of 6, because in that case it cannot be used as an
598 address for DImode or DFmode, or if the address is a post-increment
599 or pre-decrement address. */
601 xstormy16_mode_dependent_address_p (rtx x
)
603 if (LEGITIMATE_ADDRESS_CONST_INT_P (x
, 0)
604 && ! LEGITIMATE_ADDRESS_CONST_INT_P (x
, 6))
607 if (GET_CODE (x
) == PLUS
608 && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x
, 1), 0)
609 && ! LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x
, 1), 6))
612 if (GET_CODE (x
) == PLUS
)
615 if (GET_CODE (x
) == POST_INC
616 || GET_CODE (x
) == PRE_DEC
)
622 /* A C expression that defines the optional machine-dependent constraint
623 letters (`Q', `R', `S', `T', `U') that can be used to segregate specific
624 types of operands, usually memory references, for the target machine.
625 Normally this macro will not be defined. If it is required for a particular
626 target machine, it should return 1 if VALUE corresponds to the operand type
627 represented by the constraint letter C. If C is not defined as an extra
628 constraint, the value returned should be 0 regardless of VALUE. */
630 xstormy16_extra_constraint_p (rtx x
, int c
)
634 /* 'Q' is for pushes. */
636 return (GET_CODE (x
) == MEM
637 && GET_CODE (XEXP (x
, 0)) == POST_INC
638 && XEXP (XEXP (x
, 0), 0) == stack_pointer_rtx
);
640 /* 'R' is for pops. */
642 return (GET_CODE (x
) == MEM
643 && GET_CODE (XEXP (x
, 0)) == PRE_DEC
644 && XEXP (XEXP (x
, 0), 0) == stack_pointer_rtx
);
646 /* 'S' is for immediate memory addresses. */
648 return (GET_CODE (x
) == MEM
649 && GET_CODE (XEXP (x
, 0)) == CONST_INT
650 && xstormy16_legitimate_address_p (VOIDmode
, XEXP (x
, 0), 0));
654 /* Not implemented yet. */
657 /* 'U' is for CONST_INT values not between 2 and 15 inclusive,
658 for allocating a scratch register for 32-bit shifts. */
660 return (GET_CODE (x
) == CONST_INT
661 && (INTVAL (x
) < 2 || INTVAL (x
) > 15));
663 /* 'Z' is for CONST_INT value zero. This is for adding zero to
664 a register in addhi3, which would otherwise require a carry. */
666 return (GET_CODE (x
) == CONST_INT
667 && (INTVAL (x
) == 0));
675 short_memory_operand (rtx x
, enum machine_mode mode
)
677 if (! memory_operand (x
, mode
))
679 return (GET_CODE (XEXP (x
, 0)) != PLUS
);
683 nonimmediate_nonstack_operand (rtx op
, enum machine_mode mode
)
685 /* 'Q' is for pushes, 'R' for pops. */
686 return (nonimmediate_operand (op
, mode
)
687 && ! xstormy16_extra_constraint_p (op
, 'Q')
688 && ! xstormy16_extra_constraint_p (op
, 'R'));
691 /* Splitter for the 'move' patterns, for modes not directly implemented
692 by hardware. Emit insns to copy a value of mode MODE from SRC to
695 This function is only called when reload_completed.
699 xstormy16_split_move (enum machine_mode mode
, rtx dest
, rtx src
)
701 int num_words
= GET_MODE_BITSIZE (mode
) / BITS_PER_WORD
;
702 int direction
, end
, i
;
703 int src_modifies
= 0;
704 int dest_modifies
= 0;
705 int src_volatile
= 0;
706 int dest_volatile
= 0;
708 rtx auto_inc_reg_rtx
= NULL_RTX
;
710 /* Check initial conditions. */
711 if (! reload_completed
712 || mode
== QImode
|| mode
== HImode
713 || ! nonimmediate_operand (dest
, mode
)
714 || ! general_operand (src
, mode
))
717 /* This case is not supported below, and shouldn't be generated. */
718 if (GET_CODE (dest
) == MEM
719 && GET_CODE (src
) == MEM
)
722 /* This case is very very bad after reload, so trap it now. */
723 if (GET_CODE (dest
) == SUBREG
724 || GET_CODE (src
) == SUBREG
)
727 /* The general idea is to copy by words, offsetting the source and
728 destination. Normally the least-significant word will be copied
729 first, but for pre-dec operations it's better to copy the
730 most-significant word first. Only one operand can be a pre-dec
733 It's also possible that the copy overlaps so that the direction
737 if (GET_CODE (dest
) == MEM
)
739 mem_operand
= XEXP (dest
, 0);
740 dest_modifies
= side_effects_p (mem_operand
);
741 if (auto_inc_p (mem_operand
))
742 auto_inc_reg_rtx
= XEXP (mem_operand
, 0);
743 dest_volatile
= MEM_VOLATILE_P (dest
);
746 dest
= copy_rtx (dest
);
747 MEM_VOLATILE_P (dest
) = 0;
750 else if (GET_CODE (src
) == MEM
)
752 mem_operand
= XEXP (src
, 0);
753 src_modifies
= side_effects_p (mem_operand
);
754 if (auto_inc_p (mem_operand
))
755 auto_inc_reg_rtx
= XEXP (mem_operand
, 0);
756 src_volatile
= MEM_VOLATILE_P (src
);
759 src
= copy_rtx (src
);
760 MEM_VOLATILE_P (src
) = 0;
764 mem_operand
= NULL_RTX
;
766 if (mem_operand
== NULL_RTX
)
768 if (GET_CODE (src
) == REG
769 && GET_CODE (dest
) == REG
770 && reg_overlap_mentioned_p (dest
, src
)
771 && REGNO (dest
) > REGNO (src
))
774 else if (GET_CODE (mem_operand
) == PRE_DEC
775 || (GET_CODE (mem_operand
) == PLUS
776 && GET_CODE (XEXP (mem_operand
, 0)) == PRE_DEC
))
778 else if (GET_CODE (src
) == MEM
779 && reg_overlap_mentioned_p (dest
, src
))
782 if (GET_CODE (dest
) != REG
)
784 regno
= REGNO (dest
);
786 if (! refers_to_regno_p (regno
, regno
+ num_words
, mem_operand
, 0))
789 if (refers_to_regno_p (regno
, regno
+ 1, mem_operand
, 0))
791 else if (refers_to_regno_p (regno
+ num_words
- 1, regno
+ num_words
,
795 /* This means something like
796 (set (reg:DI r0) (mem:DI (reg:HI r1)))
797 which we'd need to support by doing the set of the second word
802 end
= direction
< 0 ? -1 : num_words
;
803 for (i
= direction
< 0 ? num_words
- 1 : 0; i
!= end
; i
+= direction
)
805 rtx w_src
, w_dest
, insn
;
808 w_src
= gen_rtx_MEM (word_mode
, mem_operand
);
810 w_src
= simplify_gen_subreg (word_mode
, src
, mode
, i
* UNITS_PER_WORD
);
812 MEM_VOLATILE_P (w_src
) = 1;
814 w_dest
= gen_rtx_MEM (word_mode
, mem_operand
);
816 w_dest
= simplify_gen_subreg (word_mode
, dest
, mode
,
819 MEM_VOLATILE_P (w_dest
) = 1;
821 /* The simplify_subreg calls must always be able to simplify. */
822 if (GET_CODE (w_src
) == SUBREG
823 || GET_CODE (w_dest
) == SUBREG
)
826 insn
= emit_insn (gen_rtx_SET (VOIDmode
, w_dest
, w_src
));
827 if (auto_inc_reg_rtx
)
828 REG_NOTES (insn
) = alloc_EXPR_LIST (REG_INC
,
834 /* Expander for the 'move' patterns. Emit insns to copy a value of
835 mode MODE from SRC to DEST. */
838 xstormy16_expand_move (enum machine_mode mode
, rtx dest
, rtx src
)
840 if ((GET_CODE (dest
) == MEM
) && (GET_CODE (XEXP (dest
, 0)) == PRE_MODIFY
))
842 rtx pmv
= XEXP (dest
, 0);
843 rtx dest_reg
= XEXP (pmv
, 0);
844 rtx dest_mod
= XEXP (pmv
, 1);
845 rtx set
= gen_rtx_SET (Pmode
, dest_reg
, dest_mod
);
846 rtx clobber
= gen_rtx_CLOBBER (VOIDmode
, gen_rtx_REG (BImode
, 16));
848 dest
= gen_rtx_MEM (mode
, dest_reg
);
849 emit_insn (gen_rtx_PARALLEL (VOIDmode
, gen_rtvec (2, set
, clobber
)));
851 else if ((GET_CODE (src
) == MEM
) && (GET_CODE (XEXP (src
, 0)) == PRE_MODIFY
))
853 rtx pmv
= XEXP (src
, 0);
854 rtx src_reg
= XEXP (pmv
, 0);
855 rtx src_mod
= XEXP (pmv
, 1);
856 rtx set
= gen_rtx_SET (Pmode
, src_reg
, src_mod
);
857 rtx clobber
= gen_rtx_CLOBBER (VOIDmode
, gen_rtx_REG (BImode
, 16));
859 src
= gen_rtx_MEM (mode
, src_reg
);
860 emit_insn (gen_rtx_PARALLEL (VOIDmode
, gen_rtvec (2, set
, clobber
)));
863 /* There are only limited immediate-to-memory move instructions. */
864 if (! reload_in_progress
865 && ! reload_completed
866 && GET_CODE (dest
) == MEM
867 && (GET_CODE (XEXP (dest
, 0)) != CONST_INT
868 || ! xstormy16_legitimate_address_p (mode
, XEXP (dest
, 0), 0))
869 && GET_CODE (src
) != REG
870 && GET_CODE (src
) != SUBREG
)
871 src
= copy_to_mode_reg (mode
, src
);
873 /* Don't emit something we would immediately split. */
875 && mode
!= HImode
&& mode
!= QImode
)
877 xstormy16_split_move (mode
, dest
, src
);
881 emit_insn (gen_rtx_SET (VOIDmode
, dest
, src
));
887 The stack is laid out as follows:
891 Register save area (up to 4 words)
892 Argument register save area for stdarg (NUM_ARGUMENT_REGISTERS words)
894 AP-> Return address (two words)
895 9th procedure parameter word
896 10th procedure parameter word
898 last procedure parameter word
900 The frame pointer location is tuned to make it most likely that all
901 parameters and local variables can be accessed using a load-indexed
904 /* A structure to describe the layout. */
905 struct xstormy16_stack_layout
907 /* Size of the topmost three items on the stack. */
909 int register_save_size
;
910 int stdarg_save_size
;
911 /* Sum of the above items. */
913 /* Various offsets. */
914 int first_local_minus_ap
;
919 /* Does REGNO need to be saved? */
920 #define REG_NEEDS_SAVE(REGNUM, IFUN) \
921 ((regs_ever_live[REGNUM] && ! call_used_regs[REGNUM]) \
922 || (IFUN && ! fixed_regs[REGNUM] && call_used_regs[REGNUM] \
923 && (REGNO_REG_CLASS (REGNUM) != CARRY_REGS) \
924 && (regs_ever_live[REGNUM] || ! current_function_is_leaf)))
926 /* Compute the stack layout. */
927 struct xstormy16_stack_layout
928 xstormy16_compute_stack_layout (void)
930 struct xstormy16_stack_layout layout
;
932 const int ifun
= xstormy16_interrupt_function_p ();
934 layout
.locals_size
= get_frame_size ();
936 layout
.register_save_size
= 0;
937 for (regno
= 0; regno
< FIRST_PSEUDO_REGISTER
; regno
++)
938 if (REG_NEEDS_SAVE (regno
, ifun
))
939 layout
.register_save_size
+= UNITS_PER_WORD
;
941 if (current_function_stdarg
)
942 layout
.stdarg_save_size
= NUM_ARGUMENT_REGISTERS
* UNITS_PER_WORD
;
944 layout
.stdarg_save_size
= 0;
946 layout
.frame_size
= (layout
.locals_size
947 + layout
.register_save_size
948 + layout
.stdarg_save_size
);
950 if (current_function_args_size
<= 2048 && current_function_args_size
!= -1)
952 if (layout
.frame_size
+ INCOMING_FRAME_SP_OFFSET
953 + current_function_args_size
<= 2048)
954 layout
.fp_minus_ap
= layout
.frame_size
+ INCOMING_FRAME_SP_OFFSET
;
956 layout
.fp_minus_ap
= 2048 - current_function_args_size
;
959 layout
.fp_minus_ap
= (layout
.stdarg_save_size
960 + layout
.register_save_size
961 + INCOMING_FRAME_SP_OFFSET
);
962 layout
.sp_minus_fp
= (layout
.frame_size
+ INCOMING_FRAME_SP_OFFSET
963 - layout
.fp_minus_ap
);
964 layout
.first_local_minus_ap
= layout
.sp_minus_fp
- layout
.locals_size
;
968 /* Determine how all the special registers get eliminated. */
970 xstormy16_initial_elimination_offset (int from
, int to
)
972 struct xstormy16_stack_layout layout
;
975 layout
= xstormy16_compute_stack_layout ();
977 if (from
== FRAME_POINTER_REGNUM
&& to
== HARD_FRAME_POINTER_REGNUM
)
978 result
= layout
.sp_minus_fp
- layout
.locals_size
;
979 else if (from
== FRAME_POINTER_REGNUM
&& to
== STACK_POINTER_REGNUM
)
980 result
= -layout
.locals_size
;
981 else if (from
== ARG_POINTER_REGNUM
&& to
== HARD_FRAME_POINTER_REGNUM
)
982 result
= -layout
.fp_minus_ap
;
983 else if (from
== ARG_POINTER_REGNUM
&& to
== STACK_POINTER_REGNUM
)
984 result
= -(layout
.sp_minus_fp
+ layout
.fp_minus_ap
);
992 emit_addhi3_postreload (rtx dest
, rtx src0
, rtx src1
)
994 rtx set
, clobber
, insn
;
996 set
= gen_rtx_SET (VOIDmode
, dest
, gen_rtx_PLUS (HImode
, src0
, src1
));
997 clobber
= gen_rtx_CLOBBER (VOIDmode
, gen_rtx_REG (BImode
, 16));
998 insn
= emit_insn (gen_rtx_PARALLEL (VOIDmode
, gen_rtvec (2, set
, clobber
)));
1002 /* Called after register allocation to add any instructions needed for
1003 the prologue. Using a prologue insn is favored compared to putting
1004 all of the instructions in the TARGET_ASM_FUNCTION_PROLOGUE macro,
1005 since it allows the scheduler to intermix instructions with the
1006 saves of the caller saved registers. In some cases, it might be
1007 necessary to emit a barrier instruction as the last insn to prevent
1010 Also any insns generated here should have RTX_FRAME_RELATED_P(insn) = 1
1011 so that the debug info generation code can handle them properly. */
1013 xstormy16_expand_prologue (void)
1015 struct xstormy16_stack_layout layout
;
1019 const int ifun
= xstormy16_interrupt_function_p ();
1021 mem_push_rtx
= gen_rtx_POST_INC (Pmode
, stack_pointer_rtx
);
1022 mem_push_rtx
= gen_rtx_MEM (HImode
, mem_push_rtx
);
1024 layout
= xstormy16_compute_stack_layout ();
1026 if (layout
.locals_size
>= 32768)
1027 error ("Local variable memory requirements exceed capacity.");
1029 /* Save the argument registers if necessary. */
1030 if (layout
.stdarg_save_size
)
1031 for (regno
= FIRST_ARGUMENT_REGISTER
;
1032 regno
< FIRST_ARGUMENT_REGISTER
+ NUM_ARGUMENT_REGISTERS
;
1036 rtx reg
= gen_rtx_REG (HImode
, regno
);
1038 insn
= emit_move_insn (mem_push_rtx
, reg
);
1039 RTX_FRAME_RELATED_P (insn
) = 1;
1041 dwarf
= gen_rtx_SEQUENCE (VOIDmode
, rtvec_alloc (2));
1043 XVECEXP (dwarf
, 0, 0) = gen_rtx_SET (VOIDmode
,
1044 gen_rtx_MEM (Pmode
, stack_pointer_rtx
),
1046 XVECEXP (dwarf
, 0, 1) = gen_rtx_SET (Pmode
, stack_pointer_rtx
,
1047 plus_constant (stack_pointer_rtx
,
1048 GET_MODE_SIZE (Pmode
)));
1049 REG_NOTES (insn
) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR
,
1052 RTX_FRAME_RELATED_P (XVECEXP (dwarf
, 0, 0)) = 1;
1053 RTX_FRAME_RELATED_P (XVECEXP (dwarf
, 0, 1)) = 1;
1056 /* Push each of the registers to save. */
1057 for (regno
= 0; regno
< FIRST_PSEUDO_REGISTER
; regno
++)
1058 if (REG_NEEDS_SAVE (regno
, ifun
))
1061 rtx reg
= gen_rtx_REG (HImode
, regno
);
1063 insn
= emit_move_insn (mem_push_rtx
, reg
);
1064 RTX_FRAME_RELATED_P (insn
) = 1;
1066 dwarf
= gen_rtx_SEQUENCE (VOIDmode
, rtvec_alloc (2));
1068 XVECEXP (dwarf
, 0, 0) = gen_rtx_SET (VOIDmode
,
1069 gen_rtx_MEM (Pmode
, stack_pointer_rtx
),
1071 XVECEXP (dwarf
, 0, 1) = gen_rtx_SET (Pmode
, stack_pointer_rtx
,
1072 plus_constant (stack_pointer_rtx
,
1073 GET_MODE_SIZE (Pmode
)));
1074 REG_NOTES (insn
) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR
,
1077 RTX_FRAME_RELATED_P (XVECEXP (dwarf
, 0, 0)) = 1;
1078 RTX_FRAME_RELATED_P (XVECEXP (dwarf
, 0, 1)) = 1;
1081 /* It's just possible that the SP here might be what we need for
1083 if (frame_pointer_needed
&& layout
.sp_minus_fp
== layout
.locals_size
)
1084 emit_move_insn (hard_frame_pointer_rtx
, stack_pointer_rtx
);
1086 /* Allocate space for local variables. */
1087 if (layout
.locals_size
)
1089 insn
= emit_addhi3_postreload (stack_pointer_rtx
, stack_pointer_rtx
,
1090 GEN_INT (layout
.locals_size
));
1091 RTX_FRAME_RELATED_P (insn
) = 1;
1094 /* Set up the frame pointer, if required. */
1095 if (frame_pointer_needed
&& layout
.sp_minus_fp
!= layout
.locals_size
)
1097 insn
= emit_move_insn (hard_frame_pointer_rtx
, stack_pointer_rtx
);
1099 if (layout
.sp_minus_fp
)
1100 emit_addhi3_postreload (hard_frame_pointer_rtx
,
1101 hard_frame_pointer_rtx
,
1102 GEN_INT (-layout
.sp_minus_fp
));
1106 /* Do we need an epilogue at all? */
1108 direct_return (void)
1110 return (reload_completed
1111 && xstormy16_compute_stack_layout ().frame_size
== 0);
1114 /* Called after register allocation to add any instructions needed for
1115 the epilogue. Using an epilogue insn is favored compared to putting
1116 all of the instructions in the TARGET_ASM_FUNCTION_PROLOGUE macro,
1117 since it allows the scheduler to intermix instructions with the
1118 saves of the caller saved registers. In some cases, it might be
1119 necessary to emit a barrier instruction as the last insn to prevent
1123 xstormy16_expand_epilogue (void)
1125 struct xstormy16_stack_layout layout
;
1126 rtx mem_pop_rtx
, insn
;
1128 const int ifun
= xstormy16_interrupt_function_p ();
1130 mem_pop_rtx
= gen_rtx_PRE_DEC (Pmode
, stack_pointer_rtx
);
1131 mem_pop_rtx
= gen_rtx_MEM (HImode
, mem_pop_rtx
);
1133 layout
= xstormy16_compute_stack_layout ();
1135 /* Pop the stack for the locals. */
1136 if (layout
.locals_size
)
1138 if (frame_pointer_needed
&& layout
.sp_minus_fp
== layout
.locals_size
)
1139 emit_move_insn (stack_pointer_rtx
, hard_frame_pointer_rtx
);
1142 insn
= emit_addhi3_postreload (stack_pointer_rtx
, stack_pointer_rtx
,
1143 GEN_INT (- layout
.locals_size
));
1144 RTX_FRAME_RELATED_P (insn
) = 1;
1148 /* Restore any call-saved registers. */
1149 for (regno
= FIRST_PSEUDO_REGISTER
- 1; regno
>= 0; regno
--)
1150 if (REG_NEEDS_SAVE (regno
, ifun
))
1154 insn
= emit_move_insn (gen_rtx_REG (HImode
, regno
), mem_pop_rtx
);
1155 RTX_FRAME_RELATED_P (insn
) = 1;
1156 dwarf
= gen_rtx_SET (Pmode
, stack_pointer_rtx
,
1157 plus_constant (stack_pointer_rtx
,
1158 -GET_MODE_SIZE (Pmode
)));
1159 REG_NOTES (insn
) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR
,
1164 /* Pop the stack for the stdarg save area. */
1165 if (layout
.stdarg_save_size
)
1167 insn
= emit_addhi3_postreload (stack_pointer_rtx
, stack_pointer_rtx
,
1168 GEN_INT (- layout
.stdarg_save_size
));
1169 RTX_FRAME_RELATED_P (insn
) = 1;
1174 emit_jump_insn (gen_return_internal_interrupt ());
1176 emit_jump_insn (gen_return_internal ());
1180 xstormy16_epilogue_uses (int regno
)
1182 if (reload_completed
&& call_used_regs
[regno
])
1184 const int ifun
= xstormy16_interrupt_function_p ();
1185 return REG_NEEDS_SAVE (regno
, ifun
);
1191 xstormy16_function_profiler (void)
1193 sorry ("function_profiler support");
1197 /* Return an updated summarizer variable CUM to advance past an
1198 argument in the argument list. The values MODE, TYPE and NAMED
1199 describe that argument. Once this is done, the variable CUM is
1200 suitable for analyzing the *following* argument with
1201 `FUNCTION_ARG', etc.
1203 This function need not do anything if the argument in question was
1204 passed on the stack. The compiler knows how to track the amount of
1205 stack space used for arguments without any special help. However,
1206 it makes life easier for xstormy16_build_va_list if it does update
1209 xstormy16_function_arg_advance (CUMULATIVE_ARGS cum
, enum machine_mode mode
,
1210 tree type
, int named ATTRIBUTE_UNUSED
)
1212 /* If an argument would otherwise be passed partially in registers,
1213 and partially on the stack, the whole of it is passed on the
1215 if (cum
< NUM_ARGUMENT_REGISTERS
1216 && cum
+ XSTORMY16_WORD_SIZE (type
, mode
) > NUM_ARGUMENT_REGISTERS
)
1217 cum
= NUM_ARGUMENT_REGISTERS
;
1219 cum
+= XSTORMY16_WORD_SIZE (type
, mode
);
1225 xstormy16_function_arg (CUMULATIVE_ARGS cum
, enum machine_mode mode
,
1226 tree type
, int named ATTRIBUTE_UNUSED
)
1228 if (mode
== VOIDmode
)
1230 if (MUST_PASS_IN_STACK (mode
, type
)
1231 || cum
+ XSTORMY16_WORD_SIZE (type
, mode
) > NUM_ARGUMENT_REGISTERS
)
1233 return gen_rtx_REG (mode
, cum
+ 2);
1236 /* Do any needed setup for a variadic function. CUM has not been updated
1237 for the last named argument which has type TYPE and mode MODE. */
1239 xstormy16_setup_incoming_varargs (CUMULATIVE_ARGS cum ATTRIBUTE_UNUSED
,
1240 int int_mode ATTRIBUTE_UNUSED
,
1241 tree type ATTRIBUTE_UNUSED
,
1242 int *pretend_size ATTRIBUTE_UNUSED
)
1246 /* Build the va_list type.
1248 For this chip, va_list is a record containing a counter and a pointer.
1249 The counter is of type 'int' and indicates how many bytes
1250 have been used to date. The pointer indicates the stack position
1251 for arguments that have not been passed in registers.
1252 To keep the layout nice, the pointer is first in the structure. */
1255 xstormy16_build_va_list (void)
1257 tree f_1
, f_2
, record
, type_decl
;
1259 record
= (*lang_hooks
.types
.make_type
) (RECORD_TYPE
);
1260 type_decl
= build_decl (TYPE_DECL
, get_identifier ("__va_list_tag"), record
);
1262 f_1
= build_decl (FIELD_DECL
, get_identifier ("base"),
1264 f_2
= build_decl (FIELD_DECL
, get_identifier ("count"),
1265 unsigned_type_node
);
1267 DECL_FIELD_CONTEXT (f_1
) = record
;
1268 DECL_FIELD_CONTEXT (f_2
) = record
;
1270 TREE_CHAIN (record
) = type_decl
;
1271 TYPE_NAME (record
) = type_decl
;
1272 TYPE_FIELDS (record
) = f_1
;
1273 TREE_CHAIN (f_1
) = f_2
;
1275 layout_type (record
);
1280 /* Implement the stdarg/varargs va_start macro. STDARG_P is nonzero if this
1281 is stdarg.h instead of varargs.h. VALIST is the tree of the va_list
1282 variable to initialize. NEXTARG is the machine independent notion of the
1283 'next' argument after the variable arguments. */
1285 xstormy16_expand_builtin_va_start (tree valist
, rtx nextarg ATTRIBUTE_UNUSED
)
1287 tree f_base
, f_count
;
1291 if (xstormy16_interrupt_function_p ())
1292 error ("cannot use va_start in interrupt function");
1294 f_base
= TYPE_FIELDS (va_list_type_node
);
1295 f_count
= TREE_CHAIN (f_base
);
1297 base
= build (COMPONENT_REF
, TREE_TYPE (f_base
), valist
, f_base
);
1298 count
= build (COMPONENT_REF
, TREE_TYPE (f_count
), valist
, f_count
);
1300 t
= make_tree (TREE_TYPE (base
), virtual_incoming_args_rtx
);
1301 t
= build (PLUS_EXPR
, TREE_TYPE (base
), t
,
1302 build_int_2 (INCOMING_FRAME_SP_OFFSET
, 0));
1303 t
= build (MODIFY_EXPR
, TREE_TYPE (base
), base
, t
);
1304 TREE_SIDE_EFFECTS (t
) = 1;
1305 expand_expr (t
, const0_rtx
, VOIDmode
, EXPAND_NORMAL
);
1307 t
= build (MODIFY_EXPR
, TREE_TYPE (count
), count
,
1308 build_int_2 (current_function_args_info
* UNITS_PER_WORD
, 0));
1309 TREE_SIDE_EFFECTS (t
) = 1;
1310 expand_expr (t
, const0_rtx
, VOIDmode
, EXPAND_NORMAL
);
1313 /* Implement the stdarg/varargs va_arg macro. VALIST is the variable
1314 of type va_list as a tree, TYPE is the type passed to va_arg.
1315 Note: This algorithm is documented in stormy-abi. */
1318 xstormy16_expand_builtin_va_arg (tree valist
, tree type
)
1320 tree f_base
, f_count
;
1322 rtx count_rtx
, addr_rtx
, r
;
1323 rtx lab_gotaddr
, lab_fromstack
;
1325 int size
, size_of_reg_args
, must_stack
;
1326 tree size_tree
, count_plus_size
;
1327 rtx count_plus_size_rtx
;
1329 f_base
= TYPE_FIELDS (va_list_type_node
);
1330 f_count
= TREE_CHAIN (f_base
);
1332 base
= build (COMPONENT_REF
, TREE_TYPE (f_base
), valist
, f_base
);
1333 count
= build (COMPONENT_REF
, TREE_TYPE (f_count
), valist
, f_count
);
1335 must_stack
= MUST_PASS_IN_STACK (TYPE_MODE (type
), type
);
1336 size_tree
= round_up (size_in_bytes (type
), UNITS_PER_WORD
);
1338 size_of_reg_args
= NUM_ARGUMENT_REGISTERS
* UNITS_PER_WORD
;
1340 count_rtx
= expand_expr (count
, NULL_RTX
, HImode
, EXPAND_NORMAL
);
1341 lab_gotaddr
= gen_label_rtx ();
1342 lab_fromstack
= gen_label_rtx ();
1343 addr_rtx
= gen_reg_rtx (Pmode
);
1347 count_plus_size
= build (PLUS_EXPR
, TREE_TYPE (count
), count
, size_tree
);
1348 count_plus_size_rtx
= expand_expr (count_plus_size
, NULL_RTX
, HImode
, EXPAND_NORMAL
);
1349 emit_cmp_and_jump_insns (count_plus_size_rtx
, GEN_INT (size_of_reg_args
),
1350 GTU
, const1_rtx
, HImode
, 1, lab_fromstack
);
1352 t
= build (PLUS_EXPR
, ptr_type_node
, base
, count
);
1353 r
= expand_expr (t
, addr_rtx
, Pmode
, EXPAND_NORMAL
);
1355 emit_move_insn (addr_rtx
, r
);
1357 emit_jump_insn (gen_jump (lab_gotaddr
));
1359 emit_label (lab_fromstack
);
1362 /* Arguments larger than a word might need to skip over some
1363 registers, since arguments are either passed entirely in
1364 registers or entirely on the stack. */
1365 size
= PUSH_ROUNDING (int_size_in_bytes (type
));
1366 if (size
> 2 || size
< 0 || must_stack
)
1368 rtx lab_notransition
= gen_label_rtx ();
1369 emit_cmp_and_jump_insns (count_rtx
, GEN_INT (NUM_ARGUMENT_REGISTERS
1371 GEU
, const1_rtx
, HImode
, 1, lab_notransition
);
1373 t
= build (MODIFY_EXPR
, TREE_TYPE (count
), count
,
1374 build_int_2 (NUM_ARGUMENT_REGISTERS
* UNITS_PER_WORD
, 0));
1375 TREE_SIDE_EFFECTS (t
) = 1;
1376 expand_expr (t
, const0_rtx
, VOIDmode
, EXPAND_NORMAL
);
1378 emit_label (lab_notransition
);
1381 t
= build (PLUS_EXPR
, sizetype
, size_tree
,
1382 build_int_2 ((- NUM_ARGUMENT_REGISTERS
* UNITS_PER_WORD
1383 + INCOMING_FRAME_SP_OFFSET
),
1385 t
= build (PLUS_EXPR
, TREE_TYPE (count
), count
, fold (t
));
1386 t
= build (MINUS_EXPR
, TREE_TYPE (base
), base
, t
);
1387 r
= expand_expr (t
, addr_rtx
, Pmode
, EXPAND_NORMAL
);
1389 emit_move_insn (addr_rtx
, r
);
1391 emit_label (lab_gotaddr
);
1393 count_plus_size
= build (PLUS_EXPR
, TREE_TYPE (count
), count
, size_tree
);
1394 t
= build (MODIFY_EXPR
, TREE_TYPE (count
), count
, count_plus_size
);
1395 TREE_SIDE_EFFECTS (t
) = 1;
1396 expand_expr (t
, const0_rtx
, VOIDmode
, EXPAND_NORMAL
);
1401 /* Initialize the variable parts of a trampoline. ADDR is an RTX for
1402 the address of the trampoline; FNADDR is an RTX for the address of
1403 the nested function; STATIC_CHAIN is an RTX for the static chain
1404 value that should be passed to the function when it is called. */
1406 xstormy16_initialize_trampoline (rtx addr
, rtx fnaddr
, rtx static_chain
)
1408 rtx reg_addr
= gen_reg_rtx (Pmode
);
1409 rtx temp
= gen_reg_rtx (HImode
);
1410 rtx reg_fnaddr
= gen_reg_rtx (HImode
);
1413 reg_addr_mem
= gen_rtx_MEM (HImode
, reg_addr
);
1415 emit_move_insn (reg_addr
, addr
);
1416 emit_move_insn (temp
, GEN_INT (0x3130 | STATIC_CHAIN_REGNUM
));
1417 emit_move_insn (reg_addr_mem
, temp
);
1418 emit_insn (gen_addhi3 (reg_addr
, reg_addr
, const2_rtx
));
1419 emit_move_insn (temp
, static_chain
);
1420 emit_move_insn (reg_addr_mem
, temp
);
1421 emit_insn (gen_addhi3 (reg_addr
, reg_addr
, const2_rtx
));
1422 emit_move_insn (reg_fnaddr
, fnaddr
);
1423 emit_move_insn (temp
, reg_fnaddr
);
1424 emit_insn (gen_andhi3 (temp
, temp
, GEN_INT (0xFF)));
1425 emit_insn (gen_iorhi3 (temp
, temp
, GEN_INT (0x0200)));
1426 emit_move_insn (reg_addr_mem
, temp
);
1427 emit_insn (gen_addhi3 (reg_addr
, reg_addr
, const2_rtx
));
1428 emit_insn (gen_lshrhi3 (reg_fnaddr
, reg_fnaddr
, GEN_INT (8)));
1429 emit_move_insn (reg_addr_mem
, reg_fnaddr
);
1432 /* Create an RTX representing the place where a function returns a
1433 value of data type VALTYPE. VALTYPE is a tree node representing a
1434 data type. Write `TYPE_MODE (VALTYPE)' to get the machine mode
1435 used to represent that type. On many machines, only the mode is
1436 relevant. (Actually, on most machines, scalar values are returned
1437 in the same place regardless of mode).
1439 If `PROMOTE_FUNCTION_RETURN' is defined, you must apply the same promotion
1440 rules specified in `PROMOTE_MODE' if VALTYPE is a scalar type.
1442 If the precise function being called is known, FUNC is a tree node
1443 (`FUNCTION_DECL') for it; otherwise, FUNC is a null pointer. This makes it
1444 possible to use a different value-returning convention for specific
1445 functions when all their calls are known.
1447 `FUNCTION_VALUE' is not used for return vales with aggregate data types,
1448 because these are returned in another way. See `STRUCT_VALUE_REGNUM' and
1451 xstormy16_function_value (tree valtype
, tree func ATTRIBUTE_UNUSED
)
1453 enum machine_mode mode
;
1454 mode
= TYPE_MODE (valtype
);
1455 PROMOTE_MODE (mode
, 0, valtype
);
1456 return gen_rtx_REG (mode
, RETURN_VALUE_REGNUM
);
1459 /* A C compound statement that outputs the assembler code for a thunk function,
1460 used to implement C++ virtual function calls with multiple inheritance. The
1461 thunk acts as a wrapper around a virtual function, adjusting the implicit
1462 object parameter before handing control off to the real function.
1464 First, emit code to add the integer DELTA to the location that contains the
1465 incoming first argument. Assume that this argument contains a pointer, and
1466 is the one used to pass the `this' pointer in C++. This is the incoming
1467 argument *before* the function prologue, e.g. `%o0' on a sparc. The
1468 addition must preserve the values of all other incoming arguments.
1470 After the addition, emit code to jump to FUNCTION, which is a
1471 `FUNCTION_DECL'. This is a direct pure jump, not a call, and does not touch
1472 the return address. Hence returning from FUNCTION will return to whoever
1473 called the current `thunk'.
1475 The effect must be as if @var{function} had been called directly
1476 with the adjusted first argument. This macro is responsible for
1477 emitting all of the code for a thunk function;
1478 TARGET_ASM_FUNCTION_PROLOGUE and TARGET_ASM_FUNCTION_EPILOGUE are
1481 The THUNK_FNDECL is redundant. (DELTA and FUNCTION have already been
1482 extracted from it.) It might possibly be useful on some targets, but
1486 xstormy16_asm_output_mi_thunk (FILE *file
,
1487 tree thunk_fndecl ATTRIBUTE_UNUSED
,
1488 HOST_WIDE_INT delta
,
1489 HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED
,
1492 int regnum
= FIRST_ARGUMENT_REGISTER
;
1494 /* There might be a hidden first argument for a returned structure. */
1495 if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function
)), function
))
1498 fprintf (file
, "\tadd %s,#0x%x\n", reg_names
[regnum
], (int) delta
& 0xFFFF);
1499 fputs ("\tjmpf ", file
);
1500 assemble_name (file
, XSTR (XEXP (DECL_RTL (function
), 0), 0));
1504 /* Output constructors and destructors. Just like
1505 default_named_section_asm_out_* but don't set the sections writable. */
1506 #undef TARGET_ASM_CONSTRUCTOR
1507 #define TARGET_ASM_CONSTRUCTOR xstormy16_asm_out_constructor
1508 #undef TARGET_ASM_DESTRUCTOR
1509 #define TARGET_ASM_DESTRUCTOR xstormy16_asm_out_destructor
1512 xstormy16_asm_out_destructor (rtx symbol
, int priority
)
1514 const char *section
= ".dtors";
1517 /* ??? This only works reliably with the GNU linker. */
1518 if (priority
!= DEFAULT_INIT_PRIORITY
)
1520 sprintf (buf
, ".dtors.%.5u",
1521 /* Invert the numbering so the linker puts us in the proper
1522 order; constructors are run from right to left, and the
1523 linker sorts in increasing order. */
1524 MAX_INIT_PRIORITY
- priority
);
1528 named_section_flags (section
, 0);
1529 assemble_align (POINTER_SIZE
);
1530 assemble_integer (symbol
, POINTER_SIZE
/ BITS_PER_UNIT
, POINTER_SIZE
, 1);
1534 xstormy16_asm_out_constructor (rtx symbol
, int priority
)
1536 const char *section
= ".ctors";
1539 /* ??? This only works reliably with the GNU linker. */
1540 if (priority
!= DEFAULT_INIT_PRIORITY
)
1542 sprintf (buf
, ".ctors.%.5u",
1543 /* Invert the numbering so the linker puts us in the proper
1544 order; constructors are run from right to left, and the
1545 linker sorts in increasing order. */
1546 MAX_INIT_PRIORITY
- priority
);
1550 named_section_flags (section
, 0);
1551 assemble_align (POINTER_SIZE
);
1552 assemble_integer (symbol
, POINTER_SIZE
/ BITS_PER_UNIT
, POINTER_SIZE
, 1);
1555 /* Print a memory address as an operand to reference that memory location. */
1557 xstormy16_print_operand_address (FILE *file
, rtx address
)
1559 HOST_WIDE_INT offset
;
1560 int pre_dec
, post_inc
;
1562 /* There are a few easy cases. */
1563 if (GET_CODE (address
) == CONST_INT
)
1565 fprintf (file
, HOST_WIDE_INT_PRINT_DEC
, INTVAL (address
) & 0xFFFF);
1569 if (CONSTANT_P (address
) || GET_CODE (address
) == CODE_LABEL
)
1571 output_addr_const (file
, address
);
1575 /* Otherwise, it's hopefully something of the form
1576 (plus:HI (pre_dec:HI (reg:HI ...)) (const_int ...))
1579 if (GET_CODE (address
) == PLUS
)
1581 if (GET_CODE (XEXP (address
, 1)) != CONST_INT
)
1583 offset
= INTVAL (XEXP (address
, 1));
1584 address
= XEXP (address
, 0);
1589 pre_dec
= (GET_CODE (address
) == PRE_DEC
);
1590 post_inc
= (GET_CODE (address
) == POST_INC
);
1591 if (pre_dec
|| post_inc
)
1592 address
= XEXP (address
, 0);
1594 if (GET_CODE (address
) != REG
)
1600 fputs (reg_names
[REGNO (address
)], file
);
1604 fprintf (file
, "," HOST_WIDE_INT_PRINT_DEC
, offset
);
1608 /* Print an operand to an assembler instruction. */
1610 xstormy16_print_operand (FILE *file
, rtx x
, int code
)
1615 /* There is either one bit set, or one bit clear, in X.
1616 Print it preceded by '#'. */
1618 HOST_WIDE_INT xx
= 1;
1621 if (GET_CODE (x
) == CONST_INT
)
1624 output_operand_lossage ("`B' operand is not constant");
1626 l
= exact_log2 (xx
);
1628 l
= exact_log2 (~xx
);
1630 output_operand_lossage ("`B' operand has multiple bits set");
1632 fprintf (file
, IMMEDIATE_PREFIX HOST_WIDE_INT_PRINT_DEC
, l
);
1637 /* Print the symbol without a surrounding @fptr(). */
1638 if (GET_CODE (x
) == SYMBOL_REF
)
1639 assemble_name (file
, XSTR (x
, 0));
1640 else if (GET_CODE (x
) == LABEL_REF
)
1641 output_asm_label (x
);
1643 xstormy16_print_operand_address (file
, x
);
1648 /* Print the immediate operand less one, preceded by '#'.
1649 For 'O', negate it first. */
1651 HOST_WIDE_INT xx
= 0;
1653 if (GET_CODE (x
) == CONST_INT
)
1656 output_operand_lossage ("`o' operand is not constant");
1661 fprintf (file
, IMMEDIATE_PREFIX HOST_WIDE_INT_PRINT_DEC
, xx
- 1);
1666 /* Handled below. */
1670 output_operand_lossage ("xstormy16_print_operand: unknown code");
1674 switch (GET_CODE (x
))
1677 fputs (reg_names
[REGNO (x
)], file
);
1681 xstormy16_print_operand_address (file
, XEXP (x
, 0));
1685 /* Some kind of constant or label; an immediate operand,
1686 so prefix it with '#' for the assembler. */
1687 fputs (IMMEDIATE_PREFIX
, file
);
1688 output_addr_const (file
, x
);
1696 /* Expander for the `casesi' pattern.
1697 INDEX is the index of the switch statement.
1698 LOWER_BOUND is a CONST_INT that is the value of INDEX corresponding
1699 to the first table entry.
1700 RANGE is the number of table entries.
1701 TABLE is an ADDR_VEC that is the jump table.
1702 DEFAULT_LABEL is the address to branch to if INDEX is outside the
1703 range LOWER_BOUND to LOWER_BOUND+RANGE-1.
1707 xstormy16_expand_casesi (rtx index
, rtx lower_bound
, rtx range
,
1708 rtx table
, rtx default_label
)
1710 HOST_WIDE_INT range_i
= INTVAL (range
);
1713 /* This code uses 'br', so it can deal only with tables of size up to
1715 if (range_i
>= 8192)
1716 sorry ("switch statement of size %lu entries too large",
1717 (unsigned long) range_i
);
1719 index
= expand_binop (SImode
, sub_optab
, index
, lower_bound
, NULL_RTX
, 0,
1721 emit_cmp_and_jump_insns (index
, range
, GTU
, NULL_RTX
, SImode
, 1,
1723 int_index
= gen_lowpart_common (HImode
, index
);
1724 emit_insn (gen_ashlhi3 (int_index
, int_index
, GEN_INT (2)));
1725 emit_jump_insn (gen_tablejump_pcrel (int_index
, table
));
1728 /* Output an ADDR_VEC. It is output as a sequence of 'jmpf'
1729 instructions, without label or alignment or any other special
1730 constructs. We know that the previous instruction will be the
1731 `tablejump_pcrel' output above.
1733 TODO: it might be nice to output 'br' instructions if they could
1737 xstormy16_output_addr_vec (FILE *file
, rtx label ATTRIBUTE_UNUSED
, rtx table
)
1741 function_section (current_function_decl
);
1743 vlen
= XVECLEN (table
, 0);
1744 for (idx
= 0; idx
< vlen
; idx
++)
1746 fputs ("\tjmpf ", file
);
1747 output_asm_label (XEXP (XVECEXP (table
, 0, idx
), 0));
1753 /* Expander for the `call' patterns.
1754 INDEX is the index of the switch statement.
1755 LOWER_BOUND is a CONST_INT that is the value of INDEX corresponding
1756 to the first table entry.
1757 RANGE is the number of table entries.
1758 TABLE is an ADDR_VEC that is the jump table.
1759 DEFAULT_LABEL is the address to branch to if INDEX is outside the
1760 range LOWER_BOUND to LOWER_BOUND+RANGE-1.
1764 xstormy16_expand_call (rtx retval
, rtx dest
, rtx counter
)
1767 enum machine_mode mode
;
1769 if (GET_CODE (dest
) != MEM
)
1771 dest
= XEXP (dest
, 0);
1773 if (! CONSTANT_P (dest
)
1774 && GET_CODE (dest
) != REG
)
1775 dest
= force_reg (Pmode
, dest
);
1780 mode
= GET_MODE (retval
);
1782 call
= gen_rtx_CALL (mode
, gen_rtx_MEM (FUNCTION_MODE
, dest
),
1785 call
= gen_rtx_SET (VOIDmode
, retval
, call
);
1787 if (! CONSTANT_P (dest
))
1789 temp
= gen_reg_rtx (HImode
);
1790 emit_move_insn (temp
, const0_rtx
);
1795 call
= gen_rtx_PARALLEL (VOIDmode
, gen_rtvec (2, call
,
1796 gen_rtx_USE (VOIDmode
, temp
)));
1797 emit_call_insn (call
);
1800 /* Expanders for multiword computational operations. */
1802 /* Expander for arithmetic operations; emit insns to compute
1804 (set DEST (CODE:MODE SRC0 SRC1))
1806 using CARRY as a temporary. When CODE is COMPARE, a branch
1807 template is generated (this saves duplicating code in
1808 xstormy16_split_cbranch). */
1811 xstormy16_expand_arith (enum machine_mode mode
, enum rtx_code code
,
1812 rtx dest
, rtx src0
, rtx src1
, rtx carry
)
1814 int num_words
= GET_MODE_BITSIZE (mode
) / BITS_PER_WORD
;
1819 emit_move_insn (src0
, const0_rtx
);
1821 for (i
= 0; i
< num_words
; i
++)
1823 rtx w_src0
, w_src1
, w_dest
;
1826 w_src0
= simplify_gen_subreg (word_mode
, src0
, mode
,
1827 i
* UNITS_PER_WORD
);
1828 w_src1
= simplify_gen_subreg (word_mode
, src1
, mode
, i
* UNITS_PER_WORD
);
1829 w_dest
= simplify_gen_subreg (word_mode
, dest
, mode
, i
* UNITS_PER_WORD
);
1835 && GET_CODE (w_src1
) == CONST_INT
&& INTVAL (w_src1
) == 0)
1839 insn
= gen_addchi4 (w_dest
, w_src0
, w_src1
, carry
);
1841 insn
= gen_addchi5 (w_dest
, w_src0
, w_src1
, carry
, carry
);
1847 if (code
== COMPARE
&& i
== num_words
- 1)
1849 rtx branch
, sub
, clobber
, sub_1
;
1851 sub_1
= gen_rtx_MINUS (HImode
, w_src0
,
1852 gen_rtx_ZERO_EXTEND (HImode
, carry
));
1853 sub
= gen_rtx_SET (VOIDmode
, w_dest
,
1854 gen_rtx_MINUS (HImode
, sub_1
, w_src1
));
1855 clobber
= gen_rtx_CLOBBER (VOIDmode
, carry
);
1856 branch
= gen_rtx_SET (VOIDmode
, pc_rtx
,
1857 gen_rtx_IF_THEN_ELSE (VOIDmode
,
1863 insn
= gen_rtx_PARALLEL (VOIDmode
,
1864 gen_rtvec (3, branch
, sub
, clobber
));
1868 && GET_CODE (w_src1
) == CONST_INT
&& INTVAL (w_src1
) == 0)
1871 insn
= gen_subchi4 (w_dest
, w_src0
, w_src1
, carry
);
1873 insn
= gen_subchi5 (w_dest
, w_src0
, w_src1
, carry
, carry
);
1879 if (GET_CODE (w_src1
) == CONST_INT
1880 && INTVAL (w_src1
) == -(code
== AND
))
1883 insn
= gen_rtx_SET (VOIDmode
, w_dest
, gen_rtx (code
, mode
,
1888 insn
= gen_rtx_SET (VOIDmode
, w_dest
, gen_rtx_NOT (mode
, w_src0
));
1899 /* If we emit nothing, try_split() will think we failed. So emit
1900 something that does nothing and can be optimized away. */
1905 /* Return 1 if OP is a shift operator. */
1908 shift_operator (register rtx op
, enum machine_mode mode ATTRIBUTE_UNUSED
)
1910 enum rtx_code code
= GET_CODE (op
);
1912 return (code
== ASHIFT
1914 || code
== LSHIFTRT
);
1917 /* The shift operations are split at output time for constant values;
1918 variable-width shifts get handed off to a library routine.
1920 Generate an output string to do (set X (CODE:MODE X SIZE_R))
1921 SIZE_R will be a CONST_INT, X will be a hard register. */
1924 xstormy16_output_shift (enum machine_mode mode
, enum rtx_code code
,
1925 rtx x
, rtx size_r
, rtx temp
)
1928 const char *r0
, *r1
, *rt
;
1931 if (GET_CODE (size_r
) != CONST_INT
1932 || GET_CODE (x
) != REG
1935 size
= INTVAL (size_r
) & (GET_MODE_BITSIZE (mode
) - 1);
1940 r0
= reg_names
[REGNO (x
)];
1941 r1
= reg_names
[REGNO (x
) + 1];
1943 /* For shifts of size 1, we can use the rotate instructions. */
1949 sprintf (r
, "shl %s,#1 | rlc %s,#1", r0
, r1
);
1952 sprintf (r
, "asr %s,#1 | rrc %s,#1", r1
, r0
);
1955 sprintf (r
, "shr %s,#1 | rrc %s,#1", r1
, r0
);
1963 /* For large shifts, there are easy special cases. */
1969 sprintf (r
, "mov %s,%s | mov %s,#0", r1
, r0
, r0
);
1972 sprintf (r
, "mov %s,%s | asr %s,#15", r0
, r1
, r1
);
1975 sprintf (r
, "mov %s,%s | mov %s,#0", r0
, r1
, r1
);
1987 sprintf (r
, "mov %s,%s | mov %s,#0 | shl %s,#%d",
1988 r1
, r0
, r0
, r1
, (int) size
- 16);
1991 sprintf (r
, "mov %s,%s | asr %s,#15 | asr %s,#%d",
1992 r0
, r1
, r1
, r0
, (int) size
- 16);
1995 sprintf (r
, "mov %s,%s | mov %s,#0 | shr %s,#%d",
1996 r0
, r1
, r1
, r0
, (int) size
- 16);
2004 /* For the rest, we have to do more work. In particular, we
2005 need a temporary. */
2006 rt
= reg_names
[REGNO (temp
)];
2011 "mov %s,%s | shl %s,#%d | shl %s,#%d | shr %s,#%d | or %s,%s",
2012 rt
, r0
, r0
, (int) size
, r1
, (int) size
, rt
, (int) (16-size
),
2017 "mov %s,%s | asr %s,#%d | shr %s,#%d | shl %s,#%d | or %s,%s",
2018 rt
, r1
, r1
, (int) size
, r0
, (int) size
, rt
, (int) (16-size
),
2023 "mov %s,%s | shr %s,#%d | shr %s,#%d | shl %s,#%d | or %s,%s",
2024 rt
, r1
, r1
, (int) size
, r0
, (int) size
, rt
, (int) (16-size
),
2033 /* Attribute handling. */
2035 /* Return nonzero if the function is an interrupt function. */
2037 xstormy16_interrupt_function_p (void)
2041 /* The dwarf2 mechanism asks for INCOMING_FRAME_SP_OFFSET before
2042 any functions are declared, which is demonstrably wrong, but
2043 it is worked around here. FIXME. */
2047 attributes
= TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl
));
2048 return lookup_attribute ("interrupt", attributes
) != NULL_TREE
;
2051 #undef TARGET_ATTRIBUTE_TABLE
2052 #define TARGET_ATTRIBUTE_TABLE xstormy16_attribute_table
2053 static tree xstormy16_handle_interrupt_attribute
2054 (tree
*, tree
, tree
, int, bool *);
2056 static const struct attribute_spec xstormy16_attribute_table
[] =
2058 /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
2059 { "interrupt", 0, 0, false, true, true, xstormy16_handle_interrupt_attribute
},
2060 { NULL
, 0, 0, false, false, false, NULL
}
2063 /* Handle an "interrupt" attribute;
2064 arguments as in struct attribute_spec.handler. */
2066 xstormy16_handle_interrupt_attribute (tree
*node
, tree name
,
2067 tree args ATTRIBUTE_UNUSED
,
2068 int flags ATTRIBUTE_UNUSED
,
2071 if (TREE_CODE (*node
) != FUNCTION_TYPE
)
2073 warning ("`%s' attribute only applies to functions",
2074 IDENTIFIER_POINTER (name
));
2075 *no_add_attrs
= true;
2081 #undef TARGET_INIT_BUILTINS
2082 #define TARGET_INIT_BUILTINS xstormy16_init_builtins
2083 #undef TARGET_EXPAND_BUILTIN
2084 #define TARGET_EXPAND_BUILTIN xstormy16_expand_builtin
2089 const char *arg_ops
; /* 0..9, t for temp register, r for return value */
2090 const char *arg_types
; /* s=short,l=long, upper case for unsigned */
2092 { "__sdivlh", CODE_FOR_sdivlh
, "rt01", "sls" },
2093 { "__smodlh", CODE_FOR_sdivlh
, "tr01", "sls" },
2094 { "__udivlh", CODE_FOR_udivlh
, "rt01", "SLS" },
2095 { "__umodlh", CODE_FOR_udivlh
, "tr01", "SLS" },
2100 xstormy16_init_builtins (void)
2102 tree args
, ret_type
, arg
;
2105 ret_type
= void_type_node
;
2107 for (i
=0; s16builtins
[i
].name
; i
++)
2109 args
= void_list_node
;
2110 for (a
=strlen (s16builtins
[i
].arg_types
)-1; a
>=0; a
--)
2112 switch (s16builtins
[i
].arg_types
[a
])
2114 case 's': arg
= short_integer_type_node
; break;
2115 case 'S': arg
= short_unsigned_type_node
; break;
2116 case 'l': arg
= long_integer_type_node
; break;
2117 case 'L': arg
= long_unsigned_type_node
; break;
2123 args
= tree_cons (NULL_TREE
, arg
, args
);
2125 builtin_function (s16builtins
[i
].name
,
2126 build_function_type (ret_type
, args
),
2127 i
, BUILT_IN_MD
, NULL
, NULL
);
2132 xstormy16_expand_builtin(tree exp
, rtx target
,
2133 rtx subtarget ATTRIBUTE_UNUSED
,
2134 enum machine_mode mode ATTRIBUTE_UNUSED
,
2135 int ignore ATTRIBUTE_UNUSED
)
2137 rtx op
[10], args
[10], pat
, copyto
[10], retval
= 0;
2138 tree fndecl
, argtree
;
2141 fndecl
= TREE_OPERAND (TREE_OPERAND (exp
, 0), 0);
2142 argtree
= TREE_OPERAND (exp
, 1);
2143 i
= DECL_FUNCTION_CODE (fndecl
);
2144 code
= s16builtins
[i
].md_code
;
2146 for (a
= 0; a
< 10 && argtree
; a
++)
2148 args
[a
] = expand_expr (TREE_VALUE (argtree
), NULL_RTX
, VOIDmode
, 0);
2149 argtree
= TREE_CHAIN (argtree
);
2152 for (o
= 0; s16builtins
[i
].arg_ops
[o
]; o
++)
2154 char ao
= s16builtins
[i
].arg_ops
[o
];
2155 char c
= insn_data
[code
].operand
[o
].constraint
[0];
2160 omode
= insn_data
[code
].operand
[o
].mode
;
2162 op
[o
] = target
? target
: gen_reg_rtx (omode
);
2164 op
[o
] = gen_reg_rtx (omode
);
2166 op
[o
] = args
[(int) hex_value (ao
)];
2168 if (! (*insn_data
[code
].operand
[o
].predicate
) (op
[o
], GET_MODE (op
[o
])))
2170 if (c
== '+' || c
== '=')
2173 op
[o
] = gen_reg_rtx (omode
);
2176 op
[o
] = copy_to_mode_reg (omode
, op
[o
]);
2183 pat
= GEN_FCN (code
) (op
[0], op
[1], op
[2], op
[3], op
[4],
2184 op
[5], op
[6], op
[7], op
[8], op
[9]);
2187 for (o
= 0; s16builtins
[i
].arg_ops
[o
]; o
++)
2190 emit_move_insn (copyto
[o
], op
[o
]);
2191 if (op
[o
] == retval
)
2199 #undef TARGET_ASM_ALIGNED_HI_OP
2200 #define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
2201 #undef TARGET_ASM_ALIGNED_SI_OP
2202 #define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
2204 #undef TARGET_ASM_OUTPUT_MI_THUNK
2205 #define TARGET_ASM_OUTPUT_MI_THUNK xstormy16_asm_output_mi_thunk
2206 #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
2207 #define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
2209 #undef TARGET_RTX_COSTS
2210 #define TARGET_RTX_COSTS xstormy16_rtx_costs
2211 #undef TARGET_ADDRESS_COST
2212 #define TARGET_ADDRESS_COST xstormy16_address_cost
2214 struct gcc_target targetm
= TARGET_INITIALIZER
;