]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/config/stormy16/stormy16.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / config / stormy16 / stormy16.cc
CommitLineData
c6243b4c 1/* Xstormy16 target functions.
83ffe9cd 2 Copyright (C) 1997-2023 Free Software Foundation, Inc.
4b58290f
GK
3 Contributed by Red Hat, Inc.
4
5ab9749e 5 This file is part of GCC.
4b58290f 6
5ab9749e
NC
7 GCC is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
4b58290f 11
5ab9749e
NC
12 GCC is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
4b58290f 16
5ab9749e
NC
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
4b58290f 20
8fcc61f8
RS
21#define IN_TARGET_CODE 1
22
4b58290f
GK
23#include "config.h"
24#include "system.h"
4977bab6 25#include "coretypes.h"
c7131fb2 26#include "backend.h"
e11c4407
AM
27#include "target.h"
28#include "rtl.h"
c7131fb2 29#include "tree.h"
314e6352
ML
30#include "stringpool.h"
31#include "attribs.h"
c7131fb2 32#include "gimple.h"
c7131fb2 33#include "df.h"
4d0cdd0c 34#include "memmodel.h"
e11c4407
AM
35#include "tm_p.h"
36#include "stringpool.h"
37#include "optabs.h"
38#include "emit-rtl.h"
4b58290f 39#include "recog.h"
718f9c0f 40#include "diagnostic-core.h"
e11c4407 41#include "output.h"
40e23961 42#include "fold-const.h"
d8a2d370
DN
43#include "stor-layout.h"
44#include "varasm.h"
45#include "calls.h"
36566b39 46#include "explow.h"
4b58290f 47#include "expr.h"
f1e639b1 48#include "langhooks.h"
60393bbc 49#include "cfgrtl.h"
45b0be94 50#include "gimplify.h"
6b1ce545 51#include "reload.h"
9b2b7279 52#include "builtins.h"
4b58290f 53
994c5d85 54/* This file should be included last. */
d58627a0
RS
55#include "target-def.h"
56
51c16b7e
SB
57static rtx emit_addhi3_postreload (rtx, rtx, rtx);
58static void xstormy16_asm_out_constructor (rtx, int);
59static void xstormy16_asm_out_destructor (rtx, int);
60static void xstormy16_asm_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
61 HOST_WIDE_INT, tree);
4b58290f 62
51c16b7e 63static void xstormy16_init_builtins (void);
ef4bddc2 64static rtx xstormy16_expand_builtin (tree, rtx, rtx, machine_mode, int);
ef4bddc2 65static int xstormy16_address_cost (rtx, machine_mode, addr_space_t, bool);
586de218 66static bool xstormy16_return_in_memory (const_tree, const_tree);
3d4b192a 67
d6b5193b
RS
68static GTY(()) section *bss100_section;
69
3c50106f
RH
70/* Compute a (partial) cost for rtx X. Return true if the complete
71 cost has been computed, and false if subexpressions should be
72 scanned. In either case, *TOTAL contains the cost result. */
73
74static bool
e548c9df
AM
75xstormy16_rtx_costs (rtx x, machine_mode mode ATTRIBUTE_UNUSED,
76 int outer_code ATTRIBUTE_UNUSED,
68f932c4
RS
77 int opno ATTRIBUTE_UNUSED, int *total,
78 bool speed ATTRIBUTE_UNUSED)
3c50106f 79{
e548c9df
AM
80 int code = GET_CODE (x);
81
3c50106f
RH
82 switch (code)
83 {
84 case CONST_INT:
85 if (INTVAL (x) < 16 && INTVAL (x) >= 0)
86 *total = COSTS_N_INSNS (1) / 2;
87 else if (INTVAL (x) < 256 && INTVAL (x) >= 0)
88 *total = COSTS_N_INSNS (1);
89 else
90 *total = COSTS_N_INSNS (2);
91 return true;
92
93 case CONST_DOUBLE:
94 case CONST:
95 case SYMBOL_REF:
96 case LABEL_REF:
5ab9749e 97 *total = COSTS_N_INSNS (2);
3c50106f
RH
98 return true;
99
100 case MULT:
101 *total = COSTS_N_INSNS (35 + 6);
102 return true;
103 case DIV:
104 *total = COSTS_N_INSNS (51 - 6);
105 return true;
106
107 default:
108 return false;
109 }
110}
111
dcefdf67 112static int
ef4bddc2 113xstormy16_address_cost (rtx x, machine_mode mode ATTRIBUTE_UNUSED,
b413068c
OE
114 addr_space_t as ATTRIBUTE_UNUSED,
115 bool speed ATTRIBUTE_UNUSED)
dcefdf67 116{
a21eaf5e 117 return (CONST_INT_P (x) ? 2
dcefdf67
RH
118 : GET_CODE (x) == PLUS ? 7
119 : 5);
120}
3c50106f 121
6b1ce545
AS
122/* Worker function for TARGET_MEMORY_MOVE_COST. */
123
124static int
ef4bddc2 125xstormy16_memory_move_cost (machine_mode mode, reg_class_t rclass,
6b1ce545
AS
126 bool in)
127{
128 return (5 + memory_move_secondary_cost (mode, rclass, in));
129}
130
4b58290f
GK
131/* Branches are handled as follows:
132
133 1. HImode compare-and-branches. The machine supports these
134 natively, so the appropriate pattern is emitted directly.
135
136 2. SImode EQ and NE. These are emitted as pairs of HImode
5ab9749e 137 compare-and-branches.
4b58290f
GK
138
139 3. SImode LT, GE, LTU and GEU. These are emitted as a sequence
140 of a SImode subtract followed by a branch (not a compare-and-branch),
141 like this:
142 sub
143 sbc
144 blt
145
146 4. SImode GT, LE, GTU, LEU. These are emitted as a sequence like:
147 sub
148 sbc
149 blt
150 or
5ab9749e 151 bne. */
4b58290f
GK
152
153/* Emit a branch of kind CODE to location LOC. */
154
155void
f90b7a5a 156xstormy16_emit_cbranch (enum rtx_code code, rtx op0, rtx op1, rtx loc)
4b58290f 157{
4b58290f
GK
158 rtx condition_rtx, loc_ref, branch, cy_clobber;
159 rtvec vec;
ef4bddc2 160 machine_mode mode;
5ab9749e 161
4b58290f 162 mode = GET_MODE (op0);
4718bfd8 163 gcc_assert (mode == HImode || mode == SImode);
4b58290f
GK
164
165 if (mode == SImode
166 && (code == GT || code == LE || code == GTU || code == LEU))
167 {
168 int unsigned_p = (code == GTU || code == LEU);
169 int gt_p = (code == GT || code == GTU);
cd4c46f3 170 rtx lab = NULL_RTX;
5ab9749e 171
4b58290f
GK
172 if (gt_p)
173 lab = gen_label_rtx ();
f90b7a5a 174 xstormy16_emit_cbranch (unsigned_p ? LTU : LT, op0, op1, gt_p ? lab : loc);
4b58290f
GK
175 /* This should be generated as a comparison against the temporary
176 created by the previous insn, but reload can't handle that. */
f90b7a5a 177 xstormy16_emit_cbranch (gt_p ? NE : EQ, op0, op1, loc);
4b58290f
GK
178 if (gt_p)
179 emit_label (lab);
180 return;
181 }
5ab9749e 182 else if (mode == SImode
4b58290f
GK
183 && (code == NE || code == EQ)
184 && op1 != const0_rtx)
185 {
f90b7a5a 186 rtx op0_word, op1_word;
cd4c46f3 187 rtx lab = NULL_RTX;
4b58290f
GK
188 int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
189 int i;
5ab9749e 190
4b58290f
GK
191 if (code == EQ)
192 lab = gen_label_rtx ();
5ab9749e 193
4b58290f
GK
194 for (i = 0; i < num_words - 1; i++)
195 {
f90b7a5a
PB
196 op0_word = simplify_gen_subreg (word_mode, op0, mode,
197 i * UNITS_PER_WORD);
198 op1_word = simplify_gen_subreg (word_mode, op1, mode,
199 i * UNITS_PER_WORD);
200 xstormy16_emit_cbranch (NE, op0_word, op1_word, code == EQ ? lab : loc);
4b58290f 201 }
f90b7a5a
PB
202 op0_word = simplify_gen_subreg (word_mode, op0, mode,
203 i * UNITS_PER_WORD);
204 op1_word = simplify_gen_subreg (word_mode, op1, mode,
205 i * UNITS_PER_WORD);
206 xstormy16_emit_cbranch (code, op0_word, op1_word, loc);
4b58290f
GK
207
208 if (code == EQ)
209 emit_label (lab);
210 return;
211 }
212
213 /* We can't allow reload to try to generate any reload after a branch,
214 so when some register must match we must make the temporary ourselves. */
215 if (mode != HImode)
216 {
217 rtx tmp;
218 tmp = gen_reg_rtx (mode);
219 emit_move_insn (tmp, op0);
220 op0 = tmp;
221 }
222
1c563bed 223 condition_rtx = gen_rtx_fmt_ee (code, mode, op0, op1);
4b58290f 224 loc_ref = gen_rtx_LABEL_REF (VOIDmode, loc);
f7df4a84 225 branch = gen_rtx_SET (pc_rtx,
4b58290f
GK
226 gen_rtx_IF_THEN_ELSE (VOIDmode, condition_rtx,
227 loc_ref, pc_rtx));
228
b72bbbcb 229 cy_clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (BImode, CARRY_REGNUM));
4b58290f
GK
230
231 if (mode == HImode)
232 vec = gen_rtvec (2, branch, cy_clobber);
233 else if (code == NE || code == EQ)
234 vec = gen_rtvec (2, branch, gen_rtx_CLOBBER (VOIDmode, op0));
235 else
236 {
237 rtx sub;
238#if 0
f7df4a84 239 sub = gen_rtx_SET (op0, gen_rtx_MINUS (SImode, op0, op1));
4b58290f
GK
240#else
241 sub = gen_rtx_CLOBBER (SImode, op0);
242#endif
243 vec = gen_rtvec (3, branch, sub, cy_clobber);
244 }
245
246 emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec));
247}
248
249/* Take a SImode conditional branch, one of GT/LE/GTU/LEU, and split
250 the arithmetic operation. Most of the work is done by
c6243b4c 251 xstormy16_expand_arith. */
4b58290f
GK
252
253void
ef4bddc2 254xstormy16_split_cbranch (machine_mode mode, rtx label, rtx comparison,
d40ba0b6 255 rtx dest)
4b58290f
GK
256{
257 rtx op0 = XEXP (comparison, 0);
258 rtx op1 = XEXP (comparison, 1);
b32d5189 259 rtx_insn *seq, *last_insn;
4b58290f 260 rtx compare;
5ab9749e 261
4b58290f 262 start_sequence ();
d40ba0b6 263 xstormy16_expand_arith (mode, COMPARE, dest, op0, op1);
2f937369 264 seq = get_insns ();
4b58290f 265 end_sequence ();
2f937369 266
4718bfd8 267 gcc_assert (INSN_P (seq));
2f937369
DM
268
269 last_insn = seq;
270 while (NEXT_INSN (last_insn) != NULL_RTX)
271 last_insn = NEXT_INSN (last_insn);
272
273 compare = SET_SRC (XVECEXP (PATTERN (last_insn), 0, 0));
4b58290f
GK
274 PUT_CODE (XEXP (compare, 0), GET_CODE (comparison));
275 XEXP (compare, 1) = gen_rtx_LABEL_REF (VOIDmode, label);
276 emit_insn (seq);
277}
278
279
280/* Return the string to output a conditional branch to LABEL, which is
281 the operand number of the label.
282
283 OP is the conditional expression, or NULL for branch-always.
284
5e7a8ee0 285 REVERSED is nonzero if we should reverse the sense of the comparison.
4b58290f
GK
286
287 INSN is the insn. */
288
289char *
84034c69
DM
290xstormy16_output_cbranch_hi (rtx op, const char *label, int reversed,
291 rtx_insn *insn)
4b58290f
GK
292{
293 static char string[64];
294 int need_longbranch = (op != NULL_RTX
295 ? get_attr_length (insn) == 8
296 : get_attr_length (insn) == 4);
297 int really_reversed = reversed ^ need_longbranch;
298 const char *ccode;
0a2aaacc 299 const char *templ;
4b58290f
GK
300 const char *operands;
301 enum rtx_code code;
5ab9749e 302
4b58290f
GK
303 if (! op)
304 {
305 if (need_longbranch)
306 ccode = "jmpf";
307 else
308 ccode = "br";
309 sprintf (string, "%s %s", ccode, label);
310 return string;
311 }
312
313 code = GET_CODE (op);
314
a21eaf5e 315 if (! REG_P (XEXP (op, 0)))
4b58290f
GK
316 {
317 code = swap_condition (code);
318 operands = "%3,%2";
319 }
320 else
321 operands = "%2,%3";
322
323 /* Work out which way this really branches. */
324 if (really_reversed)
325 code = reverse_condition (code);
326
327 switch (code)
328 {
329 case EQ: ccode = "z"; break;
330 case NE: ccode = "nz"; break;
331 case GE: ccode = "ge"; break;
332 case LT: ccode = "lt"; break;
333 case GT: ccode = "gt"; break;
334 case LE: ccode = "le"; break;
335 case GEU: ccode = "nc"; break;
336 case LTU: ccode = "c"; break;
337 case GTU: ccode = "hi"; break;
338 case LEU: ccode = "ls"; break;
5ab9749e 339
4b58290f 340 default:
4718bfd8 341 gcc_unreachable ();
4b58290f
GK
342 }
343
344 if (need_longbranch)
0a2aaacc 345 templ = "b%s %s,.+8 | jmpf %s";
4b58290f 346 else
0a2aaacc
KG
347 templ = "b%s %s,%s";
348 sprintf (string, templ, ccode, operands, label);
5ab9749e 349
4b58290f
GK
350 return string;
351}
352
353/* Return the string to output a conditional branch to LABEL, which is
354 the operand number of the label, but suitable for the tail of a
355 SImode branch.
356
357 OP is the conditional expression (OP is never NULL_RTX).
358
5e7a8ee0 359 REVERSED is nonzero if we should reverse the sense of the comparison.
4b58290f
GK
360
361 INSN is the insn. */
362
363char *
84034c69
DM
364xstormy16_output_cbranch_si (rtx op, const char *label, int reversed,
365 rtx_insn *insn)
4b58290f
GK
366{
367 static char string[64];
368 int need_longbranch = get_attr_length (insn) >= 8;
369 int really_reversed = reversed ^ need_longbranch;
370 const char *ccode;
0a2aaacc 371 const char *templ;
4b58290f
GK
372 char prevop[16];
373 enum rtx_code code;
5ab9749e 374
4b58290f
GK
375 code = GET_CODE (op);
376
377 /* Work out which way this really branches. */
378 if (really_reversed)
379 code = reverse_condition (code);
380
381 switch (code)
382 {
383 case EQ: ccode = "z"; break;
384 case NE: ccode = "nz"; break;
385 case GE: ccode = "ge"; break;
386 case LT: ccode = "lt"; break;
387 case GEU: ccode = "nc"; break;
388 case LTU: ccode = "c"; break;
389
390 /* The missing codes above should never be generated. */
391 default:
4718bfd8 392 gcc_unreachable ();
4b58290f
GK
393 }
394
395 switch (code)
396 {
397 case EQ: case NE:
398 {
399 int regnum;
5ab9749e 400
a21eaf5e 401 gcc_assert (REG_P (XEXP (op, 0)));
5ab9749e 402
4b58290f
GK
403 regnum = REGNO (XEXP (op, 0));
404 sprintf (prevop, "or %s,%s", reg_names[regnum], reg_names[regnum+1]);
405 }
406 break;
407
408 case GE: case LT: case GEU: case LTU:
409 strcpy (prevop, "sbc %2,%3");
410 break;
411
412 default:
4718bfd8 413 gcc_unreachable ();
4b58290f
GK
414 }
415
416 if (need_longbranch)
0a2aaacc 417 templ = "%s | b%s .+6 | jmpf %s";
4b58290f 418 else
0a2aaacc
KG
419 templ = "%s | b%s %s";
420 sprintf (string, templ, prevop, ccode, label);
5ab9749e 421
4b58290f
GK
422 return string;
423}
424\f
425/* Many machines have some registers that cannot be copied directly to or from
426 memory or even from other types of registers. An example is the `MQ'
427 register, which on most machines, can only be copied to or from general
428 registers, but not memory. Some machines allow copying all registers to and
429 from memory, but require a scratch register for stores to some memory
430 locations (e.g., those with symbolic address on the RT, and those with
981f6289 431 certain symbolic address on the SPARC when compiling PIC). In some cases,
4b58290f
GK
432 both an intermediate and a scratch register are required.
433
434 You should define these macros to indicate to the reload phase that it may
435 need to allocate at least one register for a reload in addition to the
436 register to contain the data. Specifically, if copying X to a register
0a2aaacc 437 RCLASS in MODE requires an intermediate register, you should define
4b58290f
GK
438 `SECONDARY_INPUT_RELOAD_CLASS' to return the largest register class all of
439 whose registers can be used as intermediate registers or scratch registers.
440
0a2aaacc 441 If copying a register RCLASS in MODE to X requires an intermediate or scratch
4b58290f
GK
442 register, `SECONDARY_OUTPUT_RELOAD_CLASS' should be defined to return the
443 largest register class required. If the requirements for input and output
444 reloads are the same, the macro `SECONDARY_RELOAD_CLASS' should be used
445 instead of defining both macros identically.
446
447 The values returned by these macros are often `GENERAL_REGS'. Return
448 `NO_REGS' if no spare register is needed; i.e., if X can be directly copied
0a2aaacc 449 to or from a register of RCLASS in MODE without requiring a scratch register.
4b58290f
GK
450 Do not define this macro if it would always return `NO_REGS'.
451
452 If a scratch register is required (either with or without an intermediate
453 register), you should define patterns for `reload_inM' or `reload_outM', as
454 required.. These patterns, which will normally be implemented with a
455 `define_expand', should be similar to the `movM' patterns, except that
456 operand 2 is the scratch register.
457
458 Define constraints for the reload register and scratch register that contain
459 a single register class. If the original reload register (whose class is
0a2aaacc 460 RCLASS) can meet the constraint given in the pattern, the value returned by
4b58290f
GK
461 these macros is used for the class of the scratch register. Otherwise, two
462 additional reload registers are required. Their classes are obtained from
463 the constraints in the insn pattern.
464
465 X might be a pseudo-register or a `subreg' of a pseudo-register, which could
466 either be in a hard register or in memory. Use `true_regnum' to find out;
467 it will return -1 if the pseudo is in memory and the hard register number if
468 it is in a register.
469
470 These macros should not be used in the case where a particular class of
471 registers can only be copied to memory and not to another class of
472 registers. In that case, secondary reload registers are not needed and
473 would not be helpful. Instead, a stack location must be used to perform the
e03f5d43 474 copy and the `movM' pattern should use memory as an intermediate storage.
4b58290f
GK
475 This case often occurs between floating-point and general registers. */
476
477enum reg_class
0a2aaacc 478xstormy16_secondary_reload_class (enum reg_class rclass,
ef4bddc2 479 machine_mode mode ATTRIBUTE_UNUSED,
51c16b7e 480 rtx x)
4b58290f
GK
481{
482 /* This chip has the interesting property that only the first eight
483 registers can be moved to/from memory. */
a21eaf5e
NC
484 if ((MEM_P (x)
485 || ((GET_CODE (x) == SUBREG || REG_P (x))
4b58290f
GK
486 && (true_regnum (x) == -1
487 || true_regnum (x) >= FIRST_PSEUDO_REGISTER)))
0a2aaacc 488 && ! reg_class_subset_p (rclass, EIGHT_REGS))
4b58290f
GK
489 return EIGHT_REGS;
490
4b58290f
GK
491 return NO_REGS;
492}
493
ef795fc2
AS
494/* Worker function for TARGET_PREFERRED_RELOAD_CLASS
495 and TARGET_PREFERRED_OUTPUT_RELOAD_CLASS. */
496
497static reg_class_t
498xstormy16_preferred_reload_class (rtx x, reg_class_t rclass)
4b58290f 499{
7f26e60c
JL
500 /* Only the first eight registers can be moved to/from memory.
501 So those prefer EIGHT_REGS.
502
503 Similarly reloading an auto-increment address is going to
504 require loads and stores, so we must use EIGHT_REGS for those
505 too. */
506 if (rclass == GENERAL_REGS
507 && (MEM_P (x)
508 || GET_CODE (x) == POST_INC
509 || GET_CODE (x) == PRE_DEC
510 || GET_CODE (x) == PRE_MODIFY))
4b58290f
GK
511 return EIGHT_REGS;
512
0a2aaacc 513 return rclass;
4b58290f
GK
514}
515
54e9a19d
DD
516/* Predicate for symbols and addresses that reflect special 8-bit
517 addressing. */
5ab9749e 518
a6fef2e1 519bool
54e9a19d 520xstormy16_below100_symbol (rtx x,
ef4bddc2 521 machine_mode mode ATTRIBUTE_UNUSED)
54e9a19d
DD
522{
523 if (GET_CODE (x) == CONST)
524 x = XEXP (x, 0);
a21eaf5e 525 if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1)))
54e9a19d 526 x = XEXP (x, 0);
2f806f3b 527
54e9a19d 528 if (GET_CODE (x) == SYMBOL_REF)
2f806f3b
NC
529 return (SYMBOL_REF_FLAGS (x) & SYMBOL_FLAG_XSTORMY16_BELOW100) != 0;
530
a21eaf5e 531 if (CONST_INT_P (x))
54e9a19d
DD
532 {
533 HOST_WIDE_INT i = INTVAL (x);
a21eaf5e 534
54e9a19d
DD
535 if ((i >= 0x0000 && i <= 0x00ff)
536 || (i >= 0x7f00 && i <= 0x7fff))
537 return 1;
538 }
539 return 0;
540}
541
54e9a19d
DD
542/* Likewise, but only for non-volatile MEMs, for patterns where the
543 MEM will get split into smaller sized accesses. */
5ab9749e 544
a6fef2e1 545bool
ef4bddc2 546xstormy16_splittable_below100_operand (rtx x, machine_mode mode)
54e9a19d 547{
a21eaf5e 548 if (MEM_P (x) && MEM_VOLATILE_P (x))
54e9a19d
DD
549 return 0;
550 return xstormy16_below100_operand (x, mode);
551}
552
54e9a19d
DD
553/* Expand an 8-bit IOR. This either detects the one case we can
554 actually do, or uses a 16-bit IOR. */
5ab9749e 555
54e9a19d
DD
556void
557xstormy16_expand_iorqi3 (rtx *operands)
558{
559 rtx in, out, outsub, val;
560
561 out = operands[0];
562 in = operands[1];
563 val = operands[2];
564
565 if (xstormy16_onebit_set_operand (val, QImode))
566 {
567 if (!xstormy16_below100_or_register (in, QImode))
568 in = copy_to_mode_reg (QImode, in);
569 if (!xstormy16_below100_or_register (out, QImode))
570 out = gen_reg_rtx (QImode);
571 emit_insn (gen_iorqi3_internal (out, in, val));
572 if (out != operands[0])
573 emit_move_insn (operands[0], out);
574 return;
575 }
576
a21eaf5e 577 if (! REG_P (in))
54e9a19d 578 in = copy_to_mode_reg (QImode, in);
a21eaf5e
NC
579
580 if (! REG_P (val) && ! CONST_INT_P (val))
54e9a19d 581 val = copy_to_mode_reg (QImode, val);
a21eaf5e
NC
582
583 if (! REG_P (out))
54e9a19d
DD
584 out = gen_reg_rtx (QImode);
585
586 in = simplify_gen_subreg (HImode, in, QImode, 0);
587 outsub = simplify_gen_subreg (HImode, out, QImode, 0);
a21eaf5e
NC
588
589 if (! CONST_INT_P (val))
54e9a19d
DD
590 val = simplify_gen_subreg (HImode, val, QImode, 0);
591
592 emit_insn (gen_iorhi3 (outsub, in, val));
593
594 if (out != operands[0])
595 emit_move_insn (operands[0], out);
596}
597
5ab9749e
NC
598/* Expand an 8-bit AND. This either detects the one case we can
599 actually do, or uses a 16-bit AND. */
600
54e9a19d
DD
601void
602xstormy16_expand_andqi3 (rtx *operands)
603{
604 rtx in, out, outsub, val;
605
606 out = operands[0];
607 in = operands[1];
608 val = operands[2];
609
610 if (xstormy16_onebit_clr_operand (val, QImode))
611 {
612 if (!xstormy16_below100_or_register (in, QImode))
613 in = copy_to_mode_reg (QImode, in);
614 if (!xstormy16_below100_or_register (out, QImode))
615 out = gen_reg_rtx (QImode);
616 emit_insn (gen_andqi3_internal (out, in, val));
617 if (out != operands[0])
618 emit_move_insn (operands[0], out);
619 return;
620 }
621
a21eaf5e 622 if (! REG_P (in))
54e9a19d 623 in = copy_to_mode_reg (QImode, in);
a21eaf5e
NC
624
625 if (! REG_P (val) && ! CONST_INT_P (val))
54e9a19d 626 val = copy_to_mode_reg (QImode, val);
a21eaf5e
NC
627
628 if (! REG_P (out))
54e9a19d
DD
629 out = gen_reg_rtx (QImode);
630
631 in = simplify_gen_subreg (HImode, in, QImode, 0);
632 outsub = simplify_gen_subreg (HImode, out, QImode, 0);
a21eaf5e
NC
633
634 if (! CONST_INT_P (val))
54e9a19d
DD
635 val = simplify_gen_subreg (HImode, val, QImode, 0);
636
637 emit_insn (gen_andhi3 (outsub, in, val));
638
639 if (out != operands[0])
640 emit_move_insn (operands[0], out);
641}
642
4b58290f 643#define LEGITIMATE_ADDRESS_INTEGER_P(X, OFFSET) \
a21eaf5e 644 (CONST_INT_P (X) \
4b58290f
GK
645 && (unsigned HOST_WIDE_INT) (INTVAL (X) + (OFFSET) + 2048) < 4096)
646
647#define LEGITIMATE_ADDRESS_CONST_INT_P(X, OFFSET) \
a21eaf5e 648 (CONST_INT_P (X) \
4b58290f
GK
649 && INTVAL (X) + (OFFSET) >= 0 \
650 && INTVAL (X) + (OFFSET) < 0x8000 \
651 && (INTVAL (X) + (OFFSET) < 0x100 || INTVAL (X) + (OFFSET) >= 0x7F00))
652
d634083b 653bool
ef4bddc2 654xstormy16_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
c6c3dba9 655 rtx x, bool strict)
4b58290f
GK
656{
657 if (LEGITIMATE_ADDRESS_CONST_INT_P (x, 0))
a21eaf5e 658 return true;
4b58290f
GK
659
660 if (GET_CODE (x) == PLUS
661 && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 0))
813ab1d7
NC
662 {
663 x = XEXP (x, 0);
664 /* PR 31232: Do not allow INT+INT as an address. */
a21eaf5e
NC
665 if (CONST_INT_P (x))
666 return false;
813ab1d7 667 }
5ab9749e 668
a21eaf5e 669 if ((GET_CODE (x) == PRE_MODIFY && CONST_INT_P (XEXP (XEXP (x, 1), 1)))
f3922fd2 670 || GET_CODE (x) == POST_INC
4b58290f
GK
671 || GET_CODE (x) == PRE_DEC)
672 x = XEXP (x, 0);
5ab9749e 673
a21eaf5e
NC
674 if (REG_P (x)
675 && REGNO_OK_FOR_BASE_P (REGNO (x))
4b58290f 676 && (! strict || REGNO (x) < FIRST_PSEUDO_REGISTER))
a21eaf5e 677 return true;
54e9a19d 678
f84fe9b6 679 if (xstormy16_below100_symbol (x, mode))
a21eaf5e 680 return true;
5ab9749e 681
a21eaf5e 682 return false;
4b58290f
GK
683}
684
192997cf 685/* Worker function for TARGET_MODE_DEPENDENT_ADDRESS_P.
5ab9749e 686
4b58290f
GK
687 On this chip, this is true if the address is valid with an offset
688 of 0 but not of 6, because in that case it cannot be used as an
689 address for DImode or DFmode, or if the address is a post-increment
690 or pre-decrement address. */
5ab9749e 691
192997cf 692static bool
5bfed9a9
GJL
693xstormy16_mode_dependent_address_p (const_rtx x,
694 addr_space_t as ATTRIBUTE_UNUSED)
4b58290f
GK
695{
696 if (LEGITIMATE_ADDRESS_CONST_INT_P (x, 0)
697 && ! LEGITIMATE_ADDRESS_CONST_INT_P (x, 6))
192997cf 698 return true;
5ab9749e 699
4b58290f
GK
700 if (GET_CODE (x) == PLUS
701 && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 0)
702 && ! LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 6))
192997cf 703 return true;
4b58290f 704
e53b6e56 705 /* Auto-increment addresses are now treated generically in recog.cc. */
192997cf 706 return false;
4b58290f
GK
707}
708
4b58290f 709int
ef4bddc2 710short_memory_operand (rtx x, machine_mode mode)
4b58290f
GK
711{
712 if (! memory_operand (x, mode))
713 return 0;
714 return (GET_CODE (XEXP (x, 0)) != PLUS);
715}
716
fae778eb 717/* Splitter for the 'move' patterns, for modes not directly implemented
4b58290f
GK
718 by hardware. Emit insns to copy a value of mode MODE from SRC to
719 DEST.
720
5ab9749e 721 This function is only called when reload_completed. */
4b58290f 722
5ab9749e 723void
ef4bddc2 724xstormy16_split_move (machine_mode mode, rtx dest, rtx src)
4b58290f
GK
725{
726 int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
727 int direction, end, i;
728 int src_modifies = 0;
729 int dest_modifies = 0;
730 int src_volatile = 0;
731 int dest_volatile = 0;
732 rtx mem_operand;
7c87e9f9 733 rtx auto_inc_reg_rtx = NULL_RTX;
5ab9749e 734
4b58290f 735 /* Check initial conditions. */
4718bfd8
NS
736 gcc_assert (reload_completed
737 && mode != QImode && mode != HImode
738 && nonimmediate_operand (dest, mode)
739 && general_operand (src, mode));
4b58290f
GK
740
741 /* This case is not supported below, and shouldn't be generated. */
a21eaf5e 742 gcc_assert (! MEM_P (dest) || ! MEM_P (src));
4b58290f
GK
743
744 /* This case is very very bad after reload, so trap it now. */
4718bfd8 745 gcc_assert (GET_CODE (dest) != SUBREG && GET_CODE (src) != SUBREG);
4b58290f
GK
746
747 /* The general idea is to copy by words, offsetting the source and
748 destination. Normally the least-significant word will be copied
5ab9749e 749 first, but for pre-dec operations it's better to copy the
4b58290f 750 most-significant word first. Only one operand can be a pre-dec
5ab9749e 751 or post-inc operand.
4b58290f
GK
752
753 It's also possible that the copy overlaps so that the direction
754 must be reversed. */
755 direction = 1;
5ab9749e 756
a21eaf5e 757 if (MEM_P (dest))
4b58290f
GK
758 {
759 mem_operand = XEXP (dest, 0);
760 dest_modifies = side_effects_p (mem_operand);
7c87e9f9
CM
761 if (auto_inc_p (mem_operand))
762 auto_inc_reg_rtx = XEXP (mem_operand, 0);
4b58290f
GK
763 dest_volatile = MEM_VOLATILE_P (dest);
764 if (dest_volatile)
765 {
766 dest = copy_rtx (dest);
767 MEM_VOLATILE_P (dest) = 0;
768 }
769 }
a21eaf5e 770 else if (MEM_P (src))
4b58290f
GK
771 {
772 mem_operand = XEXP (src, 0);
773 src_modifies = side_effects_p (mem_operand);
7c87e9f9
CM
774 if (auto_inc_p (mem_operand))
775 auto_inc_reg_rtx = XEXP (mem_operand, 0);
4b58290f
GK
776 src_volatile = MEM_VOLATILE_P (src);
777 if (src_volatile)
778 {
779 src = copy_rtx (src);
780 MEM_VOLATILE_P (src) = 0;
781 }
782 }
783 else
784 mem_operand = NULL_RTX;
785
786 if (mem_operand == NULL_RTX)
787 {
a21eaf5e
NC
788 if (REG_P (src)
789 && REG_P (dest)
4b58290f
GK
790 && reg_overlap_mentioned_p (dest, src)
791 && REGNO (dest) > REGNO (src))
792 direction = -1;
793 }
794 else if (GET_CODE (mem_operand) == PRE_DEC
5ab9749e 795 || (GET_CODE (mem_operand) == PLUS
4b58290f
GK
796 && GET_CODE (XEXP (mem_operand, 0)) == PRE_DEC))
797 direction = -1;
a21eaf5e 798 else if (MEM_P (src) && reg_overlap_mentioned_p (dest, src))
4b58290f
GK
799 {
800 int regno;
5ab9749e 801
a21eaf5e 802 gcc_assert (REG_P (dest));
4b58290f 803 regno = REGNO (dest);
5ab9749e 804
4718bfd8
NS
805 gcc_assert (refers_to_regno_p (regno, regno + num_words,
806 mem_operand, 0));
5ab9749e 807
c9bd6bcd 808 if (refers_to_regno_p (regno, mem_operand))
4b58290f
GK
809 direction = -1;
810 else if (refers_to_regno_p (regno + num_words - 1, regno + num_words,
811 mem_operand, 0))
812 direction = 1;
813 else
814 /* This means something like
815 (set (reg:DI r0) (mem:DI (reg:HI r1)))
816 which we'd need to support by doing the set of the second word
817 last. */
4718bfd8 818 gcc_unreachable ();
4b58290f
GK
819 }
820
821 end = direction < 0 ? -1 : num_words;
822 for (i = direction < 0 ? num_words - 1 : 0; i != end; i += direction)
823 {
7c87e9f9
CM
824 rtx w_src, w_dest, insn;
825
4b58290f
GK
826 if (src_modifies)
827 w_src = gen_rtx_MEM (word_mode, mem_operand);
828 else
829 w_src = simplify_gen_subreg (word_mode, src, mode, i * UNITS_PER_WORD);
830 if (src_volatile)
831 MEM_VOLATILE_P (w_src) = 1;
832 if (dest_modifies)
833 w_dest = gen_rtx_MEM (word_mode, mem_operand);
834 else
5ab9749e 835 w_dest = simplify_gen_subreg (word_mode, dest, mode,
4b58290f
GK
836 i * UNITS_PER_WORD);
837 if (dest_volatile)
838 MEM_VOLATILE_P (w_dest) = 1;
5ab9749e 839
4b58290f 840 /* The simplify_subreg calls must always be able to simplify. */
4718bfd8
NS
841 gcc_assert (GET_CODE (w_src) != SUBREG
842 && GET_CODE (w_dest) != SUBREG);
5ab9749e 843
f7df4a84 844 insn = emit_insn (gen_rtx_SET (w_dest, w_src));
7c87e9f9
CM
845 if (auto_inc_reg_rtx)
846 REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
847 auto_inc_reg_rtx,
848 REG_NOTES (insn));
4b58290f
GK
849 }
850}
851
852/* Expander for the 'move' patterns. Emit insns to copy a value of
853 mode MODE from SRC to DEST. */
854
5ab9749e 855void
ef4bddc2 856xstormy16_expand_move (machine_mode mode, rtx dest, rtx src)
4b58290f 857{
a21eaf5e 858 if (MEM_P (dest) && (GET_CODE (XEXP (dest, 0)) == PRE_MODIFY))
f3922fd2
DD
859 {
860 rtx pmv = XEXP (dest, 0);
861 rtx dest_reg = XEXP (pmv, 0);
862 rtx dest_mod = XEXP (pmv, 1);
f7df4a84 863 rtx set = gen_rtx_SET (dest_reg, dest_mod);
b72bbbcb 864 rtx clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (BImode, CARRY_REGNUM));
5ab9749e 865
f3922fd2
DD
866 dest = gen_rtx_MEM (mode, dest_reg);
867 emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber)));
868 }
a21eaf5e 869 else if (MEM_P (src) && (GET_CODE (XEXP (src, 0)) == PRE_MODIFY))
f3922fd2
DD
870 {
871 rtx pmv = XEXP (src, 0);
872 rtx src_reg = XEXP (pmv, 0);
873 rtx src_mod = XEXP (pmv, 1);
f7df4a84 874 rtx set = gen_rtx_SET (src_reg, src_mod);
b72bbbcb 875 rtx clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (BImode, CARRY_REGNUM));
5ab9749e 876
f3922fd2
DD
877 src = gen_rtx_MEM (mode, src_reg);
878 emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber)));
879 }
5ab9749e 880
4b58290f
GK
881 /* There are only limited immediate-to-memory move instructions. */
882 if (! reload_in_progress
883 && ! reload_completed
a21eaf5e
NC
884 && MEM_P (dest)
885 && (! CONST_INT_P (XEXP (dest, 0))
c6243b4c 886 || ! xstormy16_legitimate_address_p (mode, XEXP (dest, 0), 0))
54e9a19d 887 && ! xstormy16_below100_operand (dest, mode)
a21eaf5e 888 && ! REG_P (src)
4b58290f
GK
889 && GET_CODE (src) != SUBREG)
890 src = copy_to_mode_reg (mode, src);
891
892 /* Don't emit something we would immediately split. */
893 if (reload_completed
894 && mode != HImode && mode != QImode)
895 {
c6243b4c 896 xstormy16_split_move (mode, dest, src);
4b58290f
GK
897 return;
898 }
5ab9749e 899
f7df4a84 900 emit_insn (gen_rtx_SET (dest, src));
4b58290f 901}
4b58290f
GK
902\f
903/* Stack Layout:
904
905 The stack is laid out as follows:
906
907SP->
908FP-> Local variables
909 Register save area (up to 4 words)
910 Argument register save area for stdarg (NUM_ARGUMENT_REGISTERS words)
911
912AP-> Return address (two words)
913 9th procedure parameter word
914 10th procedure parameter word
915 ...
916 last procedure parameter word
917
918 The frame pointer location is tuned to make it most likely that all
919 parameters and local variables can be accessed using a load-indexed
920 instruction. */
921
922/* A structure to describe the layout. */
c6243b4c 923struct xstormy16_stack_layout
4b58290f
GK
924{
925 /* Size of the topmost three items on the stack. */
926 int locals_size;
927 int register_save_size;
928 int stdarg_save_size;
929 /* Sum of the above items. */
930 int frame_size;
931 /* Various offsets. */
932 int first_local_minus_ap;
933 int sp_minus_fp;
934 int fp_minus_ap;
935};
936
937/* Does REGNO need to be saved? */
938#define REG_NEEDS_SAVE(REGNUM, IFUN) \
a365fa06
RS
939 ((df_regs_ever_live_p (REGNUM) && !call_used_or_fixed_reg_p (REGNUM)) \
940 || (IFUN && !fixed_regs[REGNUM] && call_used_or_fixed_reg_p (REGNUM) \
d40ba0b6 941 && (REGNUM != CARRY_REGNUM) \
416ff32e 942 && (df_regs_ever_live_p (REGNUM) || ! crtl->is_leaf)))
4b58290f
GK
943
944/* Compute the stack layout. */
5ab9749e
NC
945
946struct xstormy16_stack_layout
51c16b7e 947xstormy16_compute_stack_layout (void)
4b58290f 948{
c6243b4c 949 struct xstormy16_stack_layout layout;
4b58290f 950 int regno;
c6243b4c 951 const int ifun = xstormy16_interrupt_function_p ();
4b58290f
GK
952
953 layout.locals_size = get_frame_size ();
5ab9749e 954
4b58290f
GK
955 layout.register_save_size = 0;
956 for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
957 if (REG_NEEDS_SAVE (regno, ifun))
958 layout.register_save_size += UNITS_PER_WORD;
5ab9749e 959
e3b5732b 960 if (cfun->stdarg)
4b58290f
GK
961 layout.stdarg_save_size = NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD;
962 else
963 layout.stdarg_save_size = 0;
5ab9749e
NC
964
965 layout.frame_size = (layout.locals_size
966 + layout.register_save_size
4b58290f 967 + layout.stdarg_save_size);
5ab9749e 968
38173d38 969 if (crtl->args.size <= 2048 && crtl->args.size != -1)
4b58290f 970 {
5ab9749e 971 if (layout.frame_size - INCOMING_FRAME_SP_OFFSET
38173d38 972 + crtl->args.size <= 2048)
b72bbbcb 973 layout.fp_minus_ap = layout.frame_size - INCOMING_FRAME_SP_OFFSET;
4b58290f 974 else
38173d38 975 layout.fp_minus_ap = 2048 - crtl->args.size;
4b58290f
GK
976 }
977 else
5ab9749e 978 layout.fp_minus_ap = (layout.stdarg_save_size
4b58290f 979 + layout.register_save_size
b72bbbcb 980 - INCOMING_FRAME_SP_OFFSET);
5ab9749e 981 layout.sp_minus_fp = (layout.frame_size - INCOMING_FRAME_SP_OFFSET
4b58290f
GK
982 - layout.fp_minus_ap);
983 layout.first_local_minus_ap = layout.sp_minus_fp - layout.locals_size;
984 return layout;
985}
986
7b5cbb57
AS
987/* Worker function for TARGET_CAN_ELIMINATE. */
988
989static bool
990xstormy16_can_eliminate (const int from, const int to)
991{
992 return (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM
993 ? ! frame_pointer_needed
994 : true);
995}
996
4b58290f 997/* Determine how all the special registers get eliminated. */
5ab9749e 998
4b58290f 999int
51c16b7e 1000xstormy16_initial_elimination_offset (int from, int to)
4b58290f 1001{
c6243b4c 1002 struct xstormy16_stack_layout layout;
4b58290f 1003 int result;
5ab9749e 1004
c6243b4c 1005 layout = xstormy16_compute_stack_layout ();
4b58290f
GK
1006
1007 if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
1008 result = layout.sp_minus_fp - layout.locals_size;
1009 else if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
b72bbbcb 1010 result = - layout.locals_size;
4b58290f 1011 else if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
b72bbbcb 1012 result = - layout.fp_minus_ap;
4b58290f 1013 else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
b72bbbcb 1014 result = - (layout.sp_minus_fp + layout.fp_minus_ap);
4b58290f 1015 else
4718bfd8 1016 gcc_unreachable ();
4b58290f
GK
1017
1018 return result;
1019}
1020
1021static rtx
51c16b7e 1022emit_addhi3_postreload (rtx dest, rtx src0, rtx src1)
4b58290f
GK
1023{
1024 rtx set, clobber, insn;
5ab9749e 1025
f7df4a84 1026 set = gen_rtx_SET (dest, gen_rtx_PLUS (HImode, src0, src1));
b72bbbcb 1027 clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (BImode, CARRY_REGNUM));
4b58290f
GK
1028 insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber)));
1029 return insn;
1030}
1031
41441dc7
NB
1032/* Called after register allocation to add any instructions needed for
1033 the prologue. Using a prologue insn is favored compared to putting
1034 all of the instructions in the TARGET_ASM_FUNCTION_PROLOGUE macro,
1035 since it allows the scheduler to intermix instructions with the
1036 saves of the caller saved registers. In some cases, it might be
1037 necessary to emit a barrier instruction as the last insn to prevent
1038 such scheduling.
4b58290f
GK
1039
1040 Also any insns generated here should have RTX_FRAME_RELATED_P(insn) = 1
1041 so that the debug info generation code can handle them properly. */
5ab9749e 1042
4b58290f 1043void
51c16b7e 1044xstormy16_expand_prologue (void)
4b58290f 1045{
c6243b4c 1046 struct xstormy16_stack_layout layout;
4b58290f
GK
1047 int regno;
1048 rtx insn;
1049 rtx mem_push_rtx;
c6243b4c 1050 const int ifun = xstormy16_interrupt_function_p ();
5ab9749e 1051
4b58290f
GK
1052 mem_push_rtx = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
1053 mem_push_rtx = gen_rtx_MEM (HImode, mem_push_rtx);
5ab9749e 1054
c6243b4c 1055 layout = xstormy16_compute_stack_layout ();
4b58290f 1056
f3922fd2 1057 if (layout.locals_size >= 32768)
ab532386 1058 error ("local variable memory requirements exceed capacity");
f3922fd2 1059
315bdf71
NC
1060 if (flag_stack_usage_info)
1061 current_function_static_stack_size = layout.frame_size;
1062
4b58290f
GK
1063 /* Save the argument registers if necessary. */
1064 if (layout.stdarg_save_size)
5ab9749e 1065 for (regno = FIRST_ARGUMENT_REGISTER;
4b58290f
GK
1066 regno < FIRST_ARGUMENT_REGISTER + NUM_ARGUMENT_REGISTERS;
1067 regno++)
1068 {
f3922fd2 1069 rtx dwarf;
4b58290f 1070 rtx reg = gen_rtx_REG (HImode, regno);
f3922fd2 1071
4b58290f
GK
1072 insn = emit_move_insn (mem_push_rtx, reg);
1073 RTX_FRAME_RELATED_P (insn) = 1;
f3922fd2
DD
1074
1075 dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (2));
5ab9749e 1076
f7df4a84 1077 XVECEXP (dwarf, 0, 0) = gen_rtx_SET (gen_rtx_MEM (Pmode, stack_pointer_rtx),
f3922fd2 1078 reg);
f7df4a84 1079 XVECEXP (dwarf, 0, 1) = gen_rtx_SET (stack_pointer_rtx,
0a81f074
RS
1080 plus_constant (Pmode,
1081 stack_pointer_rtx,
f3922fd2 1082 GET_MODE_SIZE (Pmode)));
f1cb6795 1083 add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf);
f3922fd2
DD
1084 RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 0)) = 1;
1085 RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 1)) = 1;
4b58290f 1086 }
5ab9749e 1087
4b58290f
GK
1088 /* Push each of the registers to save. */
1089 for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
1090 if (REG_NEEDS_SAVE (regno, ifun))
1091 {
f3922fd2 1092 rtx dwarf;
4b58290f 1093 rtx reg = gen_rtx_REG (HImode, regno);
f3922fd2 1094
4b58290f
GK
1095 insn = emit_move_insn (mem_push_rtx, reg);
1096 RTX_FRAME_RELATED_P (insn) = 1;
f3922fd2
DD
1097
1098 dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (2));
5ab9749e 1099
f7df4a84 1100 XVECEXP (dwarf, 0, 0) = gen_rtx_SET (gen_rtx_MEM (Pmode, stack_pointer_rtx),
f3922fd2 1101 reg);
f7df4a84 1102 XVECEXP (dwarf, 0, 1) = gen_rtx_SET (stack_pointer_rtx,
a6178a25 1103 plus_constant (Pmode,
0a81f074 1104 stack_pointer_rtx,
f3922fd2 1105 GET_MODE_SIZE (Pmode)));
f1cb6795 1106 add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf);
f3922fd2
DD
1107 RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 0)) = 1;
1108 RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 1)) = 1;
4b58290f
GK
1109 }
1110
1111 /* It's just possible that the SP here might be what we need for
b1c9bc51 1112 the new FP... */
4b58290f 1113 if (frame_pointer_needed && layout.sp_minus_fp == layout.locals_size)
b72bbbcb
DD
1114 {
1115 insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
1116 RTX_FRAME_RELATED_P (insn) = 1;
1117 }
4b58290f
GK
1118
1119 /* Allocate space for local variables. */
1120 if (layout.locals_size)
1121 {
1122 insn = emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx,
1123 GEN_INT (layout.locals_size));
1124 RTX_FRAME_RELATED_P (insn) = 1;
1125 }
1126
1127 /* Set up the frame pointer, if required. */
1128 if (frame_pointer_needed && layout.sp_minus_fp != layout.locals_size)
1129 {
1130 insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
b72bbbcb 1131 RTX_FRAME_RELATED_P (insn) = 1;
6208b55d 1132
4b58290f 1133 if (layout.sp_minus_fp)
b72bbbcb
DD
1134 {
1135 insn = emit_addhi3_postreload (hard_frame_pointer_rtx,
1136 hard_frame_pointer_rtx,
1137 GEN_INT (- layout.sp_minus_fp));
1138 RTX_FRAME_RELATED_P (insn) = 1;
1139 }
4b58290f
GK
1140 }
1141}
1142
1143/* Do we need an epilogue at all? */
5ab9749e 1144
4b58290f 1145int
51c16b7e 1146direct_return (void)
4b58290f 1147{
5ab9749e 1148 return (reload_completed
2fd7ba4a
NC
1149 && xstormy16_compute_stack_layout ().frame_size == 0
1150 && ! xstormy16_interrupt_function_p ());
4b58290f
GK
1151}
1152
41441dc7 1153/* Called after register allocation to add any instructions needed for
e03f5d43 1154 the epilogue. Using an epilogue insn is favored compared to putting
41441dc7
NB
1155 all of the instructions in the TARGET_ASM_FUNCTION_PROLOGUE macro,
1156 since it allows the scheduler to intermix instructions with the
1157 saves of the caller saved registers. In some cases, it might be
1158 necessary to emit a barrier instruction as the last insn to prevent
1159 such scheduling. */
4b58290f
GK
1160
1161void
51c16b7e 1162xstormy16_expand_epilogue (void)
4b58290f 1163{
c6243b4c 1164 struct xstormy16_stack_layout layout;
a21eaf5e 1165 rtx mem_pop_rtx;
4b58290f 1166 int regno;
c6243b4c 1167 const int ifun = xstormy16_interrupt_function_p ();
5ab9749e 1168
4b58290f
GK
1169 mem_pop_rtx = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
1170 mem_pop_rtx = gen_rtx_MEM (HImode, mem_pop_rtx);
5ab9749e 1171
c6243b4c 1172 layout = xstormy16_compute_stack_layout ();
4b58290f
GK
1173
1174 /* Pop the stack for the locals. */
1175 if (layout.locals_size)
e2470e1b
GK
1176 {
1177 if (frame_pointer_needed && layout.sp_minus_fp == layout.locals_size)
1178 emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
1179 else
b72bbbcb
DD
1180 emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx,
1181 GEN_INT (- layout.locals_size));
e2470e1b 1182 }
4b58290f
GK
1183
1184 /* Restore any call-saved registers. */
1185 for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno--)
1186 if (REG_NEEDS_SAVE (regno, ifun))
b72bbbcb 1187 emit_move_insn (gen_rtx_REG (HImode, regno), mem_pop_rtx);
5ab9749e 1188
4b58290f
GK
1189 /* Pop the stack for the stdarg save area. */
1190 if (layout.stdarg_save_size)
b72bbbcb
DD
1191 emit_addhi3_postreload (stack_pointer_rtx, stack_pointer_rtx,
1192 GEN_INT (- layout.stdarg_save_size));
4b58290f
GK
1193
1194 /* Return. */
1195 if (ifun)
1196 emit_jump_insn (gen_return_internal_interrupt ());
1197 else
1198 emit_jump_insn (gen_return_internal ());
1199}
1200
1201int
51c16b7e 1202xstormy16_epilogue_uses (int regno)
4b58290f 1203{
a365fa06 1204 if (reload_completed && call_used_or_fixed_reg_p (regno))
4b58290f 1205 {
c6243b4c 1206 const int ifun = xstormy16_interrupt_function_p ();
4b58290f
GK
1207 return REG_NEEDS_SAVE (regno, ifun);
1208 }
1209 return 0;
1210}
14b56832
DD
1211
1212void
51c16b7e 1213xstormy16_function_profiler (void)
14b56832 1214{
58385f6a 1215 sorry ("%<function_profiler%> support");
14b56832 1216}
4b58290f 1217\f
6930c98c
RS
1218/* Update CUM to advance past argument ARG. Once this is done,
1219 the variable CUM is suitable for analyzing the *following*
bf425ddd 1220 argument with `TARGET_FUNCTION_ARG', etc.
4b58290f
GK
1221
1222 This function need not do anything if the argument in question was
1223 passed on the stack. The compiler knows how to track the amount of
1224 stack space used for arguments without any special help. However,
c6243b4c 1225 it makes life easier for xstormy16_build_va_list if it does update
4b58290f 1226 the word count. */
5ab9749e 1227
bf425ddd 1228static void
6930c98c
RS
1229xstormy16_function_arg_advance (cumulative_args_t cum_v,
1230 const function_arg_info &arg)
4b58290f 1231{
d5cc9181
JR
1232 CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
1233
4b58290f
GK
1234 /* If an argument would otherwise be passed partially in registers,
1235 and partially on the stack, the whole of it is passed on the
1236 stack. */
bf425ddd 1237 if (*cum < NUM_ARGUMENT_REGISTERS
6930c98c
RS
1238 && (*cum + XSTORMY16_WORD_SIZE (arg.type, arg.mode)
1239 > NUM_ARGUMENT_REGISTERS))
bf425ddd 1240 *cum = NUM_ARGUMENT_REGISTERS;
5ab9749e 1241
6930c98c 1242 *cum += XSTORMY16_WORD_SIZE (arg.type, arg.mode);
4b58290f
GK
1243}
1244
bf425ddd 1245static rtx
6783fdb7 1246xstormy16_function_arg (cumulative_args_t cum_v, const function_arg_info &arg)
06d22853 1247{
d5cc9181
JR
1248 CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
1249
6783fdb7 1250 if (arg.end_marker_p ())
06d22853 1251 return const0_rtx;
0ffef200 1252 if (targetm.calls.must_pass_in_stack (arg)
6783fdb7
RS
1253 || (*cum + XSTORMY16_WORD_SIZE (arg.type, arg.mode)
1254 > NUM_ARGUMENT_REGISTERS))
5ab9749e 1255 return NULL_RTX;
6783fdb7 1256 return gen_rtx_REG (arg.mode, *cum + FIRST_ARGUMENT_REGISTER);
06d22853
DD
1257}
1258
4b58290f
GK
1259/* Build the va_list type.
1260
1261 For this chip, va_list is a record containing a counter and a pointer.
1262 The counter is of type 'int' and indicates how many bytes
1263 have been used to date. The pointer indicates the stack position
5ab9749e 1264 for arguments that have not been passed in registers.
4b58290f
GK
1265 To keep the layout nice, the pointer is first in the structure. */
1266
37cd4bca
NC
1267static tree
1268xstormy16_build_builtin_va_list (void)
4b58290f
GK
1269{
1270 tree f_1, f_2, record, type_decl;
1271
f1e639b1 1272 record = (*lang_hooks.types.make_type) (RECORD_TYPE);
4c4bde29
AH
1273 type_decl = build_decl (BUILTINS_LOCATION,
1274 TYPE_DECL, get_identifier ("__va_list_tag"), record);
4b58290f 1275
4c4bde29
AH
1276 f_1 = build_decl (BUILTINS_LOCATION,
1277 FIELD_DECL, get_identifier ("base"),
4b58290f 1278 ptr_type_node);
4c4bde29
AH
1279 f_2 = build_decl (BUILTINS_LOCATION,
1280 FIELD_DECL, get_identifier ("count"),
4b58290f
GK
1281 unsigned_type_node);
1282
1283 DECL_FIELD_CONTEXT (f_1) = record;
1284 DECL_FIELD_CONTEXT (f_2) = record;
1285
0fd2eac2 1286 TYPE_STUB_DECL (record) = type_decl;
4b58290f
GK
1287 TYPE_NAME (record) = type_decl;
1288 TYPE_FIELDS (record) = f_1;
910ad8de 1289 DECL_CHAIN (f_1) = f_2;
4b58290f
GK
1290
1291 layout_type (record);
1292
1293 return record;
1294}
1295
5e7a8ee0 1296/* Implement the stdarg/varargs va_start macro. STDARG_P is nonzero if this
4b58290f
GK
1297 is stdarg.h instead of varargs.h. VALIST is the tree of the va_list
1298 variable to initialize. NEXTARG is the machine independent notion of the
1299 'next' argument after the variable arguments. */
5ab9749e 1300
d7bd8aeb 1301static void
51c16b7e 1302xstormy16_expand_builtin_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
4b58290f
GK
1303{
1304 tree f_base, f_count;
1305 tree base, count;
f84fe9b6 1306 tree t,u;
4b58290f 1307
c6243b4c 1308 if (xstormy16_interrupt_function_p ())
58385f6a 1309 error ("cannot use %<va_start%> in interrupt function");
5ab9749e 1310
4b58290f 1311 f_base = TYPE_FIELDS (va_list_type_node);
910ad8de 1312 f_count = DECL_CHAIN (f_base);
5ab9749e 1313
47a25a46
RG
1314 base = build3 (COMPONENT_REF, TREE_TYPE (f_base), valist, f_base, NULL_TREE);
1315 count = build3 (COMPONENT_REF, TREE_TYPE (f_count), valist, f_count,
1316 NULL_TREE);
4b58290f
GK
1317
1318 t = make_tree (TREE_TYPE (base), virtual_incoming_args_rtx);
b72bbbcb 1319 u = build_int_cst (NULL_TREE, - INCOMING_FRAME_SP_OFFSET);
f84fe9b6 1320 u = fold_convert (TREE_TYPE (count), u);
5d49b6a7 1321 t = fold_build_pointer_plus (t, u);
726a989a 1322 t = build2 (MODIFY_EXPR, TREE_TYPE (base), base, t);
4b58290f
GK
1323 TREE_SIDE_EFFECTS (t) = 1;
1324 expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1325
5ab9749e 1326 t = build2 (MODIFY_EXPR, TREE_TYPE (count), count,
47a25a46 1327 build_int_cst (NULL_TREE,
38173d38 1328 crtl->args.info * UNITS_PER_WORD));
4b58290f
GK
1329 TREE_SIDE_EFFECTS (t) = 1;
1330 expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
1331}
1332
1333/* Implement the stdarg/varargs va_arg macro. VALIST is the variable
cf4c092e
CM
1334 of type va_list as a tree, TYPE is the type passed to va_arg.
1335 Note: This algorithm is documented in stormy-abi. */
5ab9749e 1336
5d47df87 1337static tree
726a989a
RB
1338xstormy16_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
1339 gimple_seq *post_p ATTRIBUTE_UNUSED)
4b58290f
GK
1340{
1341 tree f_base, f_count;
1342 tree base, count;
5d47df87
RH
1343 tree count_tmp, addr, t;
1344 tree lab_gotaddr, lab_fromstack;
06d22853 1345 int size, size_of_reg_args, must_stack;
5d47df87
RH
1346 tree size_tree;
1347
4b58290f 1348 f_base = TYPE_FIELDS (va_list_type_node);
910ad8de 1349 f_count = DECL_CHAIN (f_base);
5ab9749e 1350
47a25a46
RG
1351 base = build3 (COMPONENT_REF, TREE_TYPE (f_base), valist, f_base, NULL_TREE);
1352 count = build3 (COMPONENT_REF, TREE_TYPE (f_count), valist, f_count,
1353 NULL_TREE);
4b58290f 1354
4f53599c 1355 must_stack = must_pass_va_arg_in_stack (type);
4b58290f 1356 size_tree = round_up (size_in_bytes (type), UNITS_PER_WORD);
5d47df87 1357 gimplify_expr (&size_tree, pre_p, NULL, is_gimple_val, fb_rvalue);
5ab9749e 1358
cf4c092e 1359 size_of_reg_args = NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD;
4b58290f 1360
5d47df87 1361 count_tmp = get_initialized_tmp_var (count, pre_p, NULL);
4c4bde29
AH
1362 lab_gotaddr = create_artificial_label (UNKNOWN_LOCATION);
1363 lab_fromstack = create_artificial_label (UNKNOWN_LOCATION);
9b489f31 1364 addr = create_tmp_var (ptr_type_node);
cf4c092e 1365
06d22853
DD
1366 if (!must_stack)
1367 {
5d47df87
RH
1368 tree r;
1369
1370 t = fold_convert (TREE_TYPE (count), size_tree);
47a25a46 1371 t = build2 (PLUS_EXPR, TREE_TYPE (count), count_tmp, t);
5d47df87 1372 r = fold_convert (TREE_TYPE (count), size_int (size_of_reg_args));
47a25a46
RG
1373 t = build2 (GT_EXPR, boolean_type_node, t, r);
1374 t = build3 (COND_EXPR, void_type_node, t,
1375 build1 (GOTO_EXPR, void_type_node, lab_fromstack),
1376 NULL_TREE);
5d47df87 1377 gimplify_and_add (t, pre_p);
f84fe9b6 1378
5d49b6a7 1379 t = fold_build_pointer_plus (base, count_tmp);
726a989a 1380 gimplify_assign (addr, t, pre_p);
5d47df87 1381
47a25a46 1382 t = build1 (GOTO_EXPR, void_type_node, lab_gotaddr);
5d47df87
RH
1383 gimplify_and_add (t, pre_p);
1384
47a25a46 1385 t = build1 (LABEL_EXPR, void_type_node, lab_fromstack);
5d47df87 1386 gimplify_and_add (t, pre_p);
06d22853 1387 }
5ab9749e 1388
4b58290f
GK
1389 /* Arguments larger than a word might need to skip over some
1390 registers, since arguments are either passed entirely in
1391 registers or entirely on the stack. */
06d22853
DD
1392 size = PUSH_ROUNDING (int_size_in_bytes (type));
1393 if (size > 2 || size < 0 || must_stack)
4b58290f 1394 {
5d47df87
RH
1395 tree r, u;
1396
1397 r = size_int (NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD);
726a989a 1398 u = build2 (MODIFY_EXPR, TREE_TYPE (count_tmp), count_tmp, r);
5d47df87
RH
1399
1400 t = fold_convert (TREE_TYPE (count), r);
47a25a46
RG
1401 t = build2 (GE_EXPR, boolean_type_node, count_tmp, t);
1402 t = build3 (COND_EXPR, void_type_node, t, NULL_TREE, u);
5d47df87 1403 gimplify_and_add (t, pre_p);
4b58290f
GK
1404 }
1405
5d47df87 1406 t = size_int (NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD
b72bbbcb 1407 + INCOMING_FRAME_SP_OFFSET);
5d47df87 1408 t = fold_convert (TREE_TYPE (count), t);
47a25a46
RG
1409 t = build2 (MINUS_EXPR, TREE_TYPE (count), count_tmp, t);
1410 t = build2 (PLUS_EXPR, TREE_TYPE (count), t,
1411 fold_convert (TREE_TYPE (count), size_tree));
f84fe9b6
NC
1412 t = fold_convert (TREE_TYPE (t), fold (t));
1413 t = fold_build1 (NEGATE_EXPR, TREE_TYPE (t), t);
5d49b6a7 1414 t = fold_build_pointer_plus (base, t);
726a989a 1415 gimplify_assign (addr, t, pre_p);
5d47df87 1416
47a25a46 1417 t = build1 (LABEL_EXPR, void_type_node, lab_gotaddr);
5d47df87 1418 gimplify_and_add (t, pre_p);
4b58290f 1419
5d47df87 1420 t = fold_convert (TREE_TYPE (count), size_tree);
47a25a46 1421 t = build2 (PLUS_EXPR, TREE_TYPE (count), count_tmp, t);
726a989a 1422 gimplify_assign (count, t, pre_p);
5ab9749e 1423
5d47df87 1424 addr = fold_convert (build_pointer_type (type), addr);
d6e9821f 1425 return build_va_arg_indirect_ref (addr);
4b58290f
GK
1426}
1427
fb8d0fac 1428/* Worker function for TARGET_TRAMPOLINE_INIT. */
5ab9749e 1429
fb8d0fac
RH
1430static void
1431xstormy16_trampoline_init (rtx m_tramp, tree fndecl, rtx static_chain)
4b58290f 1432{
4b58290f
GK
1433 rtx temp = gen_reg_rtx (HImode);
1434 rtx reg_fnaddr = gen_reg_rtx (HImode);
fb8d0fac 1435 rtx reg_addr, reg_addr_mem;
4b58290f 1436
fb8d0fac
RH
1437 reg_addr = copy_to_reg (XEXP (m_tramp, 0));
1438 reg_addr_mem = adjust_automodify_address (m_tramp, HImode, reg_addr, 0);
5ab9749e 1439
4b58290f
GK
1440 emit_move_insn (temp, GEN_INT (0x3130 | STATIC_CHAIN_REGNUM));
1441 emit_move_insn (reg_addr_mem, temp);
e2470e1b 1442 emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx));
fb8d0fac
RH
1443 reg_addr_mem = adjust_automodify_address (reg_addr_mem, VOIDmode, NULL, 2);
1444
4b58290f
GK
1445 emit_move_insn (temp, static_chain);
1446 emit_move_insn (reg_addr_mem, temp);
e2470e1b 1447 emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx));
fb8d0fac
RH
1448 reg_addr_mem = adjust_automodify_address (reg_addr_mem, VOIDmode, NULL, 2);
1449
1450 emit_move_insn (reg_fnaddr, XEXP (DECL_RTL (fndecl), 0));
4b58290f
GK
1451 emit_move_insn (temp, reg_fnaddr);
1452 emit_insn (gen_andhi3 (temp, temp, GEN_INT (0xFF)));
1453 emit_insn (gen_iorhi3 (temp, temp, GEN_INT (0x0200)));
1454 emit_move_insn (reg_addr_mem, temp);
e2470e1b 1455 emit_insn (gen_addhi3 (reg_addr, reg_addr, const2_rtx));
fb8d0fac
RH
1456 reg_addr_mem = adjust_automodify_address (reg_addr_mem, VOIDmode, NULL, 2);
1457
4b58290f
GK
1458 emit_insn (gen_lshrhi3 (reg_fnaddr, reg_fnaddr, GEN_INT (8)));
1459 emit_move_insn (reg_addr_mem, reg_fnaddr);
1460}
1461
998871e9 1462/* Worker function for TARGET_FUNCTION_VALUE. */
bd5bd7ac 1463
998871e9
AS
1464static rtx
1465xstormy16_function_value (const_tree valtype,
1466 const_tree func ATTRIBUTE_UNUSED,
1467 bool outgoing ATTRIBUTE_UNUSED)
4b58290f 1468{
ef4bddc2 1469 machine_mode mode;
4b58290f
GK
1470 mode = TYPE_MODE (valtype);
1471 PROMOTE_MODE (mode, 0, valtype);
1472 return gen_rtx_REG (mode, RETURN_VALUE_REGNUM);
1473}
1474
998871e9
AS
1475/* Worker function for TARGET_LIBCALL_VALUE. */
1476
1477static rtx
ef4bddc2 1478xstormy16_libcall_value (machine_mode mode,
998871e9
AS
1479 const_rtx fun ATTRIBUTE_UNUSED)
1480{
1481 return gen_rtx_REG (mode, RETURN_VALUE_REGNUM);
1482}
1483
1484/* Worker function for TARGET_FUNCTION_VALUE_REGNO_P. */
1485
1486static bool
1487xstormy16_function_value_regno_p (const unsigned int regno)
1488{
1489 return (regno == RETURN_VALUE_REGNUM);
1490}
1491
52560c7b
GK
1492/* A C compound statement that outputs the assembler code for a thunk function,
1493 used to implement C++ virtual function calls with multiple inheritance. The
1494 thunk acts as a wrapper around a virtual function, adjusting the implicit
1495 object parameter before handing control off to the real function.
1496
1497 First, emit code to add the integer DELTA to the location that contains the
1498 incoming first argument. Assume that this argument contains a pointer, and
1499 is the one used to pass the `this' pointer in C++. This is the incoming
1500 argument *before* the function prologue, e.g. `%o0' on a sparc. The
1501 addition must preserve the values of all other incoming arguments.
1502
1503 After the addition, emit code to jump to FUNCTION, which is a
1504 `FUNCTION_DECL'. This is a direct pure jump, not a call, and does not touch
1505 the return address. Hence returning from FUNCTION will return to whoever
1506 called the current `thunk'.
1507
1508 The effect must be as if @var{function} had been called directly
1509 with the adjusted first argument. This macro is responsible for
1510 emitting all of the code for a thunk function;
1511 TARGET_ASM_FUNCTION_PROLOGUE and TARGET_ASM_FUNCTION_EPILOGUE are
1512 not invoked.
1513
1514 The THUNK_FNDECL is redundant. (DELTA and FUNCTION have already been
1515 extracted from it.) It might possibly be useful on some targets, but
1516 probably not. */
1517
c590b625 1518static void
51c16b7e
SB
1519xstormy16_asm_output_mi_thunk (FILE *file,
1520 tree thunk_fndecl ATTRIBUTE_UNUSED,
1521 HOST_WIDE_INT delta,
1522 HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
1523 tree function)
52560c7b 1524{
f7430263 1525 const char *fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk_fndecl));
52560c7b 1526 int regnum = FIRST_ARGUMENT_REGISTER;
5ab9749e 1527
f7430263 1528 assemble_start_function (thunk_fndecl, fnname);
52560c7b 1529 /* There might be a hidden first argument for a returned structure. */
61f71b34 1530 if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
52560c7b 1531 regnum += 1;
5ab9749e 1532
eb0424da 1533 fprintf (file, "\tadd %s,#0x%x\n", reg_names[regnum], (int) delta & 0xFFFF);
52560c7b
GK
1534 fputs ("\tjmpf ", file);
1535 assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
1536 putc ('\n', file);
f7430263 1537 assemble_end_function (thunk_fndecl, fnname);
52560c7b
GK
1538}
1539
54e9a19d
DD
1540/* The purpose of this function is to override the default behavior of
1541 BSS objects. Normally, they go into .bss or .sbss via ".common"
1542 directives, but we need to override that and put them in
1543 .bss_below100. We can't just use a section override (like we do
1544 for .data_below100), because that makes them initialized rather
1545 than uninitialized. */
5ab9749e 1546
54e9a19d
DD
1547void
1548xstormy16_asm_output_aligned_common (FILE *stream,
2f806f3b 1549 tree decl,
54e9a19d
DD
1550 const char *name,
1551 int size,
1552 int align,
1553 int global)
1554{
038eab67 1555 rtx mem = decl == NULL_TREE ? NULL_RTX : DECL_RTL (decl);
2f806f3b 1556 rtx symbol;
5ab9749e 1557
2f806f3b 1558 if (mem != NULL_RTX
a21eaf5e 1559 && MEM_P (mem)
2f806f3b
NC
1560 && GET_CODE (symbol = XEXP (mem, 0)) == SYMBOL_REF
1561 && SYMBOL_REF_FLAGS (symbol) & SYMBOL_FLAG_XSTORMY16_BELOW100)
54e9a19d 1562 {
2f806f3b
NC
1563 const char *name2;
1564 int p2align = 0;
1565
d6b5193b 1566 switch_to_section (bss100_section);
2f806f3b
NC
1567
1568 while (align > 8)
54e9a19d 1569 {
2f806f3b
NC
1570 align /= 2;
1571 p2align ++;
54e9a19d 1572 }
54e9a19d 1573
2f806f3b
NC
1574 name2 = default_strip_name_encoding (name);
1575 if (global)
1576 fprintf (stream, "\t.globl\t%s\n", name2);
1577 if (p2align)
1578 fprintf (stream, "\t.p2align %d\n", p2align);
1579 fprintf (stream, "\t.type\t%s, @object\n", name2);
1580 fprintf (stream, "\t.size\t%s, %d\n", name2, size);
1581 fprintf (stream, "%s:\n\t.space\t%d\n", name2, size);
1582 return;
54e9a19d
DD
1583 }
1584
1585 if (!global)
1586 {
1587 fprintf (stream, "\t.local\t");
1588 assemble_name (stream, name);
1589 fprintf (stream, "\n");
1590 }
1591 fprintf (stream, "\t.comm\t");
1592 assemble_name (stream, name);
43f51151 1593 fprintf (stream, ",%u,%u\n", size, align / BITS_PER_UNIT);
54e9a19d
DD
1594}
1595
d6b5193b
RS
1596/* Implement TARGET_ASM_INIT_SECTIONS. */
1597
1598static void
1599xstormy16_asm_init_sections (void)
1600{
1601 bss100_section
1602 = get_unnamed_section (SECTION_WRITE | SECTION_BSS,
1603 output_section_asm_op,
1604 "\t.section \".bss_below100\",\"aw\",@nobits");
1605}
1606
54e9a19d
DD
1607/* Mark symbols with the "below100" attribute so that we can use the
1608 special addressing modes for them. */
1609
1610static void
2f806f3b 1611xstormy16_encode_section_info (tree decl, rtx r, int first)
54e9a19d 1612{
e5eb9a52
NC
1613 default_encode_section_info (decl, r, first);
1614
2f806f3b 1615 if (TREE_CODE (decl) == VAR_DECL
54e9a19d
DD
1616 && (lookup_attribute ("below100", DECL_ATTRIBUTES (decl))
1617 || lookup_attribute ("BELOW100", DECL_ATTRIBUTES (decl))))
1618 {
2f806f3b 1619 rtx symbol = XEXP (r, 0);
5ab9749e 1620
2f806f3b
NC
1621 gcc_assert (GET_CODE (symbol) == SYMBOL_REF);
1622 SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_XSTORMY16_BELOW100;
54e9a19d
DD
1623 }
1624}
1625
6208b55d 1626#undef TARGET_ASM_CONSTRUCTOR
5ab9749e 1627#define TARGET_ASM_CONSTRUCTOR xstormy16_asm_out_constructor
6208b55d 1628#undef TARGET_ASM_DESTRUCTOR
5ab9749e
NC
1629#define TARGET_ASM_DESTRUCTOR xstormy16_asm_out_destructor
1630
1631/* Output constructors and destructors. Just like
1632 default_named_section_asm_out_* but don't set the sections writable. */
43898541
GK
1633
1634static void
51c16b7e 1635xstormy16_asm_out_destructor (rtx symbol, int priority)
43898541
GK
1636{
1637 const char *section = ".dtors";
42ee9611 1638 char buf[18];
43898541 1639
71cc389b 1640 /* ??? This only works reliably with the GNU linker. */
43898541
GK
1641 if (priority != DEFAULT_INIT_PRIORITY)
1642 {
1643 sprintf (buf, ".dtors.%.5u",
1644 /* Invert the numbering so the linker puts us in the proper
1645 order; constructors are run from right to left, and the
1646 linker sorts in increasing order. */
1647 MAX_INIT_PRIORITY - priority);
1648 section = buf;
1649 }
1650
d6b5193b 1651 switch_to_section (get_section (section, 0, NULL));
43898541
GK
1652 assemble_align (POINTER_SIZE);
1653 assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
1654}
1655
1656static void
51c16b7e 1657xstormy16_asm_out_constructor (rtx symbol, int priority)
43898541
GK
1658{
1659 const char *section = ".ctors";
42ee9611 1660 char buf[18];
43898541 1661
71cc389b 1662 /* ??? This only works reliably with the GNU linker. */
43898541
GK
1663 if (priority != DEFAULT_INIT_PRIORITY)
1664 {
1665 sprintf (buf, ".ctors.%.5u",
1666 /* Invert the numbering so the linker puts us in the proper
1667 order; constructors are run from right to left, and the
1668 linker sorts in increasing order. */
1669 MAX_INIT_PRIORITY - priority);
1670 section = buf;
1671 }
1672
d6b5193b 1673 switch_to_section (get_section (section, 0, NULL));
43898541
GK
1674 assemble_align (POINTER_SIZE);
1675 assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
1676}
4b58290f 1677\f
43070a6e 1678/* Worker function for TARGET_PRINT_OPERAND_ADDRESS.
5ab9749e 1679
43070a6e
AS
1680 Print a memory address as an operand to reference that memory location. */
1681
1682static void
cc8ca59e
JB
1683xstormy16_print_operand_address (FILE *file, machine_mode /*mode*/,
1684 rtx address)
4b58290f
GK
1685{
1686 HOST_WIDE_INT offset;
1687 int pre_dec, post_inc;
1688
1689 /* There are a few easy cases. */
a21eaf5e 1690 if (CONST_INT_P (address))
4b58290f
GK
1691 {
1692 fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (address) & 0xFFFF);
1693 return;
1694 }
5ab9749e 1695
a21eaf5e 1696 if (CONSTANT_P (address) || LABEL_P (address))
4b58290f
GK
1697 {
1698 output_addr_const (file, address);
1699 return;
1700 }
4b58290f 1701
5ab9749e
NC
1702 /* Otherwise, it's hopefully something of the form
1703 (plus:HI (pre_dec:HI (reg:HI ...)) (const_int ...)). */
4b58290f
GK
1704 if (GET_CODE (address) == PLUS)
1705 {
a21eaf5e 1706 gcc_assert (CONST_INT_P (XEXP (address, 1)));
4b58290f
GK
1707 offset = INTVAL (XEXP (address, 1));
1708 address = XEXP (address, 0);
1709 }
1710 else
1711 offset = 0;
1712
1713 pre_dec = (GET_CODE (address) == PRE_DEC);
1714 post_inc = (GET_CODE (address) == POST_INC);
1715 if (pre_dec || post_inc)
1716 address = XEXP (address, 0);
5ab9749e 1717
a21eaf5e 1718 gcc_assert (REG_P (address));
4b58290f
GK
1719
1720 fputc ('(', file);
1721 if (pre_dec)
1722 fputs ("--", file);
1723 fputs (reg_names [REGNO (address)], file);
1724 if (post_inc)
1725 fputs ("++", file);
1726 if (offset != 0)
4a0a75dd 1727 fprintf (file, "," HOST_WIDE_INT_PRINT_DEC, offset);
4b58290f
GK
1728 fputc (')', file);
1729}
1730
43070a6e 1731/* Worker function for TARGET_PRINT_OPERAND.
5ab9749e 1732
43070a6e
AS
1733 Print an operand to an assembler instruction. */
1734
1735static void
51c16b7e 1736xstormy16_print_operand (FILE *file, rtx x, int code)
4b58290f
GK
1737{
1738 switch (code)
1739 {
1740 case 'B':
1741 /* There is either one bit set, or one bit clear, in X.
1742 Print it preceded by '#'. */
1743 {
54e9a19d 1744 static int bits_set[8] = { 0, 1, 1, 2, 1, 2, 2, 3 };
e9818db2
GK
1745 HOST_WIDE_INT xx = 1;
1746 HOST_WIDE_INT l;
4b58290f 1747
a21eaf5e 1748 if (CONST_INT_P (x))
4b58290f
GK
1749 xx = INTVAL (x);
1750 else
9e637a26 1751 output_operand_lossage ("'B' operand is not constant");
5ab9749e 1752
54e9a19d
DD
1753 /* GCC sign-extends masks with the MSB set, so we have to
1754 detect all the cases that differ only in sign extension
1755 beyond the bits we care about. Normally, the predicates
1756 and constraints ensure that we have the right values. This
1757 works correctly for valid masks. */
1758 if (bits_set[xx & 7] <= 1)
1759 {
1760 /* Remove sign extension bits. */
1761 if ((~xx & ~(HOST_WIDE_INT)0xff) == 0)
1762 xx &= 0xff;
1763 else if ((~xx & ~(HOST_WIDE_INT)0xffff) == 0)
1764 xx &= 0xffff;
1765 l = exact_log2 (xx);
1766 }
1767 else
1768 {
1769 /* Add sign extension bits. */
1770 if ((xx & ~(HOST_WIDE_INT)0xff) == 0)
1771 xx |= ~(HOST_WIDE_INT)0xff;
1772 else if ((xx & ~(HOST_WIDE_INT)0xffff) == 0)
1773 xx |= ~(HOST_WIDE_INT)0xffff;
1774 l = exact_log2 (~xx);
1775 }
1776
4b58290f 1777 if (l == -1)
9e637a26 1778 output_operand_lossage ("'B' operand has multiple bits set");
5ab9749e 1779
4a0a75dd 1780 fprintf (file, IMMEDIATE_PREFIX HOST_WIDE_INT_PRINT_DEC, l);
4b58290f
GK
1781 return;
1782 }
1783
1784 case 'C':
1785 /* Print the symbol without a surrounding @fptr(). */
1786 if (GET_CODE (x) == SYMBOL_REF)
1787 assemble_name (file, XSTR (x, 0));
a21eaf5e 1788 else if (LABEL_P (x))
2f0b7af6 1789 output_asm_label (x);
4b58290f 1790 else
cc8ca59e 1791 xstormy16_print_operand_address (file, VOIDmode, x);
4b58290f
GK
1792 return;
1793
1794 case 'o':
1795 case 'O':
5ab9749e 1796 /* Print the immediate operand less one, preceded by '#'.
4b58290f
GK
1797 For 'O', negate it first. */
1798 {
e9818db2 1799 HOST_WIDE_INT xx = 0;
5ab9749e 1800
a21eaf5e 1801 if (CONST_INT_P (x))
4b58290f
GK
1802 xx = INTVAL (x);
1803 else
9e637a26 1804 output_operand_lossage ("'o' operand is not constant");
5ab9749e 1805
4b58290f
GK
1806 if (code == 'O')
1807 xx = -xx;
5ab9749e 1808
4a0a75dd 1809 fprintf (file, IMMEDIATE_PREFIX HOST_WIDE_INT_PRINT_DEC, xx - 1);
4b58290f
GK
1810 return;
1811 }
1812
54e9a19d
DD
1813 case 'b':
1814 /* Print the shift mask for bp/bn. */
1815 {
1816 HOST_WIDE_INT xx = 1;
1817 HOST_WIDE_INT l;
1818
a21eaf5e 1819 if (CONST_INT_P (x))
54e9a19d
DD
1820 xx = INTVAL (x);
1821 else
9e637a26 1822 output_operand_lossage ("'B' operand is not constant");
5ab9749e 1823
54e9a19d 1824 l = 7 - xx;
5ab9749e 1825
54e9a19d
DD
1826 fputs (IMMEDIATE_PREFIX, file);
1827 fprintf (file, HOST_WIDE_INT_PRINT_DEC, l);
1828 return;
1829 }
1830
4b58290f
GK
1831 case 0:
1832 /* Handled below. */
1833 break;
5ab9749e 1834
4b58290f 1835 default:
c6243b4c 1836 output_operand_lossage ("xstormy16_print_operand: unknown code");
4b58290f
GK
1837 return;
1838 }
1839
1840 switch (GET_CODE (x))
1841 {
1842 case REG:
1843 fputs (reg_names [REGNO (x)], file);
1844 break;
1845
1846 case MEM:
cc8ca59e 1847 xstormy16_print_operand_address (file, GET_MODE (x), XEXP (x, 0));
4b58290f
GK
1848 break;
1849
1850 default:
1851 /* Some kind of constant or label; an immediate operand,
1852 so prefix it with '#' for the assembler. */
1853 fputs (IMMEDIATE_PREFIX, file);
1854 output_addr_const (file, x);
1855 break;
1856 }
1857
1858 return;
1859}
4b58290f
GK
1860\f
1861/* Expander for the `casesi' pattern.
1862 INDEX is the index of the switch statement.
1863 LOWER_BOUND is a CONST_INT that is the value of INDEX corresponding
1864 to the first table entry.
1865 RANGE is the number of table entries.
1866 TABLE is an ADDR_VEC that is the jump table.
1867 DEFAULT_LABEL is the address to branch to if INDEX is outside the
5ab9749e 1868 range LOWER_BOUND to LOWER_BOUND + RANGE - 1. */
4b58290f 1869
5ab9749e 1870void
51c16b7e
SB
1871xstormy16_expand_casesi (rtx index, rtx lower_bound, rtx range,
1872 rtx table, rtx default_label)
4b58290f
GK
1873{
1874 HOST_WIDE_INT range_i = INTVAL (range);
1875 rtx int_index;
1876
1877 /* This code uses 'br', so it can deal only with tables of size up to
1878 8192 entries. */
1879 if (range_i >= 8192)
5ab9749e 1880 sorry ("switch statement of size %lu entries too large",
4b58290f
GK
1881 (unsigned long) range_i);
1882
4192f0d2 1883 index = expand_binop (SImode, sub_optab, index, lower_bound, NULL_RTX, 0,
4b58290f
GK
1884 OPTAB_LIB_WIDEN);
1885 emit_cmp_and_jump_insns (index, range, GTU, NULL_RTX, SImode, 1,
40c13662 1886 default_label);
4b58290f 1887 int_index = gen_lowpart_common (HImode, index);
a556fd39 1888 emit_insn (gen_ashlhi3 (int_index, int_index, const2_rtx));
4b58290f
GK
1889 emit_jump_insn (gen_tablejump_pcrel (int_index, table));
1890}
1891
1892/* Output an ADDR_VEC. It is output as a sequence of 'jmpf'
1893 instructions, without label or alignment or any other special
1894 constructs. We know that the previous instruction will be the
1895 `tablejump_pcrel' output above.
1896
1897 TODO: it might be nice to output 'br' instructions if they could
1898 all reach. */
1899
1900void
51c16b7e 1901xstormy16_output_addr_vec (FILE *file, rtx label ATTRIBUTE_UNUSED, rtx table)
5ab9749e 1902{
4b58290f 1903 int vlen, idx;
5ab9749e 1904
d6b5193b 1905 switch_to_section (current_function_section ());
4b58290f
GK
1906
1907 vlen = XVECLEN (table, 0);
1908 for (idx = 0; idx < vlen; idx++)
1909 {
1910 fputs ("\tjmpf ", file);
2f0b7af6 1911 output_asm_label (XEXP (XVECEXP (table, 0, idx), 0));
4b58290f
GK
1912 fputc ('\n', file);
1913 }
1914}
4b58290f
GK
1915\f
1916/* Expander for the `call' patterns.
a21eaf5e
NC
1917 RETVAL is the RTL for the return register or NULL for void functions.
1918 DEST is the function to call, expressed as a MEM.
1919 COUNTER is ignored. */
4b58290f 1920
5ab9749e 1921void
51c16b7e 1922xstormy16_expand_call (rtx retval, rtx dest, rtx counter)
4b58290f
GK
1923{
1924 rtx call, temp;
ef4bddc2 1925 machine_mode mode;
4b58290f 1926
a21eaf5e 1927 gcc_assert (MEM_P (dest));
4b58290f
GK
1928 dest = XEXP (dest, 0);
1929
a21eaf5e 1930 if (! CONSTANT_P (dest) && ! REG_P (dest))
4b58290f 1931 dest = force_reg (Pmode, dest);
5ab9749e 1932
4b58290f
GK
1933 if (retval == NULL)
1934 mode = VOIDmode;
1935 else
1936 mode = GET_MODE (retval);
1937
1938 call = gen_rtx_CALL (mode, gen_rtx_MEM (FUNCTION_MODE, dest),
1939 counter);
1940 if (retval)
f7df4a84 1941 call = gen_rtx_SET (retval, call);
5ab9749e 1942
4b58290f
GK
1943 if (! CONSTANT_P (dest))
1944 {
1945 temp = gen_reg_rtx (HImode);
1946 emit_move_insn (temp, const0_rtx);
1947 }
1948 else
1949 temp = const0_rtx;
5ab9749e
NC
1950
1951 call = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, call,
4b58290f
GK
1952 gen_rtx_USE (VOIDmode, temp)));
1953 emit_call_insn (call);
1954}
1955\f
1956/* Expanders for multiword computational operations. */
1957
1958/* Expander for arithmetic operations; emit insns to compute
1959
1960 (set DEST (CODE:MODE SRC0 SRC1))
5ab9749e 1961
d40ba0b6
NC
1962 When CODE is COMPARE, a branch template is generated
1963 (this saves duplicating code in xstormy16_split_cbranch). */
4b58290f 1964
5ab9749e 1965void
ef4bddc2 1966xstormy16_expand_arith (machine_mode mode, enum rtx_code code,
d40ba0b6 1967 rtx dest, rtx src0, rtx src1)
4b58290f
GK
1968{
1969 int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
1970 int i;
1971 int firstloop = 1;
1972
1973 if (code == NEG)
9be13211 1974 emit_move_insn (src0, const0_rtx);
5ab9749e 1975
4b58290f
GK
1976 for (i = 0; i < num_words; i++)
1977 {
1978 rtx w_src0, w_src1, w_dest;
1979 rtx insn;
5ab9749e
NC
1980
1981 w_src0 = simplify_gen_subreg (word_mode, src0, mode,
9be13211 1982 i * UNITS_PER_WORD);
4b58290f
GK
1983 w_src1 = simplify_gen_subreg (word_mode, src1, mode, i * UNITS_PER_WORD);
1984 w_dest = simplify_gen_subreg (word_mode, dest, mode, i * UNITS_PER_WORD);
1985
1986 switch (code)
1987 {
1988 case PLUS:
1989 if (firstloop
a21eaf5e
NC
1990 && CONST_INT_P (w_src1)
1991 && INTVAL (w_src1) == 0)
4b58290f 1992 continue;
5ab9749e 1993
4b58290f 1994 if (firstloop)
d40ba0b6 1995 insn = gen_addchi4 (w_dest, w_src0, w_src1);
4b58290f 1996 else
d40ba0b6 1997 insn = gen_addchi5 (w_dest, w_src0, w_src1);
4b58290f
GK
1998 break;
1999
2000 case NEG:
2001 case MINUS:
2002 case COMPARE:
2003 if (code == COMPARE && i == num_words - 1)
2004 {
2005 rtx branch, sub, clobber, sub_1;
5ab9749e
NC
2006
2007 sub_1 = gen_rtx_MINUS (HImode, w_src0,
b72bbbcb 2008 gen_rtx_ZERO_EXTEND (HImode, gen_rtx_REG (BImode, CARRY_REGNUM)));
f7df4a84 2009 sub = gen_rtx_SET (w_dest,
4b58290f 2010 gen_rtx_MINUS (HImode, sub_1, w_src1));
b72bbbcb 2011 clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (BImode, CARRY_REGNUM));
f7df4a84 2012 branch = gen_rtx_SET (pc_rtx,
4b58290f
GK
2013 gen_rtx_IF_THEN_ELSE (VOIDmode,
2014 gen_rtx_EQ (HImode,
2015 sub_1,
2016 w_src1),
2017 pc_rtx,
2018 pc_rtx));
2019 insn = gen_rtx_PARALLEL (VOIDmode,
2020 gen_rtvec (3, branch, sub, clobber));
2021 }
2022 else if (firstloop
2023 && code != COMPARE
a21eaf5e
NC
2024 && CONST_INT_P (w_src1)
2025 && INTVAL (w_src1) == 0)
4b58290f
GK
2026 continue;
2027 else if (firstloop)
d40ba0b6 2028 insn = gen_subchi4 (w_dest, w_src0, w_src1);
4b58290f 2029 else
d40ba0b6 2030 insn = gen_subchi5 (w_dest, w_src0, w_src1);
4b58290f
GK
2031 break;
2032
2033 case IOR:
2034 case XOR:
2035 case AND:
a21eaf5e 2036 if (CONST_INT_P (w_src1)
4b58290f
GK
2037 && INTVAL (w_src1) == -(code == AND))
2038 continue;
5ab9749e 2039
f7df4a84
RS
2040 insn = gen_rtx_SET (w_dest, gen_rtx_fmt_ee (code, mode,
2041 w_src0, w_src1));
4b58290f
GK
2042 break;
2043
2044 case NOT:
f7df4a84 2045 insn = gen_rtx_SET (w_dest, gen_rtx_NOT (mode, w_src0));
4b58290f
GK
2046 break;
2047
2048 default:
4718bfd8 2049 gcc_unreachable ();
4b58290f 2050 }
5ab9749e 2051
4b58290f
GK
2052 firstloop = 0;
2053 emit (insn);
2054 }
f3cd0185
DD
2055
2056 /* If we emit nothing, try_split() will think we failed. So emit
2057 something that does nothing and can be optimized away. */
2058 if (firstloop)
2059 emit (gen_nop ());
4b58290f
GK
2060}
2061
4b58290f 2062/* The shift operations are split at output time for constant values;
5ab9749e 2063 variable-width shifts get handed off to a library routine.
4b58290f
GK
2064
2065 Generate an output string to do (set X (CODE:MODE X SIZE_R))
2066 SIZE_R will be a CONST_INT, X will be a hard register. */
2067
5ab9749e 2068const char *
ef4bddc2 2069xstormy16_output_shift (machine_mode mode, enum rtx_code code,
51c16b7e 2070 rtx x, rtx size_r, rtx temp)
4b58290f
GK
2071{
2072 HOST_WIDE_INT size;
2073 const char *r0, *r1, *rt;
2074 static char r[64];
2075
a21eaf5e
NC
2076 gcc_assert (CONST_INT_P (size_r)
2077 && REG_P (x)
2078 && mode == SImode);
2079
4b58290f
GK
2080 size = INTVAL (size_r) & (GET_MODE_BITSIZE (mode) - 1);
2081
2082 if (size == 0)
2083 return "";
2084
2085 r0 = reg_names [REGNO (x)];
2086 r1 = reg_names [REGNO (x) + 1];
4b58290f
GK
2087
2088 /* For shifts of size 1, we can use the rotate instructions. */
2089 if (size == 1)
2090 {
2091 switch (code)
2092 {
2093 case ASHIFT:
2094 sprintf (r, "shl %s,#1 | rlc %s,#1", r0, r1);
2095 break;
2096 case ASHIFTRT:
2097 sprintf (r, "asr %s,#1 | rrc %s,#1", r1, r0);
2098 break;
2099 case LSHIFTRT:
2100 sprintf (r, "shr %s,#1 | rrc %s,#1", r1, r0);
2101 break;
2102 default:
4718bfd8 2103 gcc_unreachable ();
4b58290f
GK
2104 }
2105 return r;
2106 }
5ab9749e 2107
4b58290f
GK
2108 /* For large shifts, there are easy special cases. */
2109 if (size == 16)
2110 {
2111 switch (code)
2112 {
2113 case ASHIFT:
2114 sprintf (r, "mov %s,%s | mov %s,#0", r1, r0, r0);
2115 break;
2116 case ASHIFTRT:
2117 sprintf (r, "mov %s,%s | asr %s,#15", r0, r1, r1);
2118 break;
2119 case LSHIFTRT:
2120 sprintf (r, "mov %s,%s | mov %s,#0", r0, r1, r1);
2121 break;
2122 default:
4718bfd8 2123 gcc_unreachable ();
4b58290f
GK
2124 }
2125 return r;
2126 }
2127 if (size > 16)
2128 {
2129 switch (code)
2130 {
2131 case ASHIFT:
5ab9749e 2132 sprintf (r, "mov %s,%s | mov %s,#0 | shl %s,#%d",
4b58290f
GK
2133 r1, r0, r0, r1, (int) size - 16);
2134 break;
2135 case ASHIFTRT:
5ab9749e 2136 sprintf (r, "mov %s,%s | asr %s,#15 | asr %s,#%d",
4b58290f
GK
2137 r0, r1, r1, r0, (int) size - 16);
2138 break;
2139 case LSHIFTRT:
5ab9749e 2140 sprintf (r, "mov %s,%s | mov %s,#0 | shr %s,#%d",
4b58290f
GK
2141 r0, r1, r1, r0, (int) size - 16);
2142 break;
2143 default:
4718bfd8 2144 gcc_unreachable ();
4b58290f
GK
2145 }
2146 return r;
2147 }
2148
2149 /* For the rest, we have to do more work. In particular, we
2150 need a temporary. */
5766e0ef 2151 rt = reg_names [REGNO (temp)];
4b58290f
GK
2152 switch (code)
2153 {
2154 case ASHIFT:
5ab9749e
NC
2155 sprintf (r,
2156 "mov %s,%s | shl %s,#%d | shl %s,#%d | shr %s,#%d | or %s,%s",
2157 rt, r0, r0, (int) size, r1, (int) size, rt, (int) (16 - size),
4b58290f
GK
2158 r1, rt);
2159 break;
2160 case ASHIFTRT:
5ab9749e
NC
2161 sprintf (r,
2162 "mov %s,%s | asr %s,#%d | shr %s,#%d | shl %s,#%d | or %s,%s",
2163 rt, r1, r1, (int) size, r0, (int) size, rt, (int) (16 - size),
4b58290f
GK
2164 r0, rt);
2165 break;
2166 case LSHIFTRT:
5ab9749e
NC
2167 sprintf (r,
2168 "mov %s,%s | shr %s,#%d | shr %s,#%d | shl %s,#%d | or %s,%s",
2169 rt, r1, r1, (int) size, r0, (int) size, rt, (int) (16 - size),
4b58290f
GK
2170 r0, rt);
2171 break;
2172 default:
4718bfd8 2173 gcc_unreachable ();
4b58290f
GK
2174 }
2175 return r;
2176}
2177\f
2178/* Attribute handling. */
2179
2180/* Return nonzero if the function is an interrupt function. */
5ab9749e 2181
4b58290f 2182int
51c16b7e 2183xstormy16_interrupt_function_p (void)
4b58290f
GK
2184{
2185 tree attributes;
5ab9749e 2186
4b58290f
GK
2187 /* The dwarf2 mechanism asks for INCOMING_FRAME_SP_OFFSET before
2188 any functions are declared, which is demonstrably wrong, but
2189 it is worked around here. FIXME. */
2190 if (!cfun)
2191 return 0;
2192
2193 attributes = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
2194 return lookup_attribute ("interrupt", attributes) != NULL_TREE;
2195}
2196
5ab9749e
NC
2197#undef TARGET_ATTRIBUTE_TABLE
2198#define TARGET_ATTRIBUTE_TABLE xstormy16_attribute_table
2199
51c16b7e
SB
2200static tree xstormy16_handle_interrupt_attribute
2201 (tree *, tree, tree, int, bool *);
54e9a19d
DD
2202static tree xstormy16_handle_below100_attribute
2203 (tree *, tree, tree, int, bool *);
51c16b7e 2204
c6243b4c 2205static const struct attribute_spec xstormy16_attribute_table[] =
91d231cb 2206{
4849deb1
JJ
2207 /* name, min_len, max_len, decl_req, type_req, fn_type_req,
2208 affects_type_identity, handler, exclude. */
2209 { "interrupt", 0, 0, false, true, true, false,
2210 xstormy16_handle_interrupt_attribute, NULL },
2211 { "BELOW100", 0, 0, false, false, false, false,
2212 xstormy16_handle_below100_attribute, NULL },
2213 { "below100", 0, 0, false, false, false, false,
2214 xstormy16_handle_below100_attribute, NULL },
2215 { NULL, 0, 0, false, false, false, false, NULL, NULL }
91d231cb
JM
2216};
2217
2218/* Handle an "interrupt" attribute;
2219 arguments as in struct attribute_spec.handler. */
5ab9749e 2220
91d231cb 2221static tree
51c16b7e
SB
2222xstormy16_handle_interrupt_attribute (tree *node, tree name,
2223 tree args ATTRIBUTE_UNUSED,
2224 int flags ATTRIBUTE_UNUSED,
2225 bool *no_add_attrs)
4b58290f 2226{
91d231cb
JM
2227 if (TREE_CODE (*node) != FUNCTION_TYPE)
2228 {
29d08eba
JM
2229 warning (OPT_Wattributes, "%qE attribute only applies to functions",
2230 name);
91d231cb
JM
2231 *no_add_attrs = true;
2232 }
4b58290f 2233
91d231cb 2234 return NULL_TREE;
4b58290f 2235}
54e9a19d
DD
2236
2237/* Handle an "below" attribute;
2238 arguments as in struct attribute_spec.handler. */
5ab9749e 2239
54e9a19d
DD
2240static tree
2241xstormy16_handle_below100_attribute (tree *node,
2242 tree name ATTRIBUTE_UNUSED,
2243 tree args ATTRIBUTE_UNUSED,
2244 int flags ATTRIBUTE_UNUSED,
2245 bool *no_add_attrs)
2246{
2247 if (TREE_CODE (*node) != VAR_DECL
2248 && TREE_CODE (*node) != POINTER_TYPE
2249 && TREE_CODE (*node) != TYPE_DECL)
2250 {
5c498b10
DD
2251 warning (OPT_Wattributes,
2252 "%<__BELOW100__%> attribute only applies to variables");
54e9a19d
DD
2253 *no_add_attrs = true;
2254 }
2255 else if (args == NULL_TREE && TREE_CODE (*node) == VAR_DECL)
2256 {
2257 if (! (TREE_PUBLIC (*node) || TREE_STATIC (*node)))
2258 {
58385f6a 2259 warning (OPT_Wattributes, "%<__BELOW100__%> attribute not allowed "
ab532386 2260 "with auto storage class");
54e9a19d
DD
2261 *no_add_attrs = true;
2262 }
2263 }
5ab9749e 2264
54e9a19d
DD
2265 return NULL_TREE;
2266}
3d4b192a 2267\f
5ab9749e
NC
2268#undef TARGET_INIT_BUILTINS
2269#define TARGET_INIT_BUILTINS xstormy16_init_builtins
2270#undef TARGET_EXPAND_BUILTIN
2271#define TARGET_EXPAND_BUILTIN xstormy16_expand_builtin
2272
2273static struct
2274{
2275 const char * name;
2276 int md_code;
2277 const char * arg_ops; /* 0..9, t for temp register, r for return value. */
2278 const char * arg_types; /* s=short,l=long, upper case for unsigned. */
2279}
2280 s16builtins[] =
2281{
3d4b192a
DD
2282 { "__sdivlh", CODE_FOR_sdivlh, "rt01", "sls" },
2283 { "__smodlh", CODE_FOR_sdivlh, "tr01", "sls" },
2284 { "__udivlh", CODE_FOR_udivlh, "rt01", "SLS" },
2285 { "__umodlh", CODE_FOR_udivlh, "tr01", "SLS" },
5ab9749e 2286 { NULL, 0, NULL, NULL }
3d4b192a
DD
2287};
2288
2289static void
51c16b7e 2290xstormy16_init_builtins (void)
3d4b192a 2291{
2c67cf6e
NF
2292 tree args[2], ret_type, arg = NULL_TREE, ftype;
2293 int i, a, n_args;
3d4b192a
DD
2294
2295 ret_type = void_type_node;
2296
5ab9749e 2297 for (i = 0; s16builtins[i].name; i++)
3d4b192a 2298 {
2c67cf6e
NF
2299 n_args = strlen (s16builtins[i].arg_types) - 1;
2300
2301 gcc_assert (n_args <= (int) ARRAY_SIZE (args));
2302
15f072f9 2303 for (a = n_args - 1; a >= 0; a--)
2c67cf6e
NF
2304 args[a] = NULL_TREE;
2305
2306 for (a = n_args; a >= 0; a--)
3d4b192a
DD
2307 {
2308 switch (s16builtins[i].arg_types[a])
2309 {
2310 case 's': arg = short_integer_type_node; break;
2311 case 'S': arg = short_unsigned_type_node; break;
2312 case 'l': arg = long_integer_type_node; break;
2313 case 'L': arg = long_unsigned_type_node; break;
4718bfd8 2314 default: gcc_unreachable ();
3d4b192a
DD
2315 }
2316 if (a == 0)
2317 ret_type = arg;
2318 else
2c67cf6e 2319 args[a-1] = arg;
3d4b192a 2320 }
15f072f9 2321 ftype = build_function_type_list (ret_type, args[0], args[1], NULL_TREE);
2c67cf6e 2322 add_builtin_function (s16builtins[i].name, ftype,
15f072f9 2323 i, BUILT_IN_MD, NULL, NULL_TREE);
3d4b192a
DD
2324 }
2325}
2326
2327static rtx
f84fe9b6
NC
2328xstormy16_expand_builtin (tree exp, rtx target,
2329 rtx subtarget ATTRIBUTE_UNUSED,
ef4bddc2 2330 machine_mode mode ATTRIBUTE_UNUSED,
f84fe9b6 2331 int ignore ATTRIBUTE_UNUSED)
3d4b192a
DD
2332{
2333 rtx op[10], args[10], pat, copyto[10], retval = 0;
2334 tree fndecl, argtree;
2335 int i, a, o, code;
2336
2337 fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
2338 argtree = TREE_OPERAND (exp, 1);
4d732405 2339 i = DECL_MD_FUNCTION_CODE (fndecl);
3d4b192a
DD
2340 code = s16builtins[i].md_code;
2341
2342 for (a = 0; a < 10 && argtree; a++)
2343 {
f1cb6795 2344 args[a] = expand_normal (TREE_VALUE (argtree));
3d4b192a
DD
2345 argtree = TREE_CHAIN (argtree);
2346 }
2347
2348 for (o = 0; s16builtins[i].arg_ops[o]; o++)
2349 {
2350 char ao = s16builtins[i].arg_ops[o];
2351 char c = insn_data[code].operand[o].constraint[0];
ef4bddc2 2352 machine_mode omode;
3d4b192a
DD
2353
2354 copyto[o] = 0;
2355
ef4bddc2 2356 omode = (machine_mode) insn_data[code].operand[o].mode;
3d4b192a
DD
2357 if (ao == 'r')
2358 op[o] = target ? target : gen_reg_rtx (omode);
2359 else if (ao == 't')
2360 op[o] = gen_reg_rtx (omode);
2361 else
2362 op[o] = args[(int) hex_value (ao)];
2363
2364 if (! (*insn_data[code].operand[o].predicate) (op[o], GET_MODE (op[o])))
2365 {
2366 if (c == '+' || c == '=')
2367 {
2368 copyto[o] = op[o];
2369 op[o] = gen_reg_rtx (omode);
2370 }
2371 else
2372 op[o] = copy_to_mode_reg (omode, op[o]);
2373 }
2374
2375 if (ao == 'r')
2376 retval = op[o];
2377 }
2378
2379 pat = GEN_FCN (code) (op[0], op[1], op[2], op[3], op[4],
2380 op[5], op[6], op[7], op[8], op[9]);
2381 emit_insn (pat);
2382
2383 for (o = 0; s16builtins[i].arg_ops[o]; o++)
2384 if (copyto[o])
2385 {
2386 emit_move_insn (copyto[o], op[o]);
2387 if (op[o] == retval)
2388 retval = copyto[o];
2389 }
2390
2391 return retval;
2392}
54e9a19d 2393\f
54e9a19d
DD
2394/* Look for combinations of insns that can be converted to BN or BP
2395 opcodes. This is, unfortunately, too complex to do with MD
2396 patterns. */
5ab9749e 2397
54e9a19d 2398static void
b32d5189 2399combine_bnp (rtx_insn *insn)
54e9a19d 2400{
f99652b5
NC
2401 int insn_code, regno, need_extend;
2402 unsigned int mask;
647d790d
DM
2403 rtx cond, reg, qireg, mem;
2404 rtx_insn *and_insn, *load;
ef4bddc2
RS
2405 machine_mode load_mode = QImode;
2406 machine_mode and_mode = QImode;
647d790d 2407 rtx_insn *shift = NULL;
54e9a19d
DD
2408
2409 insn_code = recog_memoized (insn);
2410 if (insn_code != CODE_FOR_cbranchhi
2411 && insn_code != CODE_FOR_cbranchhi_neg)
2412 return;
2413
2414 cond = XVECEXP (PATTERN (insn), 0, 0); /* set */
2415 cond = XEXP (cond, 1); /* if */
2416 cond = XEXP (cond, 0); /* cond */
2417 switch (GET_CODE (cond))
2418 {
2419 case NE:
2420 case EQ:
2421 need_extend = 0;
2422 break;
2423 case LT:
2424 case GE:
2425 need_extend = 1;
2426 break;
2427 default:
2428 return;
2429 }
2430
2431 reg = XEXP (cond, 0);
a21eaf5e 2432 if (! REG_P (reg))
54e9a19d
DD
2433 return;
2434 regno = REGNO (reg);
2435 if (XEXP (cond, 1) != const0_rtx)
2436 return;
2437 if (! find_regno_note (insn, REG_DEAD, regno))
2438 return;
2439 qireg = gen_rtx_REG (QImode, regno);
2440
2441 if (need_extend)
2442 {
569b7f6a 2443 /* LT and GE conditionals should have a sign extend before
54e9a19d 2444 them. */
15f072f9
NC
2445 for (and_insn = prev_real_insn (insn);
2446 and_insn != NULL_RTX;
f1cb6795 2447 and_insn = prev_real_insn (and_insn))
54e9a19d 2448 {
f1cb6795 2449 int and_code = recog_memoized (and_insn);
f99652b5 2450
54e9a19d 2451 if (and_code == CODE_FOR_extendqihi2
f1cb6795
JR
2452 && rtx_equal_p (SET_DEST (PATTERN (and_insn)), reg)
2453 && rtx_equal_p (XEXP (SET_SRC (PATTERN (and_insn)), 0), qireg))
f99652b5 2454 break;
5ab9749e 2455
54e9a19d 2456 if (and_code == CODE_FOR_movhi_internal
f1cb6795 2457 && rtx_equal_p (SET_DEST (PATTERN (and_insn)), reg))
54e9a19d
DD
2458 {
2459 /* This is for testing bit 15. */
f1cb6795 2460 and_insn = insn;
54e9a19d
DD
2461 break;
2462 }
2463
f1cb6795 2464 if (reg_mentioned_p (reg, and_insn))
54e9a19d 2465 return;
f99652b5 2466
b64925dc 2467 if (! NOTE_P (and_insn) && ! NONJUMP_INSN_P (and_insn))
54e9a19d
DD
2468 return;
2469 }
2470 }
2471 else
2472 {
2473 /* EQ and NE conditionals have an AND before them. */
15f072f9
NC
2474 for (and_insn = prev_real_insn (insn);
2475 and_insn != NULL_RTX;
f1cb6795 2476 and_insn = prev_real_insn (and_insn))
54e9a19d 2477 {
f1cb6795
JR
2478 if (recog_memoized (and_insn) == CODE_FOR_andhi3
2479 && rtx_equal_p (SET_DEST (PATTERN (and_insn)), reg)
2480 && rtx_equal_p (XEXP (SET_SRC (PATTERN (and_insn)), 0), reg))
f99652b5 2481 break;
5ab9749e 2482
f1cb6795 2483 if (reg_mentioned_p (reg, and_insn))
54e9a19d 2484 return;
f99652b5 2485
b64925dc 2486 if (! NOTE_P (and_insn) && ! NONJUMP_INSN_P (and_insn))
54e9a19d
DD
2487 return;
2488 }
f99652b5 2489
f1cb6795 2490 if (and_insn)
f99652b5 2491 {
aabcd309 2492 /* Some mis-optimizations by GCC can generate a RIGHT-SHIFT
f99652b5
NC
2493 followed by an AND like this:
2494
2495 (parallel [(set (reg:HI r7) (lshiftrt:HI (reg:HI r7) (const_int 3)))
2496 (clobber (reg:BI carry))]
2497
2498 (set (reg:HI r7) (and:HI (reg:HI r7) (const_int 1)))
5ab9749e 2499
f99652b5 2500 Attempt to detect this here. */
f1cb6795
JR
2501 for (shift = prev_real_insn (and_insn); shift;
2502 shift = prev_real_insn (shift))
f99652b5
NC
2503 {
2504 if (recog_memoized (shift) == CODE_FOR_lshrhi3
2505 && rtx_equal_p (SET_DEST (XVECEXP (PATTERN (shift), 0, 0)), reg)
2506 && rtx_equal_p (XEXP (SET_SRC (XVECEXP (PATTERN (shift), 0, 0)), 0), reg))
2507 break;
5ab9749e 2508
f99652b5 2509 if (reg_mentioned_p (reg, shift)
b64925dc 2510 || (! NOTE_P (shift) && ! NONJUMP_INSN_P (shift)))
f99652b5 2511 {
647d790d 2512 shift = NULL;
f99652b5
NC
2513 break;
2514 }
2515 }
2516 }
54e9a19d 2517 }
15f072f9
NC
2518
2519 if (and_insn == NULL_RTX)
54e9a19d
DD
2520 return;
2521
f1cb6795 2522 for (load = shift ? prev_real_insn (shift) : prev_real_insn (and_insn);
f99652b5
NC
2523 load;
2524 load = prev_real_insn (load))
54e9a19d
DD
2525 {
2526 int load_code = recog_memoized (load);
f99652b5 2527
54e9a19d 2528 if (load_code == CODE_FOR_movhi_internal
f99652b5
NC
2529 && rtx_equal_p (SET_DEST (PATTERN (load)), reg)
2530 && xstormy16_below100_operand (SET_SRC (PATTERN (load)), HImode)
2531 && ! MEM_VOLATILE_P (SET_SRC (PATTERN (load))))
54e9a19d
DD
2532 {
2533 load_mode = HImode;
2534 break;
2535 }
2536
2537 if (load_code == CODE_FOR_movqi_internal
f99652b5
NC
2538 && rtx_equal_p (SET_DEST (PATTERN (load)), qireg)
2539 && xstormy16_below100_operand (SET_SRC (PATTERN (load)), QImode))
54e9a19d
DD
2540 {
2541 load_mode = QImode;
2542 break;
2543 }
f99652b5
NC
2544
2545 if (load_code == CODE_FOR_zero_extendqihi2
2546 && rtx_equal_p (SET_DEST (PATTERN (load)), reg)
2547 && xstormy16_below100_operand (XEXP (SET_SRC (PATTERN (load)), 0), QImode))
2548 {
2549 load_mode = QImode;
2550 and_mode = HImode;
2551 break;
2552 }
2553
54e9a19d
DD
2554 if (reg_mentioned_p (reg, load))
2555 return;
f99652b5 2556
b64925dc 2557 if (! NOTE_P (load) && ! NONJUMP_INSN_P (load))
54e9a19d
DD
2558 return;
2559 }
2560 if (!load)
2561 return;
2562
f99652b5
NC
2563 mem = SET_SRC (PATTERN (load));
2564
2565 if (need_extend)
54e9a19d 2566 {
f99652b5
NC
2567 mask = (load_mode == HImode) ? 0x8000 : 0x80;
2568
2569 /* If the mem includes a zero-extend operation and we are
2570 going to generate a sign-extend operation then move the
2571 mem inside the zero-extend. */
2572 if (GET_CODE (mem) == ZERO_EXTEND)
2573 mem = XEXP (mem, 0);
54e9a19d
DD
2574 }
2575 else
f99652b5 2576 {
f1cb6795
JR
2577 if (!xstormy16_onebit_set_operand (XEXP (SET_SRC (PATTERN (and_insn)), 1),
2578 load_mode))
f99652b5
NC
2579 return;
2580
f1cb6795 2581 mask = (int) INTVAL (XEXP (SET_SRC (PATTERN (and_insn)), 1));
f99652b5
NC
2582
2583 if (shift)
2584 mask <<= INTVAL (XEXP (SET_SRC (XVECEXP (PATTERN (shift), 0, 0)), 1));
2585 }
54e9a19d 2586
54e9a19d
DD
2587 if (load_mode == HImode)
2588 {
2589 rtx addr = XEXP (mem, 0);
f99652b5 2590
54e9a19d
DD
2591 if (! (mask & 0xff))
2592 {
0a81f074 2593 addr = plus_constant (Pmode, addr, 1);
54e9a19d
DD
2594 mask >>= 8;
2595 }
2596 mem = gen_rtx_MEM (QImode, addr);
2597 }
2598
2599 if (need_extend)
2600 XEXP (cond, 0) = gen_rtx_SIGN_EXTEND (HImode, mem);
2601 else
f99652b5
NC
2602 XEXP (cond, 0) = gen_rtx_AND (and_mode, mem, GEN_INT (mask));
2603
54e9a19d
DD
2604 INSN_CODE (insn) = -1;
2605 delete_insn (load);
f99652b5 2606
f1cb6795
JR
2607 if (and_insn != insn)
2608 delete_insn (and_insn);
f99652b5
NC
2609
2610 if (shift != NULL_RTX)
2611 delete_insn (shift);
54e9a19d
DD
2612}
2613
2614static void
2615xstormy16_reorg (void)
2616{
b32d5189 2617 rtx_insn *insn;
54e9a19d
DD
2618
2619 for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
2620 {
2621 if (! JUMP_P (insn))
2622 continue;
2623 combine_bnp (insn);
2624 }
2625}
7e43c821 2626\f
78bc94a2
KH
2627/* Worker function for TARGET_RETURN_IN_MEMORY. */
2628
7e43c821 2629static bool
586de218 2630xstormy16_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
7e43c821 2631{
586de218 2632 const HOST_WIDE_INT size = int_size_in_bytes (type);
78bc94a2 2633 return (size == -1 || size > UNITS_PER_WORD * NUM_ARGUMENT_REGISTERS);
7e43c821 2634}
f939c3e6
RS
2635
2636/* Implement TARGET_HARD_REGNO_MODE_OK. */
2637
2638static bool
2639xstormy16_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
2640{
2641 return regno != 16 || mode == BImode;
2642}
99e1629f
RS
2643
2644/* Implement TARGET_MODES_TIEABLE_P. */
2645
2646static bool
2647xstormy16_modes_tieable_p (machine_mode mode1, machine_mode mode2)
2648{
2649 return mode1 != BImode && mode2 != BImode;
2650}
7b4df2bf
RS
2651
2652/* Implement PUSH_ROUNDING. */
2653
2654poly_int64
2655xstormy16_push_rounding (poly_int64 bytes)
2656{
2657 return (bytes + 1) & ~1;
2658}
4b58290f 2659\f
5ab9749e 2660#undef TARGET_ASM_ALIGNED_HI_OP
301d03af 2661#define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
5ab9749e 2662#undef TARGET_ASM_ALIGNED_SI_OP
301d03af 2663#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
5ab9749e 2664#undef TARGET_ENCODE_SECTION_INFO
54e9a19d 2665#define TARGET_ENCODE_SECTION_INFO xstormy16_encode_section_info
301d03af 2666
5ab9749e 2667/* Select_section doesn't handle .bss_below100. */
434aeebb
RS
2668#undef TARGET_HAVE_SWITCHABLE_BSS_SECTIONS
2669#define TARGET_HAVE_SWITCHABLE_BSS_SECTIONS false
2670
5ab9749e 2671#undef TARGET_ASM_OUTPUT_MI_THUNK
c590b625 2672#define TARGET_ASM_OUTPUT_MI_THUNK xstormy16_asm_output_mi_thunk
5ab9749e 2673#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
3961e8fe 2674#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
c590b625 2675
43070a6e
AS
2676#undef TARGET_PRINT_OPERAND
2677#define TARGET_PRINT_OPERAND xstormy16_print_operand
2678#undef TARGET_PRINT_OPERAND_ADDRESS
2679#define TARGET_PRINT_OPERAND_ADDRESS xstormy16_print_operand_address
2680
6b1ce545
AS
2681#undef TARGET_MEMORY_MOVE_COST
2682#define TARGET_MEMORY_MOVE_COST xstormy16_memory_move_cost
5ab9749e 2683#undef TARGET_RTX_COSTS
3c50106f 2684#define TARGET_RTX_COSTS xstormy16_rtx_costs
5ab9749e 2685#undef TARGET_ADDRESS_COST
dcefdf67 2686#define TARGET_ADDRESS_COST xstormy16_address_cost
3c50106f 2687
5ab9749e 2688#undef TARGET_BUILD_BUILTIN_VA_LIST
f2f61ee7 2689#define TARGET_BUILD_BUILTIN_VA_LIST xstormy16_build_builtin_va_list
5ab9749e 2690#undef TARGET_EXPAND_BUILTIN_VA_START
d7bd8aeb 2691#define TARGET_EXPAND_BUILTIN_VA_START xstormy16_expand_builtin_va_start
5ab9749e 2692#undef TARGET_GIMPLIFY_VA_ARG_EXPR
f84fe9b6 2693#define TARGET_GIMPLIFY_VA_ARG_EXPR xstormy16_gimplify_va_arg_expr
37cd4bca 2694
cde0f3fd
PB
2695#undef TARGET_PROMOTE_FUNCTION_MODE
2696#define TARGET_PROMOTE_FUNCTION_MODE default_promote_function_mode_always_promote
5ab9749e 2697#undef TARGET_PROMOTE_PROTOTYPES
586de218 2698#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
7e43c821 2699
bf425ddd
NF
2700#undef TARGET_FUNCTION_ARG
2701#define TARGET_FUNCTION_ARG xstormy16_function_arg
2702#undef TARGET_FUNCTION_ARG_ADVANCE
2703#define TARGET_FUNCTION_ARG_ADVANCE xstormy16_function_arg_advance
2704
5ab9749e 2705#undef TARGET_RETURN_IN_MEMORY
7e43c821 2706#define TARGET_RETURN_IN_MEMORY xstormy16_return_in_memory
998871e9
AS
2707#undef TARGET_FUNCTION_VALUE
2708#define TARGET_FUNCTION_VALUE xstormy16_function_value
2709#undef TARGET_LIBCALL_VALUE
2710#define TARGET_LIBCALL_VALUE xstormy16_libcall_value
2711#undef TARGET_FUNCTION_VALUE_REGNO_P
2712#define TARGET_FUNCTION_VALUE_REGNO_P xstormy16_function_value_regno_p
7e43c821 2713
5ab9749e 2714#undef TARGET_MACHINE_DEPENDENT_REORG
54e9a19d
DD
2715#define TARGET_MACHINE_DEPENDENT_REORG xstormy16_reorg
2716
ef795fc2
AS
2717#undef TARGET_PREFERRED_RELOAD_CLASS
2718#define TARGET_PREFERRED_RELOAD_CLASS xstormy16_preferred_reload_class
2719#undef TARGET_PREFERRED_OUTPUT_RELOAD_CLASS
2720#define TARGET_PREFERRED_OUTPUT_RELOAD_CLASS xstormy16_preferred_reload_class
2721
d81db636
SB
2722#undef TARGET_LRA_P
2723#define TARGET_LRA_P hook_bool_void_false
2724
c6c3dba9
PB
2725#undef TARGET_LEGITIMATE_ADDRESS_P
2726#define TARGET_LEGITIMATE_ADDRESS_P xstormy16_legitimate_address_p
192997cf
AS
2727#undef TARGET_MODE_DEPENDENT_ADDRESS_P
2728#define TARGET_MODE_DEPENDENT_ADDRESS_P xstormy16_mode_dependent_address_p
c6c3dba9 2729
7b5cbb57
AS
2730#undef TARGET_CAN_ELIMINATE
2731#define TARGET_CAN_ELIMINATE xstormy16_can_eliminate
2732
fb8d0fac
RH
2733#undef TARGET_TRAMPOLINE_INIT
2734#define TARGET_TRAMPOLINE_INIT xstormy16_trampoline_init
2735
f939c3e6
RS
2736#undef TARGET_HARD_REGNO_MODE_OK
2737#define TARGET_HARD_REGNO_MODE_OK xstormy16_hard_regno_mode_ok
99e1629f
RS
2738#undef TARGET_MODES_TIEABLE_P
2739#define TARGET_MODES_TIEABLE_P xstormy16_modes_tieable_p
f939c3e6 2740
58e17cf8
RS
2741#undef TARGET_CONSTANT_ALIGNMENT
2742#define TARGET_CONSTANT_ALIGNMENT constant_alignment_word_strings
2743
01557bd4
NC
2744#undef TARGET_HAVE_SPECULATION_SAFE_VALUE
2745#define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed
2746
4b58290f 2747struct gcc_target targetm = TARGET_INITIALIZER;
d6b5193b
RS
2748
2749#include "gt-stormy16.h"