OSDN Git Service

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