OSDN Git Service

193be195012a8f7fa578fc925ce2bf925ef36904
[pf3gnuchains/gcc-fork.git] / gcc / config / stormy16 / stormy16.c
1 /* Xstormy16 target functions.
2    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002
3    Free Software Foundation, Inc.
4    Contributed by Red Hat, Inc.
5
6 This file is part of GCC.
7
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)
11 any later version.
12
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.
17
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.  */
22
23 #include "config.h"
24 #include "system.h"
25 #include "coretypes.h"
26 #include "tm.h"
27 #include "rtl.h"
28 #include "regs.h"
29 #include "hard-reg-set.h"
30 #include "real.h"
31 #include "insn-config.h"
32 #include "conditions.h"
33 #include "insn-flags.h"
34 #include "output.h"
35 #include "insn-attr.h"
36 #include "flags.h"
37 #include "recog.h"
38 #include "toplev.h"
39 #include "obstack.h"
40 #include "tree.h"
41 #include "expr.h"
42 #include "optabs.h"
43 #include "output.h"
44 #include "except.h"
45 #include "function.h"
46 #include "target.h"
47 #include "target-def.h"
48 #include "tm_p.h"
49 #include "langhooks.h"
50
51 static rtx emit_addhi3_postreload PARAMS ((rtx, rtx, rtx));
52 static void xstormy16_asm_out_constructor PARAMS ((rtx, int));
53 static void xstormy16_asm_out_destructor PARAMS ((rtx, int));
54 static void xstormy16_asm_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT,
55                                                    HOST_WIDE_INT, tree));
56
57 static void xstormy16_init_builtins PARAMS ((void));
58 static rtx xstormy16_expand_builtin PARAMS ((tree, rtx, rtx, enum machine_mode, int));
59 static bool xstormy16_rtx_costs PARAMS ((rtx, int, int, int *));
60 static int xstormy16_address_cost PARAMS ((rtx));
61
62 /* Define the information needed to generate branch and scc insns.  This is
63    stored from the compare operation.  */
64 struct rtx_def * xstormy16_compare_op0;
65 struct rtx_def * xstormy16_compare_op1;
66
67 /* Return 1 if this is a LT, GE, LTU, or GEU operator.  */
68
69 int
70 xstormy16_ineqsi_operator (op, mode)
71     register rtx op;
72     enum machine_mode mode;
73 {
74   enum rtx_code code = GET_CODE (op);
75   
76   return ((mode == VOIDmode || GET_MODE (op) == mode)
77           && (code == LT || code == GE || code == LTU || code == GEU));
78 }
79
80 /* Return 1 if this is an EQ or NE operator.  */
81
82 int
83 equality_operator (op, mode)
84     register rtx op;
85     enum machine_mode mode;
86 {
87   return ((mode == VOIDmode || GET_MODE (op) == mode)
88           && (GET_CODE (op) == EQ || GET_CODE (op) == NE));
89 }
90
91 /* Return 1 if this is a comparison operator but not an EQ or NE operator.  */
92
93 int
94 inequality_operator (op, mode)
95     register rtx op;
96     enum machine_mode mode;
97 {
98   return comparison_operator (op, mode) && ! equality_operator (op, mode);
99 }
100
101 /* Compute a (partial) cost for rtx X.  Return true if the complete
102    cost has been computed, and false if subexpressions should be
103    scanned.  In either case, *TOTAL contains the cost result.  */
104
105 static bool
106 xstormy16_rtx_costs (x, code, outer_code, total)
107      rtx x;
108      int code, outer_code ATTRIBUTE_UNUSED;
109      int *total;
110 {
111   switch (code)
112     {
113     case CONST_INT:
114       if (INTVAL (x) < 16 && INTVAL (x) >= 0)
115         *total = COSTS_N_INSNS (1) / 2;
116       else if (INTVAL (x) < 256 && INTVAL (x) >= 0)
117         *total = COSTS_N_INSNS (1);
118       else
119         *total = COSTS_N_INSNS (2);
120       return true;
121
122     case CONST_DOUBLE:
123     case CONST:
124     case SYMBOL_REF:
125     case LABEL_REF:
126       *total = COSTS_N_INSNS(2);
127       return true;
128
129     case MULT:
130       *total = COSTS_N_INSNS (35 + 6);
131       return true;
132     case DIV:
133       *total = COSTS_N_INSNS (51 - 6);
134       return true;
135
136     default:
137       return false;
138     }
139 }
140
141 static int
142 xstormy16_address_cost (x)
143      rtx x;
144 {
145   return (GET_CODE (x) == CONST_INT ? 2
146           : GET_CODE (x) == PLUS ? 7
147           : 5);
148 }
149
150 /* Branches are handled as follows:
151
152    1. HImode compare-and-branches.  The machine supports these
153       natively, so the appropriate pattern is emitted directly.
154
155    2. SImode EQ and NE.  These are emitted as pairs of HImode
156       compare-and-branches.      
157
158    3. SImode LT, GE, LTU and GEU.  These are emitted as a sequence
159       of a SImode subtract followed by a branch (not a compare-and-branch),
160       like this:
161       sub
162       sbc
163       blt
164
165    4. SImode GT, LE, GTU, LEU.  These are emitted as a sequence like:
166       sub
167       sbc
168       blt
169       or
170       bne
171 */
172
173 /* Emit a branch of kind CODE to location LOC.  */
174
175 void
176 xstormy16_emit_cbranch (code, loc)
177      enum rtx_code code;
178      rtx loc;
179 {
180   rtx op0 = xstormy16_compare_op0;
181   rtx op1 = xstormy16_compare_op1;
182   rtx condition_rtx, loc_ref, branch, cy_clobber;
183   rtvec vec;
184   enum machine_mode mode;
185   
186   mode = GET_MODE (op0);
187   if (mode != HImode && mode != SImode)
188     abort ();
189
190   if (mode == SImode
191       && (code == GT || code == LE || code == GTU || code == LEU))
192     {
193       int unsigned_p = (code == GTU || code == LEU);
194       int gt_p = (code == GT || code == GTU);
195       rtx lab = NULL_RTX;
196       
197       if (gt_p)
198         lab = gen_label_rtx ();
199       xstormy16_emit_cbranch (unsigned_p ? LTU : LT, gt_p ? lab : loc);
200       /* This should be generated as a comparison against the temporary
201          created by the previous insn, but reload can't handle that.  */
202       xstormy16_emit_cbranch (gt_p ? NE : EQ, loc);
203       if (gt_p)
204         emit_label (lab);
205       return;
206     }
207   else if (mode == SImode 
208            && (code == NE || code == EQ)
209            && op1 != const0_rtx)
210     {
211       rtx lab = NULL_RTX;
212       int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
213       int i;
214       
215       if (code == EQ)
216         lab = gen_label_rtx ();
217       
218       for (i = 0; i < num_words - 1; i++)
219         {
220           xstormy16_compare_op0 = simplify_gen_subreg (word_mode, op0, mode, 
221                                                       i * UNITS_PER_WORD);
222           xstormy16_compare_op1 = simplify_gen_subreg (word_mode, op1, mode, 
223                                                       i * UNITS_PER_WORD);
224           xstormy16_emit_cbranch (NE, code == EQ ? lab : loc);
225         }
226       xstormy16_compare_op0 = simplify_gen_subreg (word_mode, op0, mode, 
227                                                   i * UNITS_PER_WORD);
228       xstormy16_compare_op1 = simplify_gen_subreg (word_mode, op1, mode, 
229                                                   i * UNITS_PER_WORD);
230       xstormy16_emit_cbranch (code, loc);
231
232       if (code == EQ)
233         emit_label (lab);
234       return;
235     }
236
237   /* We can't allow reload to try to generate any reload after a branch,
238      so when some register must match we must make the temporary ourselves.  */
239   if (mode != HImode)
240     {
241       rtx tmp;
242       tmp = gen_reg_rtx (mode);
243       emit_move_insn (tmp, op0);
244       op0 = tmp;
245     }
246
247   condition_rtx = gen_rtx (code, mode, op0, op1);
248   loc_ref = gen_rtx_LABEL_REF (VOIDmode, loc);
249   branch = gen_rtx_SET (VOIDmode, pc_rtx,
250                         gen_rtx_IF_THEN_ELSE (VOIDmode, condition_rtx,
251                                               loc_ref, pc_rtx));
252
253   cy_clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (BImode));
254
255   if (mode == HImode)
256     vec = gen_rtvec (2, branch, cy_clobber);
257   else if (code == NE || code == EQ)
258     vec = gen_rtvec (2, branch, gen_rtx_CLOBBER (VOIDmode, op0));
259   else
260     {
261       rtx sub;
262 #if 0
263       sub = gen_rtx_SET (VOIDmode, op0, gen_rtx_MINUS (SImode, op0, op1));
264 #else
265       sub = gen_rtx_CLOBBER (SImode, op0);
266 #endif
267       vec = gen_rtvec (3, branch, sub, cy_clobber);
268     }
269
270   emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec));
271 }
272
273 /* Take a SImode conditional branch, one of GT/LE/GTU/LEU, and split
274    the arithmetic operation.  Most of the work is done by
275    xstormy16_expand_arith.  */
276
277 void
278 xstormy16_split_cbranch (mode, label, comparison, dest, carry)
279      enum machine_mode mode;
280      rtx label;
281      rtx comparison;
282      rtx dest;
283      rtx carry;
284 {
285   rtx op0 = XEXP (comparison, 0);
286   rtx op1 = XEXP (comparison, 1);
287   rtx seq, last_insn;
288   rtx compare;
289   
290   start_sequence ();
291   xstormy16_expand_arith (mode, COMPARE, dest, op0, op1, carry);
292   seq = get_insns ();
293   end_sequence ();
294
295   if (! INSN_P (seq))
296     abort ();
297
298   last_insn = seq;
299   while (NEXT_INSN (last_insn) != NULL_RTX)
300     last_insn = NEXT_INSN (last_insn);
301
302   compare = SET_SRC (XVECEXP (PATTERN (last_insn), 0, 0));
303   PUT_CODE (XEXP (compare, 0), GET_CODE (comparison));
304   XEXP (compare, 1) = gen_rtx_LABEL_REF (VOIDmode, label);
305   emit_insn (seq);
306 }
307
308
309 /* Return the string to output a conditional branch to LABEL, which is
310    the operand number of the label.
311
312    OP is the conditional expression, or NULL for branch-always.
313
314    REVERSED is nonzero if we should reverse the sense of the comparison.
315
316    INSN is the insn.  */
317
318 char *
319 xstormy16_output_cbranch_hi (op, label, reversed, insn)
320      rtx op;
321      const char * label;
322      int reversed;
323      rtx insn;
324 {
325   static char string[64];
326   int need_longbranch = (op != NULL_RTX
327                          ? get_attr_length (insn) == 8
328                          : get_attr_length (insn) == 4);
329   int really_reversed = reversed ^ need_longbranch;
330   const char *ccode;
331   const char *template;
332   const char *operands;
333   enum rtx_code code;
334   
335   if (! op)
336     {
337       if (need_longbranch)
338         ccode = "jmpf";
339       else
340         ccode = "br";
341       sprintf (string, "%s %s", ccode, label);
342       return string;
343     }
344
345   code = GET_CODE (op);
346
347   if (GET_CODE (XEXP (op, 0)) != REG)
348     {
349       code = swap_condition (code);
350       operands = "%3,%2";
351     }
352   else
353       operands = "%2,%3";
354
355   /* Work out which way this really branches.  */
356   if (really_reversed)
357     code = reverse_condition (code);
358
359   switch (code)
360     {
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 GT:   ccode = "gt";  break;
366     case LE:   ccode = "le";  break;
367     case GEU:  ccode = "nc";  break;
368     case LTU:  ccode = "c";   break;
369     case GTU:  ccode = "hi";  break;
370     case LEU:  ccode = "ls";  break;
371       
372     default:
373       abort ();
374     }
375
376   if (need_longbranch)
377     template = "b%s %s,.+8 | jmpf %s";
378   else
379     template = "b%s %s,%s";
380   sprintf (string, template, ccode, operands, label);
381   
382   return string;
383 }
384
385 /* Return the string to output a conditional branch to LABEL, which is
386    the operand number of the label, but suitable for the tail of a
387    SImode branch.
388
389    OP is the conditional expression (OP is never NULL_RTX).
390
391    REVERSED is nonzero if we should reverse the sense of the comparison.
392
393    INSN is the insn.  */
394
395 char *
396 xstormy16_output_cbranch_si (op, label, reversed, insn)
397      rtx op;
398      const char * label;
399      int reversed;
400      rtx insn;
401 {
402   static char string[64];
403   int need_longbranch = get_attr_length (insn) >= 8;
404   int really_reversed = reversed ^ need_longbranch;
405   const char *ccode;
406   const char *template;
407   char prevop[16];
408   enum rtx_code code;
409   
410   code = GET_CODE (op);
411
412   /* Work out which way this really branches.  */
413   if (really_reversed)
414     code = reverse_condition (code);
415
416   switch (code)
417     {
418     case EQ:   ccode = "z";   break;
419     case NE:   ccode = "nz";  break;
420     case GE:   ccode = "ge";  break;
421     case LT:   ccode = "lt";  break;
422     case GEU:  ccode = "nc";  break;
423     case LTU:  ccode = "c";   break;
424
425       /* The missing codes above should never be generated.  */
426     default:
427       abort ();
428     }
429
430   switch (code)
431     {
432     case EQ: case NE:
433       {
434         int regnum;
435         
436         if (GET_CODE (XEXP (op, 0)) != REG)
437           abort ();
438       
439         regnum = REGNO (XEXP (op, 0));
440         sprintf (prevop, "or %s,%s", reg_names[regnum], reg_names[regnum+1]);
441       }
442       break;
443
444     case GE: case LT: case GEU: case LTU:
445       strcpy (prevop, "sbc %2,%3");
446       break;
447
448     default:
449       abort ();
450     }
451
452   if (need_longbranch)
453     template = "%s | b%s .+6 | jmpf %s";
454   else
455     template = "%s | b%s %s";
456   sprintf (string, template, prevop, ccode, label);
457   
458   return string;
459 }
460 \f
461 /* Many machines have some registers that cannot be copied directly to or from
462    memory or even from other types of registers.  An example is the `MQ'
463    register, which on most machines, can only be copied to or from general
464    registers, but not memory.  Some machines allow copying all registers to and
465    from memory, but require a scratch register for stores to some memory
466    locations (e.g., those with symbolic address on the RT, and those with
467    certain symbolic address on the SPARC when compiling PIC).  In some cases,
468    both an intermediate and a scratch register are required.
469
470    You should define these macros to indicate to the reload phase that it may
471    need to allocate at least one register for a reload in addition to the
472    register to contain the data.  Specifically, if copying X to a register
473    CLASS in MODE requires an intermediate register, you should define
474    `SECONDARY_INPUT_RELOAD_CLASS' to return the largest register class all of
475    whose registers can be used as intermediate registers or scratch registers.
476
477    If copying a register CLASS in MODE to X requires an intermediate or scratch
478    register, `SECONDARY_OUTPUT_RELOAD_CLASS' should be defined to return the
479    largest register class required.  If the requirements for input and output
480    reloads are the same, the macro `SECONDARY_RELOAD_CLASS' should be used
481    instead of defining both macros identically.
482
483    The values returned by these macros are often `GENERAL_REGS'.  Return
484    `NO_REGS' if no spare register is needed; i.e., if X can be directly copied
485    to or from a register of CLASS in MODE without requiring a scratch register.
486    Do not define this macro if it would always return `NO_REGS'.
487
488    If a scratch register is required (either with or without an intermediate
489    register), you should define patterns for `reload_inM' or `reload_outM', as
490    required..  These patterns, which will normally be implemented with a
491    `define_expand', should be similar to the `movM' patterns, except that
492    operand 2 is the scratch register.
493
494    Define constraints for the reload register and scratch register that contain
495    a single register class.  If the original reload register (whose class is
496    CLASS) can meet the constraint given in the pattern, the value returned by
497    these macros is used for the class of the scratch register.  Otherwise, two
498    additional reload registers are required.  Their classes are obtained from
499    the constraints in the insn pattern.
500
501    X might be a pseudo-register or a `subreg' of a pseudo-register, which could
502    either be in a hard register or in memory.  Use `true_regnum' to find out;
503    it will return -1 if the pseudo is in memory and the hard register number if
504    it is in a register.
505
506    These macros should not be used in the case where a particular class of
507    registers can only be copied to memory and not to another class of
508    registers.  In that case, secondary reload registers are not needed and
509    would not be helpful.  Instead, a stack location must be used to perform the
510    copy and the `movM' pattern should use memory as an intermediate storage.
511    This case often occurs between floating-point and general registers.  */
512
513 enum reg_class
514 xstormy16_secondary_reload_class (class, mode, x)
515      enum reg_class class;
516      enum machine_mode mode;
517      rtx x;
518 {
519   /* This chip has the interesting property that only the first eight
520      registers can be moved to/from memory.  */
521   if ((GET_CODE (x) == MEM
522        || ((GET_CODE (x) == SUBREG || GET_CODE (x) == REG)
523            && (true_regnum (x) == -1
524                || true_regnum (x) >= FIRST_PSEUDO_REGISTER)))
525       && ! reg_class_subset_p (class, EIGHT_REGS))
526     return EIGHT_REGS;
527
528   /* When reloading a PLUS, the carry register will be required
529      unless the inc or dec instructions can be used.  */
530   if (xstormy16_carry_plus_operand (x, mode))
531     return CARRY_REGS;
532
533   return NO_REGS;
534 }
535
536 /* Recognize a PLUS that needs the carry register.  */
537 int
538 xstormy16_carry_plus_operand (x, mode)
539      rtx x;
540      enum machine_mode mode ATTRIBUTE_UNUSED;
541 {
542   return (GET_CODE (x) == PLUS
543           && GET_CODE (XEXP (x, 1)) == CONST_INT
544           && (INTVAL (XEXP (x, 1)) < -4 || INTVAL (XEXP (x, 1)) > 4));
545 }
546
547 /* Detect and error out on out-of-range constants for movhi.  */
548 int
549 xs_hi_general_operand (x, mode)
550      rtx x;
551      enum machine_mode mode ATTRIBUTE_UNUSED;
552 {
553   if ((GET_CODE (x) == CONST_INT) 
554    && ((INTVAL (x) >= 32768) || (INTVAL (x) < -32768)))
555     error ("Constant halfword load operand out of range.");
556   return general_operand (x, mode);
557 }
558
559 /* Detect and error out on out-of-range constants for addhi and subhi.  */
560 int
561 xs_hi_nonmemory_operand (x, mode)
562      rtx x;
563      enum machine_mode mode ATTRIBUTE_UNUSED;
564 {
565   if ((GET_CODE (x) == CONST_INT) 
566    && ((INTVAL (x) >= 32768) || (INTVAL (x) < -32768)))
567     error ("Constant arithmetic operand out of range.");
568   return nonmemory_operand (x, mode);
569 }
570
571 enum reg_class
572 xstormy16_preferred_reload_class (x, class)
573      enum reg_class class;
574      rtx x;
575 {
576   if (class == GENERAL_REGS
577       && GET_CODE (x) == MEM)
578     return EIGHT_REGS;
579
580   return class;
581 }
582
583 #define LEGITIMATE_ADDRESS_INTEGER_P(X, OFFSET)                         \
584  (GET_CODE (X) == CONST_INT                                             \
585   && (unsigned HOST_WIDE_INT) (INTVAL (X) + (OFFSET) + 2048) < 4096)
586
587 #define LEGITIMATE_ADDRESS_CONST_INT_P(X, OFFSET)                        \
588  (GET_CODE (X) == CONST_INT                                              \
589   && INTVAL (X) + (OFFSET) >= 0                                          \
590   && INTVAL (X) + (OFFSET) < 0x8000                                      \
591   && (INTVAL (X) + (OFFSET) < 0x100 || INTVAL (X) + (OFFSET) >= 0x7F00))
592
593 int
594 xstormy16_legitimate_address_p (mode, x, strict)
595      enum machine_mode mode ATTRIBUTE_UNUSED;
596      rtx x;
597      int strict;
598 {
599   if (LEGITIMATE_ADDRESS_CONST_INT_P (x, 0))
600     return 1;
601
602   if (GET_CODE (x) == PLUS
603       && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 0))
604     x = XEXP (x, 0);
605   
606   if ((GET_CODE (x) == PRE_MODIFY
607        && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT)
608       || GET_CODE (x) == POST_INC
609       || GET_CODE (x) == PRE_DEC)
610     x = XEXP (x, 0);
611   
612   if (GET_CODE (x) == REG && REGNO_OK_FOR_BASE_P (REGNO (x))
613       && (! strict || REGNO (x) < FIRST_PSEUDO_REGISTER))
614     return 1;
615   
616   return 0;
617 }
618
619 /* Return nonzero if memory address X (an RTX) can have different
620    meanings depending on the machine mode of the memory reference it
621    is used for or if the address is valid for some modes but not
622    others.
623
624    Autoincrement and autodecrement addresses typically have mode-dependent
625    effects because the amount of the increment or decrement is the size of the
626    operand being addressed.  Some machines have other mode-dependent addresses.
627    Many RISC machines have no mode-dependent addresses.
628
629    You may assume that ADDR is a valid address for the machine.  
630    
631    On this chip, this is true if the address is valid with an offset
632    of 0 but not of 6, because in that case it cannot be used as an
633    address for DImode or DFmode, or if the address is a post-increment
634    or pre-decrement address.  */
635 int
636 xstormy16_mode_dependent_address_p (x)
637      rtx x;
638 {
639   if (LEGITIMATE_ADDRESS_CONST_INT_P (x, 0)
640       && ! LEGITIMATE_ADDRESS_CONST_INT_P (x, 6))
641     return 1;
642   
643   if (GET_CODE (x) == PLUS
644       && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 0)
645       && ! LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 6))
646     return 1;
647
648   if (GET_CODE (x) == PLUS)
649     x = XEXP (x, 0);
650
651   if (GET_CODE (x) == POST_INC
652       || GET_CODE (x) == PRE_DEC)
653     return 1;
654
655   return 0;
656 }
657
658 /* A C expression that defines the optional machine-dependent constraint
659    letters (`Q', `R', `S', `T', `U') that can be used to segregate specific
660    types of operands, usually memory references, for the target machine.
661    Normally this macro will not be defined.  If it is required for a particular
662    target machine, it should return 1 if VALUE corresponds to the operand type
663    represented by the constraint letter C.  If C is not defined as an extra
664    constraint, the value returned should be 0 regardless of VALUE.  */
665 int
666 xstormy16_extra_constraint_p (x, c)
667      rtx x;
668      int c;
669 {
670   switch (c)
671     {
672       /* 'Q' is for pushes.  */
673     case 'Q':
674       return (GET_CODE (x) == MEM
675               && GET_CODE (XEXP (x, 0)) == POST_INC
676               && XEXP (XEXP (x, 0), 0) == stack_pointer_rtx);
677
678       /* 'R' is for pops.  */
679     case 'R':
680       return (GET_CODE (x) == MEM
681               && GET_CODE (XEXP (x, 0)) == PRE_DEC
682               && XEXP (XEXP (x, 0), 0) == stack_pointer_rtx);
683
684       /* 'S' is for immediate memory addresses.  */
685     case 'S':
686       return (GET_CODE (x) == MEM
687               && GET_CODE (XEXP (x, 0)) == CONST_INT
688               && xstormy16_legitimate_address_p (VOIDmode, XEXP (x, 0), 0));
689
690       /* 'T' is for Rx.  */
691     case 'T':
692       /* Not implemented yet.  */
693       return 0;
694
695       /* 'U' is for CONST_INT values not between 2 and 15 inclusive,
696          for allocating a scratch register for 32-bit shifts.  */
697     case 'U':
698       return (GET_CODE (x) == CONST_INT
699               && (INTVAL (x) < 2 || INTVAL (x) > 15));
700
701     default:
702       return 0;
703     }
704 }
705
706 int
707 short_memory_operand (x, mode)
708      rtx x;
709      enum machine_mode mode;
710 {
711   if (! memory_operand (x, mode))
712     return 0;
713   return (GET_CODE (XEXP (x, 0)) != PLUS);
714 }
715
716 int
717 nonimmediate_nonstack_operand (op, mode)
718      rtx op;
719      enum machine_mode mode;
720 {
721   /* 'Q' is for pushes, 'R' for pops.  */
722   return (nonimmediate_operand (op, mode) 
723           && ! xstormy16_extra_constraint_p (op, 'Q')
724           && ! xstormy16_extra_constraint_p (op, 'R'));
725 }
726
727 /* Splitter for the 'move' patterns, for modes not directly implemeted
728    by hardware.  Emit insns to copy a value of mode MODE from SRC to
729    DEST.
730
731    This function is only called when reload_completed.
732    */
733
734 void 
735 xstormy16_split_move (mode, dest, src)
736      enum machine_mode mode;
737      rtx dest;
738      rtx src;
739 {
740   int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
741   int direction, end, i;
742   int src_modifies = 0;
743   int dest_modifies = 0;
744   int src_volatile = 0;
745   int dest_volatile = 0;
746   rtx mem_operand;
747   rtx auto_inc_reg_rtx = NULL_RTX;
748   
749   /* Check initial conditions.  */
750   if (! reload_completed
751       || mode == QImode || mode == HImode
752       || ! nonimmediate_operand (dest, mode)
753       || ! general_operand (src, mode))
754     abort ();
755
756   /* This case is not supported below, and shouldn't be generated.  */
757   if (GET_CODE (dest) == MEM
758       && GET_CODE (src) == MEM)
759     abort ();
760
761   /* This case is very very bad after reload, so trap it now.  */
762   if (GET_CODE (dest) == SUBREG
763       || GET_CODE (src) == SUBREG)
764     abort ();
765
766   /* The general idea is to copy by words, offsetting the source and
767      destination.  Normally the least-significant word will be copied
768      first, but for pre-dec operations it's better to copy the 
769      most-significant word first.  Only one operand can be a pre-dec
770      or post-inc operand.  
771
772      It's also possible that the copy overlaps so that the direction
773      must be reversed.  */
774   direction = 1;
775   
776   if (GET_CODE (dest) == MEM)
777     {
778       mem_operand = XEXP (dest, 0);
779       dest_modifies = side_effects_p (mem_operand);
780       if (auto_inc_p (mem_operand))
781         auto_inc_reg_rtx = XEXP (mem_operand, 0);
782       dest_volatile = MEM_VOLATILE_P (dest);
783       if (dest_volatile)
784         {
785           dest = copy_rtx (dest);
786           MEM_VOLATILE_P (dest) = 0;
787         }
788     }
789   else if (GET_CODE (src) == MEM)
790     {
791       mem_operand = XEXP (src, 0);
792       src_modifies = side_effects_p (mem_operand);
793       if (auto_inc_p (mem_operand))
794         auto_inc_reg_rtx = XEXP (mem_operand, 0);
795       src_volatile = MEM_VOLATILE_P (src);
796       if (src_volatile)
797         {
798           src = copy_rtx (src);
799           MEM_VOLATILE_P (src) = 0;
800         }
801     }
802   else
803     mem_operand = NULL_RTX;
804
805   if (mem_operand == NULL_RTX)
806     {
807       if (GET_CODE (src) == REG
808           && GET_CODE (dest) == REG
809           && reg_overlap_mentioned_p (dest, src)
810           && REGNO (dest) > REGNO (src))
811         direction = -1;
812     }
813   else if (GET_CODE (mem_operand) == PRE_DEC
814       || (GET_CODE (mem_operand) == PLUS 
815           && GET_CODE (XEXP (mem_operand, 0)) == PRE_DEC))
816     direction = -1;
817   else if (GET_CODE (src) == MEM
818            && reg_overlap_mentioned_p (dest, src))
819     {
820       int regno;
821       if (GET_CODE (dest) != REG)
822         abort ();
823       regno = REGNO (dest);
824       
825       if (! refers_to_regno_p (regno, regno + num_words, mem_operand, 0))
826         abort ();
827       
828       if (refers_to_regno_p (regno, regno + 1, mem_operand, 0))
829         direction = -1;
830       else if (refers_to_regno_p (regno + num_words - 1, regno + num_words,
831                                   mem_operand, 0))
832         direction = 1;
833       else
834         /* This means something like
835            (set (reg:DI r0) (mem:DI (reg:HI r1)))
836            which we'd need to support by doing the set of the second word
837            last.  */
838         abort ();
839     }
840
841   end = direction < 0 ? -1 : num_words;
842   for (i = direction < 0 ? num_words - 1 : 0; i != end; i += direction)
843     {
844       rtx w_src, w_dest, insn;
845
846       if (src_modifies)
847         w_src = gen_rtx_MEM (word_mode, mem_operand);
848       else
849         w_src = simplify_gen_subreg (word_mode, src, mode, i * UNITS_PER_WORD);
850       if (src_volatile)
851         MEM_VOLATILE_P (w_src) = 1;
852       if (dest_modifies)
853         w_dest = gen_rtx_MEM (word_mode, mem_operand);
854       else
855         w_dest = simplify_gen_subreg (word_mode, dest, mode, 
856                                       i * UNITS_PER_WORD);
857       if (dest_volatile)
858         MEM_VOLATILE_P (w_dest) = 1;
859       
860       /* The simplify_subreg calls must always be able to simplify.  */
861       if (GET_CODE (w_src) == SUBREG
862           || GET_CODE (w_dest) == SUBREG)
863         abort ();
864       
865       insn = emit_insn (gen_rtx_SET (VOIDmode, w_dest, w_src));
866       if (auto_inc_reg_rtx)
867         REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
868                                             auto_inc_reg_rtx,
869                                             REG_NOTES (insn));
870     }
871 }
872
873 /* Expander for the 'move' patterns.  Emit insns to copy a value of
874    mode MODE from SRC to DEST.  */
875
876 void 
877 xstormy16_expand_move (mode, dest, src)
878      enum machine_mode mode;
879      rtx dest;
880      rtx src;
881 {
882   if ((GET_CODE (dest) == MEM) && (GET_CODE (XEXP (dest, 0)) == PRE_MODIFY))
883     {
884       rtx pmv      = XEXP (dest, 0);
885       rtx dest_reg = XEXP (pmv, 0);
886       rtx dest_mod = XEXP (pmv, 1);
887       rtx set      = gen_rtx_SET (Pmode, dest_reg, dest_mod);
888       rtx clobber  = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (BImode, 16));
889     
890       dest = gen_rtx_MEM (mode, dest_reg);
891       emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber)));
892     }
893   else if ((GET_CODE (src) == MEM) && (GET_CODE (XEXP (src, 0)) == PRE_MODIFY))
894     {
895       rtx pmv     = XEXP (src, 0);
896       rtx src_reg = XEXP (pmv, 0);
897       rtx src_mod = XEXP (pmv, 1);
898       rtx set     = gen_rtx_SET (Pmode, src_reg, src_mod);
899       rtx clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (BImode, 16));
900     
901       src = gen_rtx_MEM (mode, src_reg);
902       emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber)));
903     }
904    
905   /* There are only limited immediate-to-memory move instructions.  */
906   if (! reload_in_progress
907       && ! reload_completed
908       && GET_CODE (dest) == MEM
909       && (GET_CODE (XEXP (dest, 0)) != CONST_INT
910           || ! xstormy16_legitimate_address_p (mode, XEXP (dest, 0), 0))
911       && GET_CODE (src) != REG
912       && GET_CODE (src) != SUBREG)
913     src = copy_to_mode_reg (mode, src);
914
915   /* Don't emit something we would immediately split.  */
916   if (reload_completed
917       && mode != HImode && mode != QImode)
918     {
919       xstormy16_split_move (mode, dest, src);
920       return;
921     }
922   
923   emit_insn (gen_rtx_SET (VOIDmode, dest, src));
924 }
925
926 \f
927 /* Stack Layout:
928
929    The stack is laid out as follows:
930
931 SP->
932 FP->    Local variables
933         Register save area (up to 4 words)
934         Argument register save area for stdarg (NUM_ARGUMENT_REGISTERS words)
935
936 AP->    Return address (two words)
937         9th procedure parameter word
938         10th procedure parameter word
939         ...
940         last procedure parameter word
941
942   The frame pointer location is tuned to make it most likely that all
943   parameters and local variables can be accessed using a load-indexed
944   instruction.  */
945
946 /* A structure to describe the layout.  */
947 struct xstormy16_stack_layout
948 {
949   /* Size of the topmost three items on the stack.  */
950   int locals_size;
951   int register_save_size;
952   int stdarg_save_size;
953   /* Sum of the above items.  */
954   int frame_size;
955   /* Various offsets.  */
956   int first_local_minus_ap;
957   int sp_minus_fp;
958   int fp_minus_ap;
959 };
960
961 /* Does REGNO need to be saved?  */
962 #define REG_NEEDS_SAVE(REGNUM, IFUN)                                    \
963   ((regs_ever_live[REGNUM] && ! call_used_regs[REGNUM])                 \
964    || (IFUN && ! fixed_regs[REGNUM] && call_used_regs[REGNUM]           \
965        && (REGNO_REG_CLASS (REGNUM) != CARRY_REGS)                      \
966        && (regs_ever_live[REGNUM] || ! current_function_is_leaf)))
967
968 /* Compute the stack layout.  */
969 struct xstormy16_stack_layout 
970 xstormy16_compute_stack_layout ()
971 {
972   struct xstormy16_stack_layout layout;
973   int regno;
974   const int ifun = xstormy16_interrupt_function_p ();
975
976   layout.locals_size = get_frame_size ();
977   
978   layout.register_save_size = 0;
979   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
980     if (REG_NEEDS_SAVE (regno, ifun))
981       layout.register_save_size += UNITS_PER_WORD;
982   
983   if (current_function_stdarg)
984     layout.stdarg_save_size = NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD;
985   else
986     layout.stdarg_save_size = 0;
987   
988   layout.frame_size = (layout.locals_size 
989                        + layout.register_save_size 
990                        + layout.stdarg_save_size);
991   
992   if (current_function_args_size <= 2048 && current_function_args_size != -1)
993     {
994       if (layout.frame_size + INCOMING_FRAME_SP_OFFSET 
995           + current_function_args_size <= 2048)
996         layout.fp_minus_ap = layout.frame_size + INCOMING_FRAME_SP_OFFSET;
997       else
998         layout.fp_minus_ap = 2048 - current_function_args_size;
999     }
1000   else
1001     layout.fp_minus_ap = (layout.stdarg_save_size 
1002                           + layout.register_save_size
1003                           + INCOMING_FRAME_SP_OFFSET);
1004   layout.sp_minus_fp = (layout.frame_size + INCOMING_FRAME_SP_OFFSET 
1005                         - layout.fp_minus_ap);
1006   layout.first_local_minus_ap = layout.sp_minus_fp - layout.locals_size;
1007   return layout;
1008 }
1009
1010 /* Determine how all the special registers get eliminated.  */
1011 int
1012 xstormy16_initial_elimination_offset (from, to)
1013      int from, to;
1014 {
1015   struct xstormy16_stack_layout layout;
1016   int result;
1017   
1018   layout = xstormy16_compute_stack_layout ();
1019
1020   if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
1021     result = layout.sp_minus_fp - layout.locals_size;
1022   else if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
1023     result = -layout.locals_size;
1024   else if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
1025     result = -layout.fp_minus_ap;
1026   else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
1027     result = -(layout.sp_minus_fp + layout.fp_minus_ap);
1028   else
1029     abort ();
1030
1031   return result;
1032 }
1033
1034 static rtx
1035 emit_addhi3_postreload (dest, src0, src1)
1036      rtx dest;
1037      rtx src0;
1038      rtx src1;
1039 {
1040   rtx set, clobber, insn;
1041   
1042   set = gen_rtx_SET (VOIDmode, dest, gen_rtx_PLUS (HImode, src0, src1));
1043   clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (BImode, 16));
1044   insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber)));
1045   return insn;
1046 }
1047
1048 /* Called after register allocation to add any instructions needed for
1049    the prologue.  Using a prologue insn is favored compared to putting
1050    all of the instructions in the TARGET_ASM_FUNCTION_PROLOGUE macro,
1051    since it allows the scheduler to intermix instructions with the
1052    saves of the caller saved registers.  In some cases, it might be
1053    necessary to emit a barrier instruction as the last insn to prevent
1054    such scheduling.
1055
1056    Also any insns generated here should have RTX_FRAME_RELATED_P(insn) = 1
1057    so that the debug info generation code can handle them properly.  */
1058 void
1059 xstormy16_expand_prologue ()
1060 {
1061   struct xstormy16_stack_layout layout;
1062   int regno;
1063   rtx insn;
1064   rtx mem_push_rtx;
1065   const int ifun = xstormy16_interrupt_function_p ();
1066   
1067   mem_push_rtx = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
1068   mem_push_rtx = gen_rtx_MEM (HImode, mem_push_rtx);
1069     
1070   layout = xstormy16_compute_stack_layout ();
1071
1072   if (layout.locals_size >= 32768)
1073     error ("Local variable memory requirements exceed capacity.");
1074
1075   /* Save the argument registers if necessary.  */
1076   if (layout.stdarg_save_size)
1077     for (regno = FIRST_ARGUMENT_REGISTER; 
1078          regno < FIRST_ARGUMENT_REGISTER + NUM_ARGUMENT_REGISTERS;
1079          regno++)
1080       {
1081         rtx dwarf;
1082         rtx reg = gen_rtx_REG (HImode, regno);
1083
1084         insn = emit_move_insn (mem_push_rtx, reg);
1085         RTX_FRAME_RELATED_P (insn) = 1;
1086
1087         dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (2));
1088         
1089         XVECEXP (dwarf, 0, 0) = gen_rtx_SET (VOIDmode,
1090                                              gen_rtx_MEM (Pmode, stack_pointer_rtx),
1091                                              reg);
1092         XVECEXP (dwarf, 0, 1) = gen_rtx_SET (Pmode, stack_pointer_rtx,
1093                                              plus_constant (stack_pointer_rtx,
1094                                                             GET_MODE_SIZE (Pmode)));
1095         REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
1096                                               dwarf,
1097                                               REG_NOTES (insn));
1098         RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 0)) = 1;
1099         RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 1)) = 1;
1100       }
1101   
1102   /* Push each of the registers to save.  */
1103   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
1104     if (REG_NEEDS_SAVE (regno, ifun))
1105       {
1106         rtx dwarf;
1107         rtx reg = gen_rtx_REG (HImode, regno);
1108
1109         insn = emit_move_insn (mem_push_rtx, reg);
1110         RTX_FRAME_RELATED_P (insn) = 1;
1111
1112         dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (2));
1113         
1114         XVECEXP (dwarf, 0, 0) = gen_rtx_SET (VOIDmode,
1115                                              gen_rtx_MEM (Pmode, stack_pointer_rtx),
1116                                              reg);
1117         XVECEXP (dwarf, 0, 1) = gen_rtx_SET (Pmode, stack_pointer_rtx,
1118                                              plus_constant (stack_pointer_rtx,
1119                                                             GET_MODE_SIZE (Pmode)));
1120         REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
1121                                               dwarf,
1122                                               REG_NOTES (insn));
1123         RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 0)) = 1;
1124         RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 1)) = 1;
1125       }
1126
1127   /* It's just possible that the SP here might be what we need for
1128      the new FP...  */
1129   if (frame_pointer_needed && layout.sp_minus_fp == layout.locals_size)
1130     emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
1131
1132   /* Allocate space for local variables.  */
1133   if (layout.locals_size)
1134     {
1135       insn = emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx,
1136                                      GEN_INT (layout.locals_size));
1137       RTX_FRAME_RELATED_P (insn) = 1;
1138     }
1139
1140   /* Set up the frame pointer, if required.  */
1141   if (frame_pointer_needed && layout.sp_minus_fp != layout.locals_size)
1142     {
1143       insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
1144
1145       if (layout.sp_minus_fp)
1146         emit_addhi3_postreload (hard_frame_pointer_rtx,
1147                                 hard_frame_pointer_rtx,
1148                                 GEN_INT (-layout.sp_minus_fp));
1149     }
1150 }
1151
1152 /* Do we need an epilogue at all?  */
1153 int
1154 direct_return ()
1155 {
1156   return (reload_completed 
1157           && xstormy16_compute_stack_layout ().frame_size == 0);
1158 }
1159
1160 /* Called after register allocation to add any instructions needed for
1161    the epilogue.  Using an epilogue insn is favored compared to putting
1162    all of the instructions in the TARGET_ASM_FUNCTION_PROLOGUE macro,
1163    since it allows the scheduler to intermix instructions with the
1164    saves of the caller saved registers.  In some cases, it might be
1165    necessary to emit a barrier instruction as the last insn to prevent
1166    such scheduling.  */
1167
1168 void
1169 xstormy16_expand_epilogue ()
1170 {
1171   struct xstormy16_stack_layout layout;
1172   rtx mem_pop_rtx, insn;
1173   int regno;
1174   const int ifun = xstormy16_interrupt_function_p ();
1175   
1176   mem_pop_rtx = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
1177   mem_pop_rtx = gen_rtx_MEM (HImode, mem_pop_rtx);
1178   
1179   layout = xstormy16_compute_stack_layout ();
1180
1181   /* Pop the stack for the locals.  */
1182   if (layout.locals_size)
1183     {
1184       if (frame_pointer_needed && layout.sp_minus_fp == layout.locals_size)
1185         emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
1186       else
1187         {
1188           insn = emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx,
1189                                          GEN_INT (- layout.locals_size));
1190           RTX_FRAME_RELATED_P (insn) = 1;
1191         }
1192     }
1193
1194   /* Restore any call-saved registers.  */
1195   for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno--)
1196     if (REG_NEEDS_SAVE (regno, ifun))
1197       {
1198         rtx dwarf;
1199
1200         insn = emit_move_insn (gen_rtx_REG (HImode, regno), mem_pop_rtx);
1201         RTX_FRAME_RELATED_P (insn) = 1;
1202         dwarf = gen_rtx_SET (Pmode, stack_pointer_rtx,
1203                              plus_constant (stack_pointer_rtx,
1204                                             -GET_MODE_SIZE (Pmode)));
1205         REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
1206                                               dwarf,
1207                                               REG_NOTES (insn));
1208       }
1209   
1210   /* Pop the stack for the stdarg save area.  */
1211   if (layout.stdarg_save_size)
1212     {
1213       insn = emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx,
1214                                      GEN_INT (- layout.stdarg_save_size));
1215       RTX_FRAME_RELATED_P (insn) = 1;
1216     }
1217
1218   /* Return.  */
1219   if (ifun)
1220     emit_jump_insn (gen_return_internal_interrupt ());
1221   else
1222     emit_jump_insn (gen_return_internal ());
1223 }
1224
1225 int
1226 xstormy16_epilogue_uses (regno)
1227      int regno;
1228 {
1229   if (reload_completed && call_used_regs[regno])
1230     {
1231       const int ifun = xstormy16_interrupt_function_p ();
1232       return REG_NEEDS_SAVE (regno, ifun);
1233     }
1234   return 0;
1235 }
1236
1237 void
1238 xstormy16_function_profiler ()
1239 {
1240   sorry ("function_profiler support");
1241 }
1242
1243 \f
1244 /* Return an updated summarizer variable CUM to advance past an
1245    argument in the argument list.  The values MODE, TYPE and NAMED
1246    describe that argument.  Once this is done, the variable CUM is
1247    suitable for analyzing the *following* argument with
1248    `FUNCTION_ARG', etc.
1249
1250    This function need not do anything if the argument in question was
1251    passed on the stack.  The compiler knows how to track the amount of
1252    stack space used for arguments without any special help.  However,
1253    it makes life easier for xstormy16_build_va_list if it does update
1254    the word count.  */
1255 CUMULATIVE_ARGS
1256 xstormy16_function_arg_advance (cum, mode, type, named)
1257      CUMULATIVE_ARGS cum;
1258      enum machine_mode mode;
1259      tree type;
1260      int named ATTRIBUTE_UNUSED;
1261 {
1262   /* If an argument would otherwise be passed partially in registers,
1263      and partially on the stack, the whole of it is passed on the
1264      stack.  */
1265   if (cum < NUM_ARGUMENT_REGISTERS
1266       && cum + XSTORMY16_WORD_SIZE (type, mode) > NUM_ARGUMENT_REGISTERS)
1267     cum = NUM_ARGUMENT_REGISTERS;
1268   
1269   cum += XSTORMY16_WORD_SIZE (type, mode);
1270   
1271   return cum;
1272 }
1273
1274 rtx
1275 xstormy16_function_arg (cum, mode, type, named)
1276      CUMULATIVE_ARGS cum;
1277      enum machine_mode mode;
1278      tree type;
1279      int named ATTRIBUTE_UNUSED;
1280 {
1281   if (mode == VOIDmode)
1282     return const0_rtx;
1283   if (MUST_PASS_IN_STACK (mode, type)
1284       || cum + XSTORMY16_WORD_SIZE (type, mode) > NUM_ARGUMENT_REGISTERS)
1285     return 0;
1286   return gen_rtx_REG (mode, cum + 2);
1287 }
1288
1289 /* Do any needed setup for a variadic function.  CUM has not been updated
1290    for the last named argument which has type TYPE and mode MODE.  */
1291 void
1292 xstormy16_setup_incoming_varargs (cum, int_mode, type, pretend_size)
1293      CUMULATIVE_ARGS cum ATTRIBUTE_UNUSED;
1294      int             int_mode ATTRIBUTE_UNUSED;
1295      tree            type ATTRIBUTE_UNUSED;
1296      int *           pretend_size ATTRIBUTE_UNUSED;
1297 {
1298 }
1299
1300 /* Build the va_list type.
1301
1302    For this chip, va_list is a record containing a counter and a pointer.
1303    The counter is of type 'int' and indicates how many bytes
1304    have been used to date.  The pointer indicates the stack position
1305    for arguments that have not been passed in registers.  
1306    To keep the layout nice, the pointer is first in the structure.  */
1307
1308 tree
1309 xstormy16_build_va_list ()
1310 {
1311   tree f_1, f_2, record, type_decl;
1312
1313   record = (*lang_hooks.types.make_type) (RECORD_TYPE);
1314   type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
1315
1316   f_1 = build_decl (FIELD_DECL, get_identifier ("base"),
1317                       ptr_type_node);
1318   f_2 = build_decl (FIELD_DECL, get_identifier ("count"), 
1319                       unsigned_type_node);
1320
1321   DECL_FIELD_CONTEXT (f_1) = record;
1322   DECL_FIELD_CONTEXT (f_2) = record;
1323
1324   TREE_CHAIN (record) = type_decl;
1325   TYPE_NAME (record) = type_decl;
1326   TYPE_FIELDS (record) = f_1;
1327   TREE_CHAIN (f_1) = f_2;
1328
1329   layout_type (record);
1330
1331   return record;
1332 }
1333
1334 /* Implement the stdarg/varargs va_start macro.  STDARG_P is nonzero if this
1335    is stdarg.h instead of varargs.h.  VALIST is the tree of the va_list
1336    variable to initialize.  NEXTARG is the machine independent notion of the
1337    'next' argument after the variable arguments.  */
1338 void
1339 xstormy16_expand_builtin_va_start (valist, nextarg)
1340      tree valist;
1341      rtx nextarg ATTRIBUTE_UNUSED;
1342 {
1343   tree f_base, f_count;
1344   tree base, count;
1345   tree t;
1346
1347   if (xstormy16_interrupt_function_p ())
1348     error ("cannot use va_start in interrupt function");
1349   
1350   f_base = TYPE_FIELDS (va_list_type_node);
1351   f_count = TREE_CHAIN (f_base);
1352   
1353   base = build (COMPONENT_REF, TREE_TYPE (f_base), valist, f_base);
1354   count = build (COMPONENT_REF, TREE_TYPE (f_count), valist, f_count);
1355
1356   t = make_tree (TREE_TYPE (base), virtual_incoming_args_rtx);
1357   t = build (PLUS_EXPR, TREE_TYPE (base), t, 
1358              build_int_2 (INCOMING_FRAME_SP_OFFSET, 0));
1359   t = build (MODIFY_EXPR, TREE_TYPE (base), base, t);
1360   TREE_SIDE_EFFECTS (t) = 1;
1361   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1362
1363   t = build (MODIFY_EXPR, TREE_TYPE (count), count, 
1364              build_int_2 (current_function_args_info * UNITS_PER_WORD, 0));
1365   TREE_SIDE_EFFECTS (t) = 1;
1366   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1367 }
1368
1369 /* Implement the stdarg/varargs va_arg macro.  VALIST is the variable
1370    of type va_list as a tree, TYPE is the type passed to va_arg.
1371    Note:  This algorithm is documented in stormy-abi.  */
1372    
1373 rtx
1374 xstormy16_expand_builtin_va_arg (valist, type)
1375      tree valist;
1376      tree type;
1377 {
1378   tree f_base, f_count;
1379   tree base, count;
1380   rtx count_rtx, addr_rtx, r;
1381   rtx lab_gotaddr, lab_fromstack;
1382   tree t;
1383   int size, size_of_reg_args, must_stack;
1384   tree size_tree, count_plus_size;
1385   rtx count_plus_size_rtx;
1386   
1387   f_base = TYPE_FIELDS (va_list_type_node);
1388   f_count = TREE_CHAIN (f_base);
1389   
1390   base = build (COMPONENT_REF, TREE_TYPE (f_base), valist, f_base);
1391   count = build (COMPONENT_REF, TREE_TYPE (f_count), valist, f_count);
1392
1393   must_stack = MUST_PASS_IN_STACK (TYPE_MODE (type), type);
1394   size_tree = round_up (size_in_bytes (type), UNITS_PER_WORD);
1395   
1396   size_of_reg_args = NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD;
1397
1398   count_rtx = expand_expr (count, NULL_RTX, HImode, EXPAND_NORMAL);
1399   lab_gotaddr = gen_label_rtx ();
1400   lab_fromstack = gen_label_rtx ();
1401   addr_rtx = gen_reg_rtx (Pmode);
1402
1403   if (!must_stack)
1404     {
1405       count_plus_size = build (PLUS_EXPR, TREE_TYPE (count), count, size_tree);
1406       count_plus_size_rtx = expand_expr (count_plus_size, NULL_RTX, HImode, EXPAND_NORMAL);
1407       emit_cmp_and_jump_insns (count_plus_size_rtx, GEN_INT (size_of_reg_args),
1408                                GTU, const1_rtx, HImode, 1, lab_fromstack);
1409   
1410       t = build (PLUS_EXPR, ptr_type_node, base, count);
1411       r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
1412       if (r != addr_rtx)
1413         emit_move_insn (addr_rtx, r);
1414
1415       emit_jump_insn (gen_jump (lab_gotaddr));
1416       emit_barrier ();
1417       emit_label (lab_fromstack);
1418     }
1419   
1420   /* Arguments larger than a word might need to skip over some
1421      registers, since arguments are either passed entirely in
1422      registers or entirely on the stack.  */
1423   size = PUSH_ROUNDING (int_size_in_bytes (type));
1424   if (size > 2 || size < 0 || must_stack)
1425     {
1426       rtx lab_notransition = gen_label_rtx ();
1427       emit_cmp_and_jump_insns (count_rtx, GEN_INT (NUM_ARGUMENT_REGISTERS 
1428                                                    * UNITS_PER_WORD),
1429                                GEU, const1_rtx, HImode, 1, lab_notransition);
1430       
1431       t = build (MODIFY_EXPR, TREE_TYPE (count), count, 
1432                  build_int_2 (NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD, 0));
1433       TREE_SIDE_EFFECTS (t) = 1;
1434       expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1435       
1436       emit_label (lab_notransition);
1437     }
1438
1439   t = build (PLUS_EXPR, sizetype, size_tree,
1440              build_int_2 ((- NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD
1441                            + INCOMING_FRAME_SP_OFFSET),
1442                           -1));
1443   t = build (PLUS_EXPR, TREE_TYPE (count), count, fold (t));
1444   t = build (MINUS_EXPR, TREE_TYPE (base), base, t);
1445   r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
1446   if (r != addr_rtx)
1447     emit_move_insn (addr_rtx, r);
1448              
1449   emit_label (lab_gotaddr);
1450
1451   count_plus_size = build (PLUS_EXPR, TREE_TYPE (count), count, size_tree);
1452   t = build (MODIFY_EXPR, TREE_TYPE (count), count, count_plus_size);
1453   TREE_SIDE_EFFECTS (t) = 1;
1454   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1455
1456   return addr_rtx;
1457 }
1458
1459 /* Initialize the variable parts of a trampoline.  ADDR is an RTX for
1460    the address of the trampoline; FNADDR is an RTX for the address of
1461    the nested function; STATIC_CHAIN is an RTX for the static chain
1462    value that should be passed to the function when it is called.  */
1463 void
1464 xstormy16_initialize_trampoline (addr, fnaddr, static_chain)
1465      rtx addr;
1466      rtx fnaddr;
1467      rtx static_chain;
1468 {
1469   rtx reg_addr = gen_reg_rtx (Pmode);
1470   rtx temp = gen_reg_rtx (HImode);
1471   rtx reg_fnaddr = gen_reg_rtx (HImode);
1472   rtx reg_addr_mem;
1473
1474   reg_addr_mem = gen_rtx_MEM (HImode, reg_addr);
1475     
1476   emit_move_insn (reg_addr, addr);
1477   emit_move_insn (temp, GEN_INT (0x3130 | STATIC_CHAIN_REGNUM));
1478   emit_move_insn (reg_addr_mem, temp);
1479   emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx));
1480   emit_move_insn (temp, static_chain);
1481   emit_move_insn (reg_addr_mem, temp);
1482   emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx));
1483   emit_move_insn (reg_fnaddr, fnaddr);
1484   emit_move_insn (temp, reg_fnaddr);
1485   emit_insn (gen_andhi3 (temp, temp, GEN_INT (0xFF)));
1486   emit_insn (gen_iorhi3 (temp, temp, GEN_INT (0x0200)));
1487   emit_move_insn (reg_addr_mem, temp);
1488   emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx));
1489   emit_insn (gen_lshrhi3 (reg_fnaddr, reg_fnaddr, GEN_INT (8)));
1490   emit_move_insn (reg_addr_mem, reg_fnaddr);
1491 }
1492
1493 /* Create an RTX representing the place where a function returns a
1494    value of data type VALTYPE.  VALTYPE is a tree node representing a
1495    data type.  Write `TYPE_MODE (VALTYPE)' to get the machine mode
1496    used to represent that type.  On many machines, only the mode is
1497    relevant.  (Actually, on most machines, scalar values are returned
1498    in the same place regardless of mode).
1499
1500    If `PROMOTE_FUNCTION_RETURN' is defined, you must apply the same promotion
1501    rules specified in `PROMOTE_MODE' if VALTYPE is a scalar type.
1502
1503    If the precise function being called is known, FUNC is a tree node
1504    (`FUNCTION_DECL') for it; otherwise, FUNC is a null pointer.  This makes it
1505    possible to use a different value-returning convention for specific
1506    functions when all their calls are known.
1507
1508    `FUNCTION_VALUE' is not used for return vales with aggregate data types,
1509    because these are returned in another way.  See `STRUCT_VALUE_REGNUM' and
1510    related macros.  */
1511 rtx
1512 xstormy16_function_value (valtype, func)
1513      tree valtype;
1514      tree func ATTRIBUTE_UNUSED;
1515 {
1516   enum machine_mode mode;
1517   mode = TYPE_MODE (valtype);
1518   PROMOTE_MODE (mode, 0, valtype);
1519   return gen_rtx_REG (mode, RETURN_VALUE_REGNUM);
1520 }
1521
1522 /* A C compound statement that outputs the assembler code for a thunk function,
1523    used to implement C++ virtual function calls with multiple inheritance.  The
1524    thunk acts as a wrapper around a virtual function, adjusting the implicit
1525    object parameter before handing control off to the real function.
1526
1527    First, emit code to add the integer DELTA to the location that contains the
1528    incoming first argument.  Assume that this argument contains a pointer, and
1529    is the one used to pass the `this' pointer in C++.  This is the incoming
1530    argument *before* the function prologue, e.g. `%o0' on a sparc.  The
1531    addition must preserve the values of all other incoming arguments.
1532
1533    After the addition, emit code to jump to FUNCTION, which is a
1534    `FUNCTION_DECL'.  This is a direct pure jump, not a call, and does not touch
1535    the return address.  Hence returning from FUNCTION will return to whoever
1536    called the current `thunk'.
1537
1538    The effect must be as if @var{function} had been called directly
1539    with the adjusted first argument.  This macro is responsible for
1540    emitting all of the code for a thunk function;
1541    TARGET_ASM_FUNCTION_PROLOGUE and TARGET_ASM_FUNCTION_EPILOGUE are
1542    not invoked.
1543
1544    The THUNK_FNDECL is redundant.  (DELTA and FUNCTION have already been
1545    extracted from it.)  It might possibly be useful on some targets, but
1546    probably not.  */
1547
1548 static void
1549 xstormy16_asm_output_mi_thunk (file, thunk_fndecl, delta,
1550                                vcall_offset, function)
1551      FILE *file;
1552      tree thunk_fndecl ATTRIBUTE_UNUSED;
1553      HOST_WIDE_INT delta;
1554      HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED;
1555      tree function;
1556 {
1557   int regnum = FIRST_ARGUMENT_REGISTER;
1558   
1559   /* There might be a hidden first argument for a returned structure.  */
1560   if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function))))
1561     regnum += 1;
1562   
1563   fprintf (file, "\tadd %s,#0x%x\n", reg_names[regnum], (int) delta & 0xFFFF);
1564   fputs ("\tjmpf ", file);
1565   assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
1566   putc ('\n', file);
1567 }
1568
1569 /* Output constructors and destructors.  Just like 
1570    default_named_section_asm_out_* but don't set the sections writable.  */
1571 #undef  TARGET_ASM_CONSTRUCTOR
1572 #define TARGET_ASM_CONSTRUCTOR xstormy16_asm_out_constructor
1573 #undef  TARGET_ASM_DESTRUCTOR
1574 #define TARGET_ASM_DESTRUCTOR xstormy16_asm_out_destructor
1575
1576 static void
1577 xstormy16_asm_out_destructor (symbol, priority)
1578      rtx symbol;
1579      int priority;
1580 {
1581   const char *section = ".dtors";
1582   char buf[16];
1583
1584   /* ??? This only works reliably with the GNU linker.   */
1585   if (priority != DEFAULT_INIT_PRIORITY)
1586     {
1587       sprintf (buf, ".dtors.%.5u",
1588                /* Invert the numbering so the linker puts us in the proper
1589                   order; constructors are run from right to left, and the
1590                   linker sorts in increasing order.  */
1591                MAX_INIT_PRIORITY - priority);
1592       section = buf;
1593     }
1594
1595   named_section_flags (section, 0);
1596   assemble_align (POINTER_SIZE);
1597   assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
1598 }
1599
1600 static void
1601 xstormy16_asm_out_constructor (symbol, priority)
1602      rtx symbol;
1603      int priority;
1604 {
1605   const char *section = ".ctors";
1606   char buf[16];
1607
1608   /* ??? This only works reliably with the GNU linker.   */
1609   if (priority != DEFAULT_INIT_PRIORITY)
1610     {
1611       sprintf (buf, ".ctors.%.5u",
1612                /* Invert the numbering so the linker puts us in the proper
1613                   order; constructors are run from right to left, and the
1614                   linker sorts in increasing order.  */
1615                MAX_INIT_PRIORITY - priority);
1616       section = buf;
1617     }
1618
1619   named_section_flags (section, 0);
1620   assemble_align (POINTER_SIZE);
1621   assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
1622 }
1623 \f
1624 /* Print a memory address as an operand to reference that memory location.  */
1625 void
1626 xstormy16_print_operand_address (file, address)
1627      FILE * file;
1628      rtx    address;
1629 {
1630   HOST_WIDE_INT offset;
1631   int pre_dec, post_inc;
1632
1633   /* There are a few easy cases.  */
1634   if (GET_CODE (address) == CONST_INT)
1635     {
1636       fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (address) & 0xFFFF);
1637       return;
1638     }
1639   
1640   if (CONSTANT_P (address) || GET_CODE (address) == CODE_LABEL)
1641     {
1642       output_addr_const (file, address);
1643       return;
1644     }
1645   
1646   /* Otherwise, it's hopefully something of the form 
1647      (plus:HI (pre_dec:HI (reg:HI ...)) (const_int ...))
1648   */
1649
1650   if (GET_CODE (address) == PLUS)
1651     {
1652       if (GET_CODE (XEXP (address, 1)) != CONST_INT)
1653         abort ();
1654       offset = INTVAL (XEXP (address, 1));
1655       address = XEXP (address, 0);
1656     }
1657   else
1658     offset = 0;
1659
1660   pre_dec = (GET_CODE (address) == PRE_DEC);
1661   post_inc = (GET_CODE (address) == POST_INC);
1662   if (pre_dec || post_inc)
1663     address = XEXP (address, 0);
1664   
1665   if (GET_CODE (address) != REG)
1666     abort ();
1667
1668   fputc ('(', file);
1669   if (pre_dec)
1670     fputs ("--", file);
1671   fputs (reg_names [REGNO (address)], file);
1672   if (post_inc)
1673     fputs ("++", file);
1674   if (offset != 0)
1675     fprintf (file, "," HOST_WIDE_INT_PRINT_DEC, offset);
1676   fputc (')', file);
1677 }
1678
1679 /* Print an operand to an assembler instruction.  */
1680 void
1681 xstormy16_print_operand (file, x, code)
1682      FILE * file;
1683      rtx    x;
1684      int    code;
1685 {
1686   switch (code)
1687     {
1688     case 'B':
1689         /* There is either one bit set, or one bit clear, in X.
1690            Print it preceded by '#'.  */
1691       {
1692         HOST_WIDE_INT xx = 1;
1693         HOST_WIDE_INT l;
1694
1695         if (GET_CODE (x) == CONST_INT)
1696           xx = INTVAL (x);
1697         else
1698           output_operand_lossage ("`B' operand is not constant");
1699         
1700         l = exact_log2 (xx);
1701         if (l == -1)
1702           l = exact_log2 (~xx);
1703         if (l == -1)
1704           output_operand_lossage ("`B' operand has multiple bits set");
1705         
1706         fprintf (file, IMMEDIATE_PREFIX HOST_WIDE_INT_PRINT_DEC, l);
1707         return;
1708       }
1709
1710     case 'C':
1711       /* Print the symbol without a surrounding @fptr().  */
1712       if (GET_CODE (x) == SYMBOL_REF)
1713         assemble_name (file, XSTR (x, 0));
1714       else if (GET_CODE (x) == LABEL_REF)
1715         output_asm_label (x);
1716       else
1717         xstormy16_print_operand_address (file, x);
1718       return;
1719
1720     case 'o':
1721     case 'O':
1722       /* Print the immediate operand less one, preceded by '#'.  
1723          For 'O', negate it first.  */
1724       {
1725         HOST_WIDE_INT xx = 0;
1726         
1727         if (GET_CODE (x) == CONST_INT)
1728           xx = INTVAL (x);
1729         else
1730           output_operand_lossage ("`o' operand is not constant");
1731         
1732         if (code == 'O')
1733           xx = -xx;
1734         
1735         fprintf (file, IMMEDIATE_PREFIX HOST_WIDE_INT_PRINT_DEC, xx - 1);
1736         return;
1737       }
1738
1739     case 0:
1740       /* Handled below.  */
1741       break;
1742       
1743     default:
1744       output_operand_lossage ("xstormy16_print_operand: unknown code");
1745       return;
1746     }
1747
1748   switch (GET_CODE (x))
1749     {
1750     case REG:
1751       fputs (reg_names [REGNO (x)], file);
1752       break;
1753
1754     case MEM:
1755       xstormy16_print_operand_address (file, XEXP (x, 0));
1756       break;
1757
1758     default:
1759       /* Some kind of constant or label; an immediate operand,
1760          so prefix it with '#' for the assembler.  */
1761       fputs (IMMEDIATE_PREFIX, file);
1762       output_addr_const (file, x);
1763       break;
1764     }
1765
1766   return;
1767 }
1768
1769 \f
1770 /* Expander for the `casesi' pattern.
1771    INDEX is the index of the switch statement.
1772    LOWER_BOUND is a CONST_INT that is the value of INDEX corresponding
1773      to the first table entry.
1774    RANGE is the number of table entries.
1775    TABLE is an ADDR_VEC that is the jump table.
1776    DEFAULT_LABEL is the address to branch to if INDEX is outside the
1777      range LOWER_BOUND to LOWER_BOUND+RANGE-1.
1778 */
1779
1780 void 
1781 xstormy16_expand_casesi (index, lower_bound, range, table, default_label)
1782      rtx index;
1783      rtx lower_bound;
1784      rtx range;
1785      rtx table;
1786      rtx default_label;
1787 {
1788   HOST_WIDE_INT range_i = INTVAL (range);
1789   rtx int_index;
1790
1791   /* This code uses 'br', so it can deal only with tables of size up to
1792      8192 entries.  */
1793   if (range_i >= 8192)
1794     sorry ("switch statement of size %lu entries too large", 
1795            (unsigned long) range_i);
1796
1797   index = expand_binop (SImode, sub_optab, index, lower_bound, NULL_RTX, 0,
1798                         OPTAB_LIB_WIDEN);
1799   emit_cmp_and_jump_insns (index, range, GTU, NULL_RTX, SImode, 1,
1800                            default_label);
1801   int_index = gen_lowpart_common (HImode, index);
1802   emit_insn (gen_ashlhi3 (int_index, int_index, GEN_INT (2)));
1803   emit_jump_insn (gen_tablejump_pcrel (int_index, table));
1804 }
1805
1806 /* Output an ADDR_VEC.  It is output as a sequence of 'jmpf'
1807    instructions, without label or alignment or any other special
1808    constructs.  We know that the previous instruction will be the
1809    `tablejump_pcrel' output above.
1810
1811    TODO: it might be nice to output 'br' instructions if they could
1812    all reach.  */
1813
1814 void
1815 xstormy16_output_addr_vec (file, label, table)
1816      FILE *file;
1817      rtx label ATTRIBUTE_UNUSED;
1818      rtx table;
1819
1820   int vlen, idx;
1821   
1822   function_section (current_function_decl);
1823
1824   vlen = XVECLEN (table, 0);
1825   for (idx = 0; idx < vlen; idx++)
1826     {
1827       fputs ("\tjmpf ", file);
1828       output_asm_label (XEXP (XVECEXP (table, 0, idx), 0));
1829       fputc ('\n', file);
1830     }
1831 }
1832
1833 \f
1834 /* Expander for the `call' patterns.
1835    INDEX is the index of the switch statement.
1836    LOWER_BOUND is a CONST_INT that is the value of INDEX corresponding
1837      to the first table entry.
1838    RANGE is the number of table entries.
1839    TABLE is an ADDR_VEC that is the jump table.
1840    DEFAULT_LABEL is the address to branch to if INDEX is outside the
1841      range LOWER_BOUND to LOWER_BOUND+RANGE-1.
1842 */
1843
1844 void 
1845 xstormy16_expand_call (retval, dest, counter)
1846      rtx retval;
1847      rtx dest;
1848      rtx counter;
1849 {
1850   rtx call, temp;
1851   enum machine_mode mode;
1852
1853   if (GET_CODE (dest) != MEM)
1854     abort ();
1855   dest = XEXP (dest, 0);
1856
1857   if (! CONSTANT_P (dest)
1858       && GET_CODE (dest) != REG)
1859     dest = force_reg (Pmode, dest);
1860   
1861   if (retval == NULL)
1862     mode = VOIDmode;
1863   else
1864     mode = GET_MODE (retval);
1865
1866   call = gen_rtx_CALL (mode, gen_rtx_MEM (FUNCTION_MODE, dest),
1867                        counter);
1868   if (retval)
1869     call = gen_rtx_SET (VOIDmode, retval, call);
1870   
1871   if (! CONSTANT_P (dest))
1872     {
1873       temp = gen_reg_rtx (HImode);
1874       emit_move_insn (temp, const0_rtx);
1875     }
1876   else
1877     temp = const0_rtx;
1878   
1879   call = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, call, 
1880                                                 gen_rtx_USE (VOIDmode, temp)));
1881   emit_call_insn (call);
1882 }
1883 \f
1884 /* Expanders for multiword computational operations.  */
1885
1886 /* Expander for arithmetic operations; emit insns to compute
1887
1888    (set DEST (CODE:MODE SRC0 SRC1))
1889    
1890    using CARRY as a temporary.  When CODE is COMPARE, a branch
1891    template is generated (this saves duplicating code in
1892    xstormy16_split_cbranch).  */
1893
1894 void 
1895 xstormy16_expand_arith (mode, code, dest, src0, src1, carry)
1896      enum machine_mode mode;
1897      enum rtx_code code;
1898      rtx dest;
1899      rtx src0;
1900      rtx src1;
1901      rtx carry;
1902 {
1903   int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
1904   int i;
1905   int firstloop = 1;
1906
1907   if (code == NEG)
1908     emit_move_insn (src0, const0_rtx);
1909   
1910   for (i = 0; i < num_words; i++)
1911     {
1912       rtx w_src0, w_src1, w_dest;
1913       rtx insn;
1914       
1915       w_src0 = simplify_gen_subreg (word_mode, src0, mode, 
1916                                     i * UNITS_PER_WORD);
1917       w_src1 = simplify_gen_subreg (word_mode, src1, mode, i * UNITS_PER_WORD);
1918       w_dest = simplify_gen_subreg (word_mode, dest, mode, i * UNITS_PER_WORD);
1919
1920       switch (code)
1921         {
1922         case PLUS:
1923           if (firstloop
1924               && GET_CODE (w_src1) == CONST_INT && INTVAL (w_src1) == 0)
1925             continue;
1926           
1927           if (firstloop)
1928             insn = gen_addchi4 (w_dest, w_src0, w_src1, carry);
1929           else
1930             insn = gen_addchi5 (w_dest, w_src0, w_src1, carry, carry);
1931           break;
1932
1933         case NEG:
1934         case MINUS:
1935         case COMPARE:
1936           if (code == COMPARE && i == num_words - 1)
1937             {
1938               rtx branch, sub, clobber, sub_1;
1939               
1940               sub_1 = gen_rtx_MINUS (HImode, w_src0, 
1941                                      gen_rtx_ZERO_EXTEND (HImode, carry));
1942               sub = gen_rtx_SET (VOIDmode, w_dest,
1943                                  gen_rtx_MINUS (HImode, sub_1, w_src1));
1944               clobber = gen_rtx_CLOBBER (VOIDmode, carry);
1945               branch = gen_rtx_SET (VOIDmode, pc_rtx,
1946                                     gen_rtx_IF_THEN_ELSE (VOIDmode,
1947                                                           gen_rtx_EQ (HImode,
1948                                                                       sub_1,
1949                                                                       w_src1),
1950                                                           pc_rtx,
1951                                                           pc_rtx));
1952               insn = gen_rtx_PARALLEL (VOIDmode,
1953                                        gen_rtvec (3, branch, sub, clobber));
1954             }
1955           else if (firstloop
1956                    && code != COMPARE
1957                    && GET_CODE (w_src1) == CONST_INT && INTVAL (w_src1) == 0)
1958             continue;
1959           else if (firstloop)
1960             insn = gen_subchi4 (w_dest, w_src0, w_src1, carry);
1961           else
1962             insn = gen_subchi5 (w_dest, w_src0, w_src1, carry, carry);
1963           break;
1964
1965         case IOR:
1966         case XOR:
1967         case AND:
1968           if (GET_CODE (w_src1) == CONST_INT 
1969               && INTVAL (w_src1) == -(code == AND))
1970             continue;
1971           
1972           insn = gen_rtx_SET (VOIDmode, w_dest, gen_rtx (code, mode,
1973                                                          w_src0, w_src1));
1974           break;
1975
1976         case NOT:
1977           insn = gen_rtx_SET (VOIDmode, w_dest, gen_rtx_NOT (mode, w_src0));
1978           break;
1979
1980         default:
1981           abort ();
1982         }
1983       
1984       firstloop = 0;
1985       emit (insn);
1986     }
1987
1988   /* If we emit nothing, try_split() will think we failed.  So emit
1989      something that does nothing and can be optimized away.  */
1990   if (firstloop)
1991     emit (gen_nop ());
1992 }
1993
1994 /* Return 1 if OP is a shift operator.  */
1995
1996 int
1997 shift_operator (op, mode)
1998      register rtx op;
1999      enum machine_mode mode ATTRIBUTE_UNUSED;
2000 {
2001   enum rtx_code code = GET_CODE (op);
2002
2003   return (code == ASHIFT
2004           || code == ASHIFTRT
2005           || code == LSHIFTRT);
2006 }
2007
2008 /* The shift operations are split at output time for constant values;
2009    variable-width shifts get handed off to a library routine.  
2010
2011    Generate an output string to do (set X (CODE:MODE X SIZE_R))
2012    SIZE_R will be a CONST_INT, X will be a hard register.  */
2013
2014 const char * 
2015 xstormy16_output_shift (mode, code, x, size_r, temp)
2016      enum machine_mode mode;
2017      enum rtx_code code;
2018      rtx x;
2019      rtx size_r;
2020      rtx temp;
2021 {
2022   HOST_WIDE_INT size;
2023   const char *r0, *r1, *rt;
2024   static char r[64];
2025
2026   if (GET_CODE (size_r) != CONST_INT
2027       || GET_CODE (x) != REG
2028       || mode != SImode)
2029     abort ();
2030   size = INTVAL (size_r) & (GET_MODE_BITSIZE (mode) - 1);
2031
2032   if (size == 0)
2033     return "";
2034
2035   r0 = reg_names [REGNO (x)];
2036   r1 = reg_names [REGNO (x) + 1];
2037
2038   /* For shifts of size 1, we can use the rotate instructions.  */
2039   if (size == 1)
2040     {
2041       switch (code)
2042         {
2043         case ASHIFT:
2044           sprintf (r, "shl %s,#1 | rlc %s,#1", r0, r1);
2045           break;
2046         case ASHIFTRT:
2047           sprintf (r, "asr %s,#1 | rrc %s,#1", r1, r0);
2048           break;
2049         case LSHIFTRT:
2050           sprintf (r, "shr %s,#1 | rrc %s,#1", r1, r0);
2051           break;
2052         default:
2053           abort ();
2054         }
2055       return r;
2056     }
2057   
2058   /* For large shifts, there are easy special cases.  */
2059   if (size == 16)
2060     {
2061       switch (code)
2062         {
2063         case ASHIFT:
2064           sprintf (r, "mov %s,%s | mov %s,#0", r1, r0, r0);
2065           break;
2066         case ASHIFTRT:
2067           sprintf (r, "mov %s,%s | asr %s,#15", r0, r1, r1);
2068           break;
2069         case LSHIFTRT:
2070           sprintf (r, "mov %s,%s | mov %s,#0", r0, r1, r1);
2071           break;
2072         default:
2073           abort ();
2074         }
2075       return r;
2076     }
2077   if (size > 16)
2078     {
2079       switch (code)
2080         {
2081         case ASHIFT:
2082           sprintf (r, "mov %s,%s | mov %s,#0 | shl %s,#%d", 
2083                    r1, r0, r0, r1, (int) size - 16);
2084           break;
2085         case ASHIFTRT:
2086           sprintf (r, "mov %s,%s | asr %s,#15 | asr %s,#%d", 
2087                    r0, r1, r1, r0, (int) size - 16);
2088           break;
2089         case LSHIFTRT:
2090           sprintf (r, "mov %s,%s | mov %s,#0 | shr %s,#%d", 
2091                    r0, r1, r1, r0, (int) size - 16);
2092           break;
2093         default:
2094           abort ();
2095         }
2096       return r;
2097     }
2098
2099   /* For the rest, we have to do more work.  In particular, we
2100      need a temporary.  */
2101   rt = reg_names [REGNO (temp)];
2102   switch (code)
2103     {
2104     case ASHIFT:
2105       sprintf (r, 
2106                "mov %s,%s | shl %s,#%d | shl %s,#%d | shr %s,#%d | or %s,%s", 
2107                rt, r0, r0, (int) size, r1, (int) size, rt, (int) (16-size),
2108                r1, rt);
2109       break;
2110     case ASHIFTRT:
2111       sprintf (r, 
2112                "mov %s,%s | asr %s,#%d | shr %s,#%d | shl %s,#%d | or %s,%s", 
2113                rt, r1, r1, (int) size, r0, (int) size, rt, (int) (16-size),
2114                r0, rt);
2115       break;
2116     case LSHIFTRT:
2117       sprintf (r, 
2118                "mov %s,%s | shr %s,#%d | shr %s,#%d | shl %s,#%d | or %s,%s", 
2119                rt, r1, r1, (int) size, r0, (int) size, rt, (int) (16-size),
2120                r0, rt);
2121       break;
2122     default:
2123       abort ();
2124     }
2125   return r;
2126 }
2127 \f
2128 /* Attribute handling.  */
2129
2130 /* Return nonzero if the function is an interrupt function.  */
2131 int
2132 xstormy16_interrupt_function_p ()
2133 {
2134   tree attributes;
2135   
2136   /* The dwarf2 mechanism asks for INCOMING_FRAME_SP_OFFSET before
2137      any functions are declared, which is demonstrably wrong, but
2138      it is worked around here.  FIXME.  */
2139   if (!cfun)
2140     return 0;
2141
2142   attributes = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
2143   return lookup_attribute ("interrupt", attributes) != NULL_TREE;
2144 }
2145
2146 #undef TARGET_ATTRIBUTE_TABLE
2147 #define TARGET_ATTRIBUTE_TABLE xstormy16_attribute_table
2148 static tree xstormy16_handle_interrupt_attribute PARAMS ((tree *, tree, tree, int, bool *));
2149 static const struct attribute_spec xstormy16_attribute_table[] =
2150 {
2151   /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
2152   { "interrupt", 0, 0, false, true,  true,  xstormy16_handle_interrupt_attribute },
2153   { NULL,        0, 0, false, false, false, NULL }
2154 };
2155
2156 /* Handle an "interrupt" attribute;
2157    arguments as in struct attribute_spec.handler.  */
2158 static tree
2159 xstormy16_handle_interrupt_attribute (node, name, args, flags, no_add_attrs)
2160      tree *node;
2161      tree name;
2162      tree args ATTRIBUTE_UNUSED;
2163      int flags ATTRIBUTE_UNUSED;
2164      bool *no_add_attrs;
2165 {
2166   if (TREE_CODE (*node) != FUNCTION_TYPE)
2167     {
2168       warning ("`%s' attribute only applies to functions",
2169                IDENTIFIER_POINTER (name));
2170       *no_add_attrs = true;
2171     }
2172
2173   return NULL_TREE;
2174 }
2175 \f
2176 #undef TARGET_INIT_BUILTINS
2177 #define TARGET_INIT_BUILTINS xstormy16_init_builtins
2178 #undef TARGET_EXPAND_BUILTIN
2179 #define TARGET_EXPAND_BUILTIN xstormy16_expand_builtin
2180
2181 static struct {
2182   const char *name;
2183   int md_code;
2184   const char *arg_ops; /* 0..9, t for temp register, r for return value */
2185   const char *arg_types; /* s=short,l=long, upper case for unsigned */
2186 } s16builtins[] = {
2187   { "__sdivlh", CODE_FOR_sdivlh, "rt01", "sls" },
2188   { "__smodlh", CODE_FOR_sdivlh, "tr01", "sls" },
2189   { "__udivlh", CODE_FOR_udivlh, "rt01", "SLS" },
2190   { "__umodlh", CODE_FOR_udivlh, "tr01", "SLS" },
2191   { 0, 0, 0, 0 }
2192 };
2193
2194 static void
2195 xstormy16_init_builtins ()
2196 {
2197   tree args, ret_type, arg;
2198   int i, a;
2199
2200   ret_type = void_type_node;
2201
2202   for (i=0; s16builtins[i].name; i++)
2203     {
2204       args = void_list_node;
2205       for (a=strlen (s16builtins[i].arg_types)-1; a>=0; a--)
2206         {
2207           switch (s16builtins[i].arg_types[a])
2208             {
2209             case 's': arg = short_integer_type_node; break;
2210             case 'S': arg = short_unsigned_type_node; break;
2211             case 'l': arg = long_integer_type_node; break;
2212             case 'L': arg = long_unsigned_type_node; break;
2213             default: abort();
2214             }
2215           if (a == 0)
2216             ret_type = arg;
2217           else
2218             args = tree_cons (NULL_TREE, arg, args);
2219         }
2220       builtin_function (s16builtins[i].name,
2221                         build_function_type (ret_type, args),
2222                         i, BUILT_IN_MD, NULL, NULL);
2223     }
2224 }
2225
2226 static rtx
2227 xstormy16_expand_builtin(exp, target, subtarget, mode, ignore)
2228      tree exp;
2229      rtx target;
2230      rtx subtarget ATTRIBUTE_UNUSED;
2231      enum machine_mode mode ATTRIBUTE_UNUSED;
2232      int ignore ATTRIBUTE_UNUSED;
2233 {
2234   rtx op[10], args[10], pat, copyto[10], retval = 0;
2235   tree fndecl, argtree;
2236   int i, a, o, code;
2237
2238   fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
2239   argtree = TREE_OPERAND (exp, 1);
2240   i = DECL_FUNCTION_CODE (fndecl);
2241   code = s16builtins[i].md_code;
2242
2243   for (a = 0; a < 10 && argtree; a++)
2244     {
2245       args[a] = expand_expr (TREE_VALUE (argtree), NULL_RTX, VOIDmode, 0);
2246       argtree = TREE_CHAIN (argtree);
2247     }
2248
2249   for (o = 0; s16builtins[i].arg_ops[o]; o++)
2250     {
2251       char ao = s16builtins[i].arg_ops[o];
2252       char c = insn_data[code].operand[o].constraint[0];
2253       int omode;
2254
2255       copyto[o] = 0;
2256
2257       omode = insn_data[code].operand[o].mode;
2258       if (ao == 'r')
2259         op[o] = target ? target : gen_reg_rtx (omode);
2260       else if (ao == 't')
2261         op[o] = gen_reg_rtx (omode);
2262       else
2263         op[o] = args[(int) hex_value (ao)];
2264
2265       if (! (*insn_data[code].operand[o].predicate) (op[o], GET_MODE (op[o])))
2266         {
2267           if (c == '+' || c == '=')
2268             {
2269               copyto[o] = op[o];
2270               op[o] = gen_reg_rtx (omode);
2271             }
2272           else
2273             op[o] = copy_to_mode_reg (omode, op[o]);
2274         }
2275
2276       if (ao == 'r')
2277         retval = op[o];
2278     }
2279
2280   pat = GEN_FCN (code) (op[0], op[1], op[2], op[3], op[4],
2281                         op[5], op[6], op[7], op[8], op[9]);
2282   emit_insn (pat);
2283
2284   for (o = 0; s16builtins[i].arg_ops[o]; o++)
2285     if (copyto[o])
2286       {
2287         emit_move_insn (copyto[o], op[o]);
2288         if (op[o] == retval)
2289           retval = copyto[o];
2290       }
2291
2292   return retval;
2293 }
2294
2295 \f
2296 #undef TARGET_ASM_ALIGNED_HI_OP
2297 #define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
2298 #undef TARGET_ASM_ALIGNED_SI_OP
2299 #define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
2300
2301 #undef TARGET_ASM_OUTPUT_MI_THUNK
2302 #define TARGET_ASM_OUTPUT_MI_THUNK xstormy16_asm_output_mi_thunk
2303 #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
2304 #define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
2305
2306 #undef TARGET_RTX_COSTS
2307 #define TARGET_RTX_COSTS xstormy16_rtx_costs
2308 #undef TARGET_ADDRESS_COST
2309 #define TARGET_ADDRESS_COST xstormy16_address_cost
2310
2311 struct gcc_target targetm = TARGET_INITIALIZER;