OSDN Git Service

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