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