OSDN Git Service

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