]>
Commit | Line | Data |
---|---|---|
bc45ade3 | 1 | /* Output routines for GCC for Hitachi Super-H |
0d7e008e | 2 | Copyright (C) 1993, 1994 Free Software Foundation, Inc. |
bc45ade3 | 3 | |
0d7e008e | 4 | This file is part of GNU CC. |
bc45ade3 | 5 | |
0d7e008e SC |
6 | GNU CC is free software; you can redistribute it and/or modify |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 2, or (at your option) | |
9 | any later version. | |
bc45ade3 | 10 | |
0d7e008e SC |
11 | GNU CC is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
bc45ade3 | 15 | |
0d7e008e SC |
16 | You should have received a copy of the GNU General Public License |
17 | along with GNU CC; see the file COPYING. If not, write to | |
18 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
bc45ade3 SC |
19 | |
20 | ||
21 | /* Contributed by Steve Chamberlain (sac@cygnus.com) */ | |
22 | ||
23 | #include <stdio.h> | |
24 | #include "assert.h" | |
25 | #include "config.h" | |
26 | #include "rtl.h" | |
27 | #include "regs.h" | |
28 | #include "hard-reg-set.h" | |
29 | #include "real.h" | |
30 | #include "insn-config.h" | |
31 | #include "conditions.h" | |
32 | #include "insn-flags.h" | |
33 | #include "tree.h" | |
34 | #include "output.h" | |
0d7e008e | 35 | |
bc45ade3 SC |
36 | #include "insn-attr.h" |
37 | #include "flags.h" | |
38 | #include "obstack.h" | |
39 | #include "expr.h" | |
40 | ||
0d7e008e | 41 | static rtx add_constant (); |
bc45ade3 | 42 | |
0d7e008e SC |
43 | int pragma_interrupt; |
44 | int pragma_trapa; | |
bc45ade3 SC |
45 | |
46 | int current_function_anonymous_args; | |
47 | extern int current_function_pretend_args_size; | |
b9654711 SC |
48 | extern char *version_string; |
49 | extern int flag_traditional; | |
50 | ||
0d7e008e SC |
51 | static rtx shiftsyms[32]; |
52 | struct rtx_def *table_lab; | |
b9654711 | 53 | enum attr_cpu sh_cpu; /* target cpu */ |
bc45ade3 SC |
54 | |
55 | /* Global variables for machine-dependent things. */ | |
56 | ||
57 | /* Saved operands from the last compare to use when we generate an scc | |
0d7e008e | 58 | or bcc insn. */ |
bc45ade3 SC |
59 | |
60 | rtx sh_compare_op0; | |
61 | rtx sh_compare_op1; | |
62 | ||
63 | /* Provides the class number of the smallest class containing | |
64 | reg number */ | |
65 | ||
66 | int regno_reg_class[FIRST_PSEUDO_REGISTER] = | |
67 | { | |
0d7e008e | 68 | R0_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, |
bc45ade3 SC |
69 | GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, |
70 | GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, | |
71 | GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, | |
0d7e008e SC |
72 | GENERAL_REGS, PR_REGS, T_REGS, NO_REGS, |
73 | MAC_REGS, MAC_REGS, | |
bc45ade3 SC |
74 | }; |
75 | ||
76 | /* Provide reg_class from a letter such as appears in the machine | |
77 | description. */ | |
78 | ||
79 | enum reg_class reg_class_from_letter[] = | |
80 | { | |
81 | /* a */ NO_REGS, /* b */ NO_REGS, /* c */ NO_REGS, /* d */ NO_REGS, | |
82 | /* e */ NO_REGS, /* f */ NO_REGS, /* g */ NO_REGS, /* h */ NO_REGS, | |
83 | /* i */ NO_REGS, /* j */ NO_REGS, /* k */ NO_REGS, /* l */ PR_REGS, | |
84 | /* m */ NO_REGS, /* n */ NO_REGS, /* o */ NO_REGS, /* p */ NO_REGS, | |
85 | /* q */ NO_REGS, /* r */ NO_REGS, /* s */ NO_REGS, /* t */ T_REGS, | |
86 | /* u */ NO_REGS, /* v */ NO_REGS, /* w */ NO_REGS, /* x */ MAC_REGS, | |
87 | /* y */ NO_REGS, /* z */ R0_REGS | |
88 | }; | |
89 | ||
0d7e008e SC |
90 | /* Value is 1 if register/mode pair is acceptable on SH. Even |
91 | registers can hold DIs and DF values. The rest can only hold | |
92 | SI's efficiently */ | |
93 | ||
94 | ||
95 | #define REG_ODD \ | |
96 | ( (1 << (int) QImode) | (1 << (int) HImode) | (1 << (int) SImode) \ | |
97 | | (1 << (int) QFmode) | (1 << (int) HFmode) | (1 << (int) SFmode) \ | |
d3ae8277 | 98 | | (1 << (int) CQImode) | (1 << (int) CHImode)| (1<< (int)DFmode) | (1<<(int)DImode)) |
bc45ade3 | 99 | |
0d7e008e | 100 | #define REG_EVEN \ |
d3ae8277 | 101 | (REG_ODD | (1 << (int) CSImode) | (1 << (int) SCmode)) |
b9654711 | 102 | |
0d7e008e | 103 | #define SI_ONLY (1<<(int)SImode) |
d3ae8277 | 104 | |
0d7e008e SC |
105 | int hard_regno_mode_ok[] = |
106 | { | |
107 | REG_EVEN, REG_ODD, REG_EVEN, REG_ODD, | |
108 | REG_EVEN, REG_ODD, REG_EVEN, REG_ODD, | |
109 | REG_EVEN, REG_ODD, REG_EVEN, REG_ODD, | |
110 | REG_EVEN, REG_ODD, REG_EVEN, REG_ODD, | |
111 | REG, 0, SI_ONLY, SI_ONLY, | |
112 | SI_ONLY, SI_ONLY | |
113 | }; | |
b9654711 | 114 | |
bc45ade3 SC |
115 | /* Local label counter, used for constants in the pool and inside |
116 | pattern branches. */ | |
a9f71ad8 | 117 | static int lf = 100; |
bc45ade3 | 118 | |
0d7e008e SC |
119 | |
120 | /* Number of bytes pushed for anonymous args, used to pass information | |
121 | between expand_prologue and expand_epilogue. */ | |
122 | static int extra_push; | |
123 | ||
bc45ade3 | 124 | \f |
bc45ade3 | 125 | |
b9654711 SC |
126 | void |
127 | push (rn) | |
0d7e008e | 128 | int rn; |
bc45ade3 | 129 | { |
b9654711 | 130 | emit_insn (gen_push (gen_rtx (REG, SImode, rn))); |
bc45ade3 SC |
131 | } |
132 | ||
b9654711 SC |
133 | void |
134 | pop (rn) | |
bc45ade3 | 135 | { |
b9654711 | 136 | emit_insn (gen_pop (gen_rtx (REG, SImode, rn))); |
bc45ade3 SC |
137 | } |
138 | ||
139 | ||
b9654711 | 140 | /* Adjust the stack and return the number of bytes taken to do it */ |
bc45ade3 | 141 | |
b9654711 | 142 | static void |
0d7e008e | 143 | output_stack_adjust (size) |
b9654711 | 144 | int size; |
bc45ade3 | 145 | { |
b9654711 | 146 | if (size) |
bc45ade3 | 147 | { |
b9654711 SC |
148 | rtx val = GEN_INT (size); |
149 | rtx insn; | |
bc45ade3 | 150 | |
0d7e008e | 151 | if (!CONST_OK_FOR_I (size)) |
b9654711 | 152 | { |
0d7e008e | 153 | rtx nval = gen_rtx (REG, SImode, 3); |
b9654711 SC |
154 | emit_insn (gen_movsi (nval, val)); |
155 | val = nval; | |
156 | } | |
bc45ade3 | 157 | |
0d7e008e | 158 | insn = gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, val); |
b9654711 SC |
159 | emit_insn (insn); |
160 | } | |
bc45ade3 SC |
161 | } |
162 | ||
b9654711 SC |
163 | /* Generate code to push the regs specified in the mask, and return |
164 | the number of bytes the insns take. */ | |
bc45ade3 SC |
165 | |
166 | static void | |
b9654711 SC |
167 | push_regs (mask) |
168 | int mask; | |
bc45ade3 SC |
169 | { |
170 | int i; | |
171 | ||
b9654711 | 172 | for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
bc45ade3 | 173 | { |
b9654711 | 174 | if (mask & (1 << i)) |
bc45ade3 | 175 | { |
b9654711 | 176 | push (i); |
bc45ade3 SC |
177 | } |
178 | } | |
179 | } | |
180 | ||
bc45ade3 | 181 | |
0d7e008e SC |
182 | /* Print an instruction which would have gone into a delay slot after |
183 | an instructiuon, but couldn't because the instruction expanded into a | |
184 | sequence where putting the slot insn at the end wouldn't work. */ | |
bc45ade3 | 185 | |
0d7e008e | 186 | static void |
b9654711 SC |
187 | print_slot (insn) |
188 | rtx insn; | |
189 | { | |
190 | final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file, optimize, 0, 1); | |
bc45ade3 | 191 | |
b9654711 | 192 | INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1; |
bc45ade3 SC |
193 | } |
194 | ||
bc45ade3 SC |
195 | |
196 | /* Work out the registers which need to be saved, both as a mask and a | |
0d7e008e | 197 | count. |
bc45ade3 | 198 | |
0d7e008e SC |
199 | If doing a pragma interrupt function, then push all regs used by the function, |
200 | and if we call another function (we can tell by looking at PR), make sure that all the | |
201 | regs it clobbers are safe too. | |
202 | */ | |
203 | static int | |
204 | calc_live_regs (count_ptr) | |
205 | int *count_ptr; | |
bc45ade3 SC |
206 | { |
207 | int reg; | |
208 | int live_regs_mask = 0; | |
0d7e008e | 209 | int count = 0; |
bc45ade3 SC |
210 | for (reg = 0; reg < FIRST_PSEUDO_REGISTER; reg++) |
211 | { | |
0d7e008e SC |
212 | if (reg == ARG_POINTER_REGNUM) |
213 | continue; | |
214 | if (reg == T_REG) | |
215 | continue; | |
216 | if (reg == GBR_REG) | |
217 | continue; | |
218 | ||
219 | if (pragma_interrupt && !pragma_trapa) | |
220 | { | |
221 | /* Need to save all the regs ever live */ | |
222 | if ((regs_ever_live[reg] | |
223 | || (call_used_regs[reg] && regs_ever_live[PR_REG])) | |
224 | && reg != 15) | |
225 | { | |
226 | live_regs_mask |= 1 << reg; | |
227 | count++; | |
228 | } | |
229 | } | |
230 | else if (TARGET_SMALLCALL) | |
231 | { | |
232 | /* Don't need to push anthing, but count the regs which have | |
233 | been pushed by the wrapper */ | |
234 | if (call_used_regs[reg]) | |
235 | count++; | |
236 | } | |
237 | else | |
bc45ade3 | 238 | { |
0d7e008e SC |
239 | /* Only push those regs which are used and need to be saved */ |
240 | if (regs_ever_live[reg] && !call_used_regs[reg]) | |
241 | { | |
242 | count++; | |
243 | live_regs_mask |= (1 << reg); | |
244 | } | |
bc45ade3 SC |
245 | } |
246 | } | |
0d7e008e SC |
247 | |
248 | ||
249 | *count_ptr = count; | |
bc45ade3 SC |
250 | return live_regs_mask; |
251 | } | |
b9654711 | 252 | \f |
bc45ade3 | 253 | |
b9654711 SC |
254 | static int |
255 | need_slot (insn) | |
256 | rtx insn; | |
bc45ade3 | 257 | { |
b9654711 | 258 | return (insn && !INSN_ANNULLED_BRANCH_P (XVECEXP (insn, 0, 0))); |
bc45ade3 | 259 | } |
b9654711 | 260 | |
bc45ade3 SC |
261 | /* Print the operand address in x to the stream */ |
262 | ||
263 | void | |
264 | print_operand_address (stream, x) | |
265 | FILE *stream; | |
266 | rtx x; | |
267 | { | |
268 | switch (GET_CODE (x)) | |
269 | { | |
270 | case REG: | |
271 | fprintf (stream, "@%s", reg_names[REGNO (x)]); | |
272 | break; | |
bc45ade3 SC |
273 | case PLUS: |
274 | { | |
275 | rtx base = XEXP (x, 0); | |
276 | rtx index = XEXP (x, 1); | |
277 | ||
278 | if (GET_CODE (base) != REG) | |
279 | { | |
280 | /* Ensure that BASE is a register (one of them must be). */ | |
281 | rtx temp = base; | |
282 | base = index; | |
283 | index = temp; | |
284 | } | |
285 | ||
286 | switch (GET_CODE (index)) | |
287 | { | |
288 | case CONST_INT: | |
289 | fprintf (stream, "@(%d,%s)", | |
290 | INTVAL (index), | |
291 | reg_names[REGNO (base)]); | |
292 | break; | |
293 | ||
294 | case REG: | |
b9654711 SC |
295 | fprintf (stream, "@(r0,%s)", |
296 | reg_names[MAX (REGNO (base), REGNO (index))]); | |
297 | ||
bc45ade3 SC |
298 | break; |
299 | ||
300 | default: | |
b9654711 SC |
301 | debug_rtx (x); |
302 | ||
bc45ade3 SC |
303 | abort (); |
304 | } | |
305 | } | |
306 | ||
307 | break; | |
308 | case PRE_DEC: | |
309 | fprintf (stream, "@-%s", reg_names[REGNO (XEXP (x, 0))]); | |
310 | break; | |
311 | ||
312 | case POST_INC: | |
313 | fprintf (stream, "@%s+", reg_names[REGNO (XEXP (x, 0))]); | |
314 | break; | |
315 | ||
316 | default: | |
317 | output_addr_const (stream, x); | |
318 | break; | |
319 | } | |
320 | } | |
321 | ||
322 | /* Print operand x (an rtx) in assembler syntax to file stream | |
323 | according to modifier code. | |
324 | ||
b9654711 SC |
325 | '.' print a .s if insn needs delay slot |
326 | '*' print a local label | |
327 | '^' increment the local label number | |
328 | '!' dump the constant table | |
329 | '#' output a nop if there is nothing to put in the delay slot | |
d3ae8277 | 330 | '@' print rte or rts depending upon pragma interruptness |
b9654711 SC |
331 | 'R' print the next register or memory location along, ie the lsw in |
332 | a double word value | |
333 | 'O' print a constant without the # | |
334 | 'M' print a constant as its negative | |
0d7e008e | 335 | 'N' print insides of a @++ or @-- o */ |
bc45ade3 SC |
336 | |
337 | void | |
338 | print_operand (stream, x, code) | |
339 | FILE *stream; | |
340 | rtx x; | |
341 | int code; | |
342 | { | |
343 | switch (code) | |
344 | { | |
b9654711 SC |
345 | case '.': |
346 | if (need_slot (final_sequence)) | |
347 | fprintf (stream, ".s"); | |
348 | break; | |
bc45ade3 SC |
349 | case '*': |
350 | fprintf (stream, "LF%d", lf); | |
351 | break; | |
bc45ade3 SC |
352 | case '^': |
353 | lf++; | |
354 | break; | |
d3ae8277 SC |
355 | case '@': |
356 | if (pragma_interrupt) | |
357 | fprintf (stream,"rte"); | |
358 | else | |
359 | fprintf (stream,"rts"); | |
360 | break; | |
bc45ade3 SC |
361 | case '#': |
362 | /* Output a nop if there's nothing in the delay slot */ | |
363 | if (dbr_sequence_length () == 0) | |
364 | { | |
d3ae8277 | 365 | fprintf (stream, "\n\tnop"); |
bc45ade3 SC |
366 | } |
367 | break; | |
b9654711 | 368 | case 'O': |
0d7e008e | 369 | output_addr_const (stream, x); |
bc45ade3 | 370 | break; |
b9654711 SC |
371 | case 'M': |
372 | fprintf (asm_out_file, "#%d", -INTVAL (x)); | |
373 | break; | |
0d7e008e SC |
374 | case 'N': |
375 | fputs (reg_names[REGNO (XEXP (XEXP (x, 0), 0))], (stream)); | |
376 | break; | |
bc45ade3 | 377 | case 'R': |
0d7e008e | 378 | /* Next location along in memory or register */ |
bc45ade3 SC |
379 | switch (GET_CODE (x)) |
380 | { | |
381 | case REG: | |
382 | fputs (reg_names[REGNO (x) + 1], (stream)); | |
383 | break; | |
384 | case MEM: | |
0d7e008e | 385 | print_operand_address (stream, XEXP (adj_offsettable_operand (x, 4), 0)); |
bc45ade3 SC |
386 | break; |
387 | } | |
388 | break; | |
bc45ade3 SC |
389 | default: |
390 | switch (GET_CODE (x)) | |
391 | { | |
392 | case REG: | |
393 | fputs (reg_names[REGNO (x)], (stream)); | |
394 | break; | |
395 | case MEM: | |
396 | output_address (XEXP (x, 0)); | |
397 | break; | |
398 | default: | |
399 | fputc ('#', stream); | |
400 | output_addr_const (stream, x); | |
401 | break; | |
bc45ade3 SC |
402 | } |
403 | break; | |
404 | } | |
405 | } | |
bc45ade3 SC |
406 | \f |
407 | ||
0d7e008e SC |
408 | sextb (x) |
409 | { | |
410 | x &= 0xff; | |
411 | if (x > 127) | |
412 | { | |
413 | x = -256 + x; | |
414 | } | |
415 | return x; | |
416 | } | |
417 | ||
b9654711 | 418 | |
bc45ade3 | 419 | |
0d7e008e SC |
420 | /* Take a move with integer constant source in OPERANDS, see if it can be generated by |
421 | devious shifting. If so, generate the instruction sequence and return 1, otherwise | |
422 | return 0. | |
423 | ||
424 | OPERANDS[0] Destination register | |
425 | OPERANDS[1] Source constant | |
426 | ||
427 | 00000000 00000000 00000000 0NNNNNNNN simple load | |
428 | 00000000 00000000 00000000 NNNNNNNN0 load and shift by 1 | |
429 | 00000000 00000000 0000000N NNNNNNN00 load and shift by 2 | |
430 | 00000000 00000000 0NNNNNNN 000000000 load and shift by 8 | |
431 | 00000000 0NNNNNNN 00000000 000000000 load and shift by 16 | |
432 | N0000000 00000000 00000000 00NNNNNNN load and rotate right | |
433 | ||
434 | 11111111 11111111 11111111 1NNNNNNNN simple load | |
435 | 11111111 11111111 11111111 NNNNNNNN0 load and shift by 1 | |
436 | 11111111 11111111 1111111N NNNNNNN00 load and shift by 2 | |
437 | 11111111 11111111 1NNNNNNN 000000000 load and shift by 8 | |
438 | 11111111 1NNNNNNN 00000000 000000000 load and shift by 16 | |
439 | N1111111 11111111 11111111 11NNNNNNN load and rotate right | |
440 | ||
441 | 00000000 00000000 00000000 1NNNNNNNN load and zero extend byte | |
442 | 00000000 00000000 11111111 1NNNNNNNN load and zero extend word | |
443 | ||
444 | ||
445 | */ | |
446 | ||
447 | static int | |
448 | synth_constant (operands, mode) | |
449 | rtx operands[]; | |
450 | enum machine_mode mode; | |
bc45ade3 | 451 | { |
0d7e008e SC |
452 | rtx dst; |
453 | int i = INTVAL (operands[1]) & 0xffffffff; | |
454 | ||
d3ae8277 | 455 | if (CONST_OK_FOR_I (i)) |
0d7e008e | 456 | return 0; |
bc45ade3 | 457 | |
d3ae8277 SC |
458 | if (TARGET_CLEN0 && mode != QImode) |
459 | return 0; | |
460 | ||
461 | if (mode != SImode) | |
462 | { | |
463 | if (reload_in_progress) | |
464 | return 0; | |
465 | dst = gen_reg_rtx (SImode); | |
466 | } | |
467 | else | |
468 | { | |
469 | dst = operands[0]; | |
470 | } | |
bc45ade3 | 471 | |
0d7e008e SC |
472 | /* 00000000 00000000 11111111 1NNNNNNNN load and zero extend word */ |
473 | if ((i & 0xffffff80) == 0x0000ff80) | |
474 | { | |
475 | emit_move_insn (dst, GEN_INT (sextb (i))); | |
476 | emit_insn (gen_and_ffff (dst, dst)); | |
477 | } | |
478 | /* 00000000 00000000 00000000 1NNNNNNNN load and zero extend byte */ | |
479 | else if ((i & 0xffffff80) == 0x00000080) | |
bc45ade3 | 480 | { |
0d7e008e SC |
481 | emit_move_insn (dst, GEN_INT (sextb (i))); |
482 | emit_insn (gen_and_ff (dst, dst)); | |
bc45ade3 | 483 | } |
0d7e008e SC |
484 | /* 00000000 00000000 00000000 NNNNNNNN0 load and shift by 1 |
485 | 11111111 11111111 11111111 NNNNNNNN0 load and shift by 1 */ | |
486 | else if ((i & 0xffffff01) == 0 | |
487 | || (i & 0xffffff01) == 0xffffff00) | |
488 | { | |
489 | emit_move_insn (dst, GEN_INT (sextb (i >> 1))); | |
490 | emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (1))); | |
491 | } | |
492 | /* 00000000 00000000 0000000N NNNNNNN00 load and shift by 2 | |
493 | 11111111 11111111 1111111N NNNNNNN00 load and shift by 2*/ | |
494 | else if ((i & 0xfffffe03) == 0 | |
495 | || (i & 0xfffffe03) == 0xfffffe00) | |
496 | { | |
497 | emit_move_insn (dst, GEN_INT (sextb (i >> 2))); | |
498 | emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (2))); | |
499 | } | |
500 | /* 00000000 00000000 0NNNNNNN 000000000 load and shift by 8 | |
501 | 11111111 11111111 1NNNNNNN 000000000 load and shift by 8 */ | |
bc45ade3 | 502 | |
0d7e008e SC |
503 | else if ((i & 0xffff80ff) == 0 |
504 | || (i & 0xffff80ff) == 0xffff8000) | |
bc45ade3 | 505 | { |
0d7e008e SC |
506 | emit_move_insn (dst, GEN_INT (sextb (i >> 8))); |
507 | emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (8))); | |
508 | } | |
509 | /* 00000000 0NNNNNNN 00000000 000000000 load and shift by 16 | |
510 | 11111111 1NNNNNNN 00000000 000000000 load and shift by 16 */ | |
511 | else if ((i & 0xff80ffff) == 0 | |
512 | || (i & 0xff80ffff) == 0xff80ffff) | |
513 | { | |
514 | emit_move_insn (dst, GEN_INT (sextb (i >> 16))); | |
515 | emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (16))); | |
bc45ade3 | 516 | } |
0d7e008e SC |
517 | /* 00000000 00000000 0NNNNNNN 0NNNNNNNN load shift 8 and add */ |
518 | else if ((i & 0xffff8080) == 0 && TARGET_CLEN3) | |
519 | { | |
520 | emit_move_insn (dst, GEN_INT (sextb (i >> 8))); | |
521 | emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (8))); | |
522 | emit_insn (gen_addsi3 (dst, dst, GEN_INT (i & 0x7f))); | |
523 | } | |
524 | else | |
525 | return 0; | |
bc45ade3 | 526 | |
0d7e008e | 527 | if (mode != SImode) |
bc45ade3 | 528 | { |
0d7e008e SC |
529 | emit_insn (gen_rtx (SET, VOIDmode, operands[0], |
530 | gen_rtx (SUBREG, mode, dst, 0))); | |
531 | ||
bc45ade3 | 532 | } |
0d7e008e | 533 | return 1; |
bc45ade3 SC |
534 | } |
535 | ||
bc45ade3 | 536 | |
0d7e008e SC |
537 | /* Emit code to perform a block move. Choose the best method. |
538 | ||
539 | OPERANDS[0] is the destination. | |
540 | OPERANDS[1] is the source. | |
541 | OPERANDS[2] is the size. | |
542 | OPERANDS[3] is the alignment safe to use. */ | |
543 | ||
544 | ||
545 | int | |
546 | expand_block_move (operands) | |
547 | rtx *operands; | |
548 | { | |
549 | int align = INTVAL (operands[3]); | |
550 | int constp = (GET_CODE (operands[2]) == CONST_INT); | |
551 | int bytes = (constp ? INTVAL (operands[2]) : 0); | |
552 | enum machine_mode mode; | |
d3ae8277 | 553 | |
0d7e008e SC |
554 | /* IF odd then fail */ |
555 | if (!constp || bytes <= 0) | |
556 | return 0; | |
557 | ||
d3ae8277 SC |
558 | /* Don't expand if we'd make the code bigger and we don't want big code */ |
559 | ||
560 | if (bytes > 8 && TARGET_SMALLCODE) | |
561 | return 0; | |
562 | ||
0d7e008e SC |
563 | switch (align) |
564 | { | |
565 | case 1: | |
566 | mode = QImode; | |
567 | break; | |
568 | case 2: | |
569 | mode = HImode; | |
570 | break; | |
571 | default: | |
572 | mode = SImode; | |
573 | align = 4; | |
574 | } | |
d3ae8277 | 575 | |
0d7e008e SC |
576 | if (mode == SImode && constp && bytes < 64 && (bytes % 4 == 0)) |
577 | { | |
578 | char entry[30]; | |
579 | tree entry_name; | |
580 | rtx func_addr_rtx; | |
581 | rtx r4 = gen_rtx (REG, SImode, 4); | |
582 | rtx r5 = gen_rtx (REG, SImode, 5); | |
583 | sprintf (entry, "__movstr%s%d", GET_MODE_NAME (mode), bytes); | |
584 | entry_name = get_identifier (entry); | |
585 | ||
586 | func_addr_rtx = copy_to_mode_reg (Pmode, | |
587 | gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (entry_name))); | |
588 | emit_insn (gen_move_insn (r4, XEXP (operands[0], 0))); | |
589 | emit_insn (gen_move_insn (r5, XEXP (operands[1], 0))); | |
590 | emit_insn (gen_block_move_real (func_addr_rtx)); | |
591 | return 1; | |
592 | } | |
593 | if (mode == SImode && constp && (bytes % 4 == 0)) | |
594 | { | |
595 | char entry[30]; | |
596 | tree entry_name; | |
597 | rtx func_addr_rtx; | |
598 | int groups; | |
599 | rtx r4 = gen_rtx (REG, SImode, 4); | |
600 | rtx r5 = gen_rtx (REG, SImode, 5); | |
601 | rtx r6 = gen_rtx (REG, SImode, 6); | |
602 | entry_name = get_identifier ("__movstr"); | |
603 | ||
604 | func_addr_rtx = copy_to_mode_reg (Pmode, | |
605 | gen_rtx (SYMBOL_REF, Pmode, | |
606 | IDENTIFIER_POINTER (entry_name))); | |
607 | emit_insn (gen_move_insn (r4, XEXP (operands[0], 0))); | |
608 | emit_insn (gen_move_insn (r5, XEXP (operands[1], 0))); | |
609 | ||
610 | /* r6 controls the size of the move, 16 is decremented from it | |
611 | for each 64 bytes moved, then the -ve bit is used as an index into a | |
612 | list of move instructions like this: | |
613 | ||
614 | { | |
615 | do { | |
616 | *dst++ = *src++; | |
617 | *dst++ = *src++; | |
618 | *dst++ = *src++; | |
619 | ..etc.. 16 in all | |
620 | *dst++ = *src++; | |
621 | *dst++ = *src++; | |
622 | size -= 16; | |
623 | } while (size > 0); | |
624 | ||
625 | switch (size) | |
626 | { | |
627 | case -15: | |
628 | *dst++ = *src++; | |
629 | case -14: | |
630 | *dst++ = *src++; | |
631 | .. etc.. ; | |
632 | case -2: | |
633 | *dst++ = *src++; | |
634 | case -1: | |
635 | *dst++ = *src++; | |
636 | case 0: | |
637 | ; | |
638 | } | |
639 | } | |
640 | ||
641 | eg, a 72 byte move would be set up with size(r6) = 14, for one | |
642 | iteration through the big while loop, and a switch of -2 for the last part */ | |
643 | ||
644 | { | |
645 | int final_switch = 16 - ((bytes / 4) % 16); | |
646 | int while_loop = ((bytes / 4) / 16 - 1) * 16; | |
647 | emit_insn (gen_move_insn (r6, GEN_INT (while_loop + final_switch))); | |
648 | emit_insn (gen_block_lump_real (func_addr_rtx)); | |
649 | return 1; | |
650 | } | |
651 | } | |
0d7e008e | 652 | |
d3ae8277 | 653 | return 0; |
0d7e008e SC |
654 | } |
655 | ||
bc45ade3 | 656 | /* Prepare operands for a move define_expand; specifically, one of the |
b9654711 SC |
657 | operands must be in a register. Take this chance to remove |
658 | addressing modes which can't be coped with very well. */ | |
bc45ade3 | 659 | |
b9654711 | 660 | int |
bc45ade3 SC |
661 | prepare_move_operands (operands, mode) |
662 | rtx operands[]; | |
663 | enum machine_mode mode; | |
664 | { | |
0d7e008e SC |
665 | if (!(reload_in_progress || reload_completed) |
666 | && ((!register_operand (operands[0], mode) | |
667 | && !register_operand (operands[1], mode)) | |
668 | || GET_CODE (operands[1]) == PLUS)) | |
bc45ade3 SC |
669 | { |
670 | /* copy the source to a register */ | |
671 | operands[1] = copy_to_mode_reg (mode, operands[1]); | |
672 | } | |
0d7e008e SC |
673 | if ((mode == DImode || mode == SImode || mode == HImode || mode == QImode) |
674 | && GET_CODE (operands[1]) == CONST_INT) | |
b9654711 | 675 | { |
0d7e008e SC |
676 | return synth_constant (operands, mode); |
677 | } | |
678 | if (mode == DFmode || mode == DImode) | |
679 | { | |
680 | rtx src = operands[1]; | |
681 | rtx dst = operands[0]; | |
682 | rtx insns; | |
b9654711 | 683 | |
0d7e008e | 684 | if (src == dst) |
b9654711 | 685 | { |
0d7e008e SC |
686 | emit_insn (gen_rtx (SET, VOIDmode, dst, src)); |
687 | return 1; | |
688 | } | |
b9654711 | 689 | |
0d7e008e SC |
690 | if (GET_CODE (src) == REG && |
691 | REGNO (src) >= FIRST_PSEUDO_REGISTER) | |
692 | return 0; | |
693 | ||
694 | if (GET_CODE (dst) == REG && | |
695 | REGNO (dst) >= FIRST_PSEUDO_REGISTER) | |
696 | return 0; | |
697 | ||
0d7e008e SC |
698 | if (push_operand (dst, mode)) |
699 | return 0; | |
700 | ||
701 | if (GET_CODE (src) == CONST_DOUBLE) | |
702 | src = force_const_mem (DFmode, src); | |
703 | ||
704 | if (reload_in_progress) | |
705 | { | |
706 | if (!(offsettable_memref_p (src) || register_operand (src, mode))) | |
707 | return 0; | |
708 | if (!(offsettable_memref_p (dst) || register_operand (dst, | |
709 | mode))) | |
710 | return 0; | |
711 | } | |
712 | start_sequence (); | |
713 | if (GET_CODE (operands[0]) != REG | |
714 | || !refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1, operands[1], 0)) | |
715 | { | |
716 | emit_move_insn (operand_subword (dst, 0, 1, mode), | |
717 | operand_subword_force (src, 0, mode)); | |
718 | emit_move_insn (operand_subword (dst, 1, 1, mode), | |
719 | operand_subword_force (src, 1, mode)); | |
720 | } | |
721 | else | |
722 | { | |
723 | emit_move_insn (operand_subword (dst, 1, 1, mode), | |
724 | operand_subword_force (src, 1, mode)); | |
725 | emit_move_insn (operand_subword (dst, 0, 1, mode), | |
726 | operand_subword_force (src, 0, mode)); | |
b9654711 | 727 | } |
0d7e008e SC |
728 | |
729 | insns = get_insns (); | |
730 | end_sequence (); | |
731 | ||
732 | emit_no_conflict_block (insns, dst, src, 0, src); | |
733 | return 1; | |
b9654711 | 734 | } |
0d7e008e | 735 | |
b9654711 | 736 | return 0; |
bc45ade3 SC |
737 | } |
738 | ||
bc45ade3 SC |
739 | /* Prepare the operands for an scc instruction; make sure that the |
740 | compare has been done. */ | |
741 | rtx | |
742 | prepare_scc_operands (code) | |
743 | { | |
b9654711 SC |
744 | if (GET_CODE (sh_compare_op0) != REG |
745 | || REGNO (sh_compare_op0) != T_REG) | |
bc45ade3 | 746 | { |
0d7e008e | 747 | int newcode = code; |
bc45ade3 | 748 | /* First need a compare insn */ |
0d7e008e SC |
749 | switch (code) |
750 | { | |
751 | case NE: | |
752 | newcode = EQ; | |
753 | break; | |
754 | case LT: | |
755 | newcode = GT; | |
756 | break; | |
757 | case LE: | |
758 | newcode = GE; | |
759 | break; | |
760 | case LTU: | |
761 | newcode = GTU; | |
762 | break; | |
763 | case LEU: | |
764 | newcode = GEU; | |
765 | break; | |
766 | } | |
767 | if (newcode != code) | |
768 | { | |
769 | rtx tmp = sh_compare_op0; | |
770 | sh_compare_op0 = sh_compare_op1; | |
771 | sh_compare_op1 = tmp; | |
772 | code = newcode; | |
773 | } | |
774 | ||
775 | sh_compare_op0 = force_reg (SImode, sh_compare_op0); | |
776 | emit_insn (gen_rtx (SET, VOIDmode, | |
bc45ade3 | 777 | gen_rtx (REG, SImode, T_REG), |
0d7e008e | 778 | gen_rtx (code, SImode, sh_compare_op0, sh_compare_op1))); |
bc45ade3 | 779 | } |
b9654711 | 780 | return gen_rtx (REG, SImode, T_REG); |
bc45ade3 SC |
781 | } |
782 | \f | |
b9654711 | 783 | |
0d7e008e | 784 | /* Functions to output assembly code. */ |
bc45ade3 | 785 | |
b9654711 | 786 | /* Return a sequence of instructions to perform DI or DF move. |
bc45ade3 | 787 | |
b9654711 SC |
788 | Since the SH cannot move a DI or DF in one instruction, we have |
789 | to take care when we see overlapping source and dest registers. | |
790 | ||
791 | */ | |
0d7e008e | 792 | |
bc45ade3 | 793 | char * |
0d7e008e SC |
794 | output_movedouble (insn, operands, mode) |
795 | rtx insn; | |
bc45ade3 SC |
796 | rtx operands[]; |
797 | enum machine_mode mode; | |
798 | { | |
b9654711 SC |
799 | rtx dst = operands[0]; |
800 | rtx src = operands[1]; | |
b9654711 | 801 | |
d3ae8277 SC |
802 | /* fprintf (asm_out_file, "! move double \n"); |
803 | fprintf (asm_out_file, "! pc %04x\n", insn_addresses[INSN_UID (insn)]);*/ | |
0d7e008e SC |
804 | if (GET_CODE (dst) == MEM |
805 | && GET_CODE (XEXP (dst, 0)) == POST_INC) | |
806 | { | |
807 | operands[0] = XEXP (XEXP (dst, 0), 0); | |
808 | return "mov.l %R1,@(4,%0)\n\tmov.l %1,@%0\n\tadd #8,%0"; | |
809 | } | |
b9654711 SC |
810 | if (register_operand (dst, mode) |
811 | && register_operand (src, mode)) | |
bc45ade3 | 812 | { |
b9654711 | 813 | if (REGNO (src) == MACH_REG) |
bc45ade3 | 814 | return "sts mach,%0\n\tsts macl,%R0"; |
bc45ade3 | 815 | |
b9654711 | 816 | /* |
0d7e008e SC |
817 | when mov.d r1,r2 do r2->r3 then r1->r2 |
818 | when mov.d r1,r0 do r1->r0 then r2->r1 | |
819 | */ | |
b9654711 SC |
820 | |
821 | if (REGNO (src) + 1 == REGNO (dst)) | |
0d7e008e | 822 | return "mov %R1,%R0\n\tmov %1,%0 ! cra"; |
b9654711 | 823 | else |
0d7e008e | 824 | return "mov %1,%0\n\tmov %R1,%R0 ! crb"; |
b9654711 SC |
825 | } |
826 | else if (GET_CODE (src) == CONST_INT) | |
bc45ade3 | 827 | { |
0d7e008e SC |
828 | HOST_WIDE_INT val = INTVAL (src); |
829 | int rn = REGNO (operands[0]); | |
830 | if (val < 0) | |
831 | { | |
832 | fprintf (asm_out_file, "\tmov #-1,r%d\n", rn); | |
833 | } | |
bc45ade3 | 834 | else |
0d7e008e SC |
835 | { |
836 | fprintf (asm_out_file, "\tmov #0,r%d\n", rn); | |
837 | } | |
bc45ade3 | 838 | |
0d7e008e SC |
839 | fprintf (asm_out_file, "\tmov #%d,r%d\n", val, rn + 1); |
840 | return ""; | |
841 | } | |
b9654711 | 842 | else if (GET_CODE (src) == MEM) |
bc45ade3 | 843 | { |
b9654711 SC |
844 | int ptrreg1 = -1; |
845 | int ptrreg2 = -1; | |
846 | int dreg = REGNO (dst); | |
847 | rtx inside = XEXP (src, 0); | |
bc45ade3 SC |
848 | |
849 | if (GET_CODE (inside) == REG) | |
b9654711 SC |
850 | { |
851 | ptrreg1 = REGNO (inside); | |
852 | } | |
bc45ade3 SC |
853 | else if (GET_CODE (inside) == PLUS) |
854 | { | |
855 | rtx lhs = XEXP (inside, 0); | |
856 | rtx rhs = XEXP (inside, 1); | |
857 | if (GET_CODE (lhs) == REG) | |
b9654711 SC |
858 | ptrreg1 = REGNO (lhs); |
859 | if (GET_CODE (rhs) == REG) | |
860 | ptrreg2 = REGNO (rhs); | |
bc45ade3 | 861 | } |
0d7e008e SC |
862 | else if (GET_CODE (inside) == LABEL_REF) |
863 | { | |
864 | return "mov.l %1,%0\n\tmov.l %1+4,%R0"; | |
865 | } | |
bc45ade3 SC |
866 | else |
867 | abort (); | |
868 | ||
b9654711 SC |
869 | if ((ptrreg1 >= 0 && ptrreg2 >= 0) |
870 | && (dreg == ptrreg1 | |
871 | || dreg == ptrreg2 | |
872 | || dreg + 1 == ptrreg1 | |
873 | || dreg + 1 == ptrreg2)) | |
874 | { | |
875 | /* This move clobbers both index registers, | |
876 | calculate the sum in one register. */ | |
877 | fprintf (asm_out_file, " add %s,%s ! special fix\n", | |
878 | reg_names[ptrreg2], reg_names[ptrreg1]); | |
879 | ||
880 | if (dreg == ptrreg1) | |
881 | { | |
882 | /* Copy into dreg+1 first. */ | |
883 | fprintf (asm_out_file, " mov.l @(4,%s),%s\n", | |
884 | reg_names[ptrreg1], | |
885 | reg_names[dreg + 1]); | |
886 | ||
887 | fprintf (asm_out_file, " mov.l @(%s),%s\n", | |
888 | reg_names[ptrreg1], | |
889 | reg_names[dreg]); | |
890 | } | |
891 | else | |
892 | { | |
893 | /* Copy into dreg first. */ | |
894 | fprintf (asm_out_file, " mov.l @(%s),%s\n", | |
895 | reg_names[ptrreg1], | |
896 | reg_names[dreg]); | |
897 | ||
898 | fprintf (asm_out_file, " mov.l @(4,%s),%s\n", | |
899 | reg_names[ptrreg1], | |
900 | reg_names[dreg + 1]); | |
901 | ||
902 | } | |
903 | warning ("generated complex amode"); | |
904 | return ""; | |
905 | } | |
906 | ||
907 | /* Work out the safe way to copy */ | |
908 | if (dreg == ptrreg1) | |
bc45ade3 | 909 | { |
b9654711 SC |
910 | /* Copy into the second half first */ |
911 | return "mov.l %R1,%R0\n\tmov.l %1,%0 ! cr"; | |
bc45ade3 | 912 | } |
bc45ade3 SC |
913 | } |
914 | ||
b9654711 | 915 | return "mov.l %1,%0\n\tmov.l %R1,%R0"; |
bc45ade3 SC |
916 | } |
917 | ||
918 | /* Emit assembly to shift reg by k bits */ | |
919 | ||
920 | char * | |
b9654711 | 921 | output_shift (string, reg, k, code) |
bc45ade3 SC |
922 | char *string; |
923 | rtx reg; | |
924 | rtx k; | |
b9654711 SC |
925 | int code; |
926 | ||
bc45ade3 SC |
927 | { |
928 | int s = INTVAL (k); | |
0d7e008e SC |
929 | if (s < 0) |
930 | { | |
931 | s = -s; | |
932 | switch (code) | |
933 | { | |
934 | case LSHIFTRT: | |
935 | case ASHIFTRT: | |
3841a7f6 | 936 | code = ASHIFT; |
0d7e008e SC |
937 | break; |
938 | case ASHIFT: | |
939 | code = ASHIFTRT; | |
940 | break; | |
0d7e008e SC |
941 | default: |
942 | abort (); | |
943 | } | |
944 | } | |
b9654711 SC |
945 | if (code == ASHIFT && s == 31) |
946 | { | |
947 | /* Shift left by 31 moving into the t bit, clearing and rotating the other way */ | |
948 | ||
949 | fprintf (asm_out_file, "\trotr r%d\n", REGNO (reg)); | |
950 | fprintf (asm_out_file, "\tmov #0,r%d\n", REGNO (reg)); | |
951 | fprintf (asm_out_file, "\trotcr r%d\n", REGNO (reg)); | |
952 | s = 0; | |
953 | } | |
954 | ||
955 | if (code == LSHIFTRT && s == 31) | |
956 | { | |
957 | fprintf (asm_out_file, "\trotl r%d\n", REGNO (reg)); | |
958 | fprintf (asm_out_file, "\tmov #0,r%d\n", REGNO (reg)); | |
959 | fprintf (asm_out_file, "\trotcl r%d\n", REGNO (reg)); | |
960 | s = 0; | |
961 | } | |
962 | ||
bc45ade3 SC |
963 | while (s) |
964 | { | |
965 | char *out; | |
966 | int d; | |
967 | ||
968 | if (s >= 16) | |
969 | { | |
970 | d = 16; | |
971 | out = "16"; | |
972 | } | |
973 | else if (s >= 8) | |
974 | { | |
975 | d = 8; | |
976 | out = "8"; | |
977 | } | |
978 | else if (s >= 2) | |
979 | { | |
980 | d = 2; | |
981 | out = "2"; | |
982 | } | |
983 | else | |
984 | { | |
985 | d = 1; | |
986 | out = ""; | |
987 | } | |
988 | fprintf (asm_out_file, "\t%s%s\tr%d\n", string, out, REGNO (reg)); | |
989 | s -= d; | |
990 | } | |
991 | return ""; | |
992 | } | |
993 | ||
0d7e008e SC |
994 | |
995 | void | |
996 | function_epilogue (stream, size) | |
997 | FILE *stream; | |
998 | int size; | |
999 | { | |
d3ae8277 | 1000 | pragma_interrupt = pragma_trapa = 0; |
0d7e008e SC |
1001 | } |
1002 | ||
1003 | ||
bc45ade3 | 1004 | /* Return the text of the branch instruction which matches its length |
b9654711 SC |
1005 | attribute. |
1006 | ||
1007 | This gets tricky if we have an insn in the delay slot of a branch | |
0d7e008e SC |
1008 | and the branch needs more than 1 insn to complete. */ |
1009 | ||
1010 | int pending_const_table; | |
1011 | ||
1012 | /* We can't tell if we need a register as a scratch for the jump | |
1013 | until after branch shortening, and then it's too late to allocate a | |
1014 | register the 'proper' way. These instruction sequences are rare | |
1015 | anyway, so to avoid always using a reg up from our limited set, we'll | |
1016 | grab one when we need one on output. */ | |
1017 | ||
1018 | char * | |
1019 | output_far_jump (insn, op) | |
1020 | rtx insn; | |
1021 | rtx op; | |
1022 | { | |
1023 | rtx thislab = gen_label_rtx (); | |
1024 | ||
1025 | /* See if we can grab a reg from the prev insn */ | |
1026 | rtx gotone = 0; | |
1027 | rtx prev = PREV_INSN (insn); | |
1028 | rtx link; | |
1029 | ||
1030 | if (dbr_sequence_length ()) | |
1031 | { | |
1032 | /* Something to go in what would have been the delay | |
1033 | slot if this had been a short branch. Make sure the | |
1034 | reg we use to generate the branch target address | |
1035 | doesn't conflict */ | |
1036 | ||
1037 | int i; | |
1038 | rtx vec[2]; | |
1039 | vec[0] = thislab; | |
1040 | ||
1041 | for (i = 0; i < 8; i++) | |
1042 | { | |
1043 | vec[1] = gen_rtx (REG, SImode, i); | |
1044 | if (!reg_referenced_p (vec[1], PATTERN (XVECEXP (final_sequence, 0, 1)))) | |
1045 | break; | |
1046 | } | |
b9654711 | 1047 | |
0d7e008e SC |
1048 | output_asm_insn ("mov.l %1,@-r15", vec); |
1049 | output_asm_insn ("mov.l %O0,%1", vec); | |
1050 | print_slot (final_sequence); | |
1051 | output_asm_insn ("jmp @%1 ! 32 xcond", vec); | |
1052 | output_asm_insn ("mov.l @r15+,%1", vec); | |
1053 | } | |
1054 | else | |
1055 | { | |
1056 | output_asm_insn ("mov.l r13,@-r15", 0); | |
1057 | output_asm_insn ("mov.l %O0,r13", &thislab); | |
1058 | output_asm_insn ("jmp @r13 ! 32 zcond", 0); | |
1059 | output_asm_insn ("mov.l @r15+,r13", 0); | |
1060 | } | |
b9654711 | 1061 | |
0d7e008e | 1062 | output_asm_insn (".align 2", 0); |
d3ae8277 | 1063 | ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (thislab)); |
0d7e008e SC |
1064 | output_asm_insn (".long %O0", &op); |
1065 | return ""; | |
1066 | } | |
bc45ade3 SC |
1067 | |
1068 | char * | |
1069 | output_branch (logic, insn) | |
1070 | int logic; | |
b9654711 | 1071 | rtx insn; |
bc45ade3 SC |
1072 | { |
1073 | extern rtx recog_operand[]; | |
1074 | int label = lf++; | |
b9654711 SC |
1075 | int rn = -1; |
1076 | int need_save; | |
d3ae8277 | 1077 | /* fprintf (asm_out_file, "! pc %04x\n", insn_addresses[INSN_UID (insn)]);*/ |
b9654711 | 1078 | |
bc45ade3 SC |
1079 | switch (get_attr_length (insn)) |
1080 | { | |
1081 | case 2: | |
1082 | /* Simple branch in range -200..+200 bytes */ | |
b9654711 | 1083 | return logic ? "bt%. %l0" : "bf%. %l0"; |
bc45ade3 SC |
1084 | |
1085 | case 6: | |
1086 | /* Branch in range -4000..+4000 bytes */ | |
b9654711 SC |
1087 | { |
1088 | rtx oldop = recog_operand[0]; | |
1089 | ||
1090 | ||
1091 | if (need_slot (final_sequence)) | |
1092 | { | |
1093 | fprintf (asm_out_file, "\tb%c.s\tLF%d\n", logic ? 'f' : 't', | |
1094 | label); | |
1095 | ||
1096 | print_slot (final_sequence); | |
1097 | } | |
1098 | ||
1099 | else | |
1100 | { | |
1101 | fprintf (asm_out_file, "\tb%c\tLF%d\n", logic ? 'f' : 't', | |
1102 | label); | |
1103 | } | |
1104 | recog_operand[0] = oldop; | |
1105 | ||
1106 | output_asm_insn ("bra %l0 ! 12 bit cond ", recog_operand); | |
1107 | fprintf (asm_out_file, "\tor r0,r0\n"); | |
b9654711 SC |
1108 | fprintf (asm_out_file, "LF%d:\n", label); |
1109 | } | |
bc45ade3 SC |
1110 | return ""; |
1111 | ||
0d7e008e | 1112 | case 16: |
bc45ade3 | 1113 | /* Branches a long way away */ |
b9654711 | 1114 | { |
b9654711 SC |
1115 | rtx oldop = recog_operand[0]; |
1116 | ||
1117 | if (need_slot (final_sequence)) | |
1118 | { | |
1119 | fprintf (asm_out_file, "\tb%c.s\tLF%d\n", logic ? 'f' : 't', label); | |
1120 | print_slot (final_sequence); | |
1121 | ||
1122 | } | |
1123 | else | |
1124 | { | |
1125 | fprintf (asm_out_file, "\tb%c\tLF%d\n", logic ? 'f' : 't', label); | |
1126 | } | |
1127 | ||
0d7e008e | 1128 | output_far_jump (insn, oldop); |
b9654711 SC |
1129 | fprintf (asm_out_file, "LF%d:\n", label); |
1130 | return ""; | |
1131 | } | |
bc45ade3 SC |
1132 | } |
1133 | return "bad"; | |
bc45ade3 SC |
1134 | } |
1135 | \f | |
b9654711 | 1136 | |
0d7e008e SC |
1137 | /* The SH cannot load a large constant into a register, constants have to |
1138 | come from a pc relative load. The reference of a pc relative load | |
1139 | instruction must be less than 1k infront of the instruction. This | |
1140 | means that we often have to dump a constant inside a function, and | |
1141 | generate code to branch around it. | |
b9654711 | 1142 | |
0d7e008e SC |
1143 | It is important to minimize this, since the branches will slow things |
1144 | down and make things bigger. | |
b9654711 | 1145 | |
0d7e008e | 1146 | Worst case code looks like: |
b9654711 | 1147 | |
0d7e008e SC |
1148 | mov.l L1,rn |
1149 | bra L2 | |
1150 | nop | |
1151 | align | |
1152 | L1: .long value | |
1153 | L2: | |
1154 | .. | |
bc45ade3 | 1155 | |
0d7e008e SC |
1156 | mov.l L3,rn |
1157 | bra L4 | |
1158 | nop | |
1159 | align | |
1160 | L3: .long value | |
1161 | L4: | |
1162 | .. | |
bc45ade3 | 1163 | |
0d7e008e SC |
1164 | We fix this by performing a scan before scheduling, which notices which |
1165 | instructions need to have their operands fetched from the constant table | |
1166 | and builds the table. | |
bc45ade3 | 1167 | |
bc45ade3 | 1168 | |
0d7e008e | 1169 | The algorithm is: |
bc45ade3 | 1170 | |
0d7e008e SC |
1171 | scan, find an instruction which needs a pcrel move. Look forward, find the |
1172 | last barrier which is within MAX_COUNT bytes of the requirement. | |
1173 | If there isn't one, make one. Process all the instructions between | |
1174 | the find and the barrier. | |
bc45ade3 SC |
1175 | |
1176 | In the above example, we can tell that L3 is within 1k of L1, so | |
1177 | the first move can be shrunk from the 3 insn+constant sequence into | |
1178 | just 1 insn, and the constant moved to L3 to make: | |
1179 | ||
0d7e008e | 1180 | mov.l L1,rn |
bc45ade3 | 1181 | .. |
0d7e008e SC |
1182 | mov.l L3,rn |
1183 | bra L4 | |
bc45ade3 SC |
1184 | nop |
1185 | align | |
0d7e008e SC |
1186 | L3:.long value |
1187 | L4:.long value | |
bc45ade3 SC |
1188 | |
1189 | Then the second move becomes the target for the shortening process. | |
1190 | ||
0d7e008e | 1191 | */ |
bc45ade3 SC |
1192 | |
1193 | typedef struct | |
1194 | { | |
0d7e008e SC |
1195 | rtx value; /* Value in table */ |
1196 | rtx label; /* Label of value */ | |
1197 | enum machine_mode mode; /* Mode of value */ | |
1198 | } | |
1199 | ||
1200 | pool_node; | |
bc45ade3 SC |
1201 | |
1202 | /* The maximum number of constants that can fit into one pool, since | |
1203 | the pc relative range is 0...1020 bytes and constants are at least 4 | |
1204 | bytes long */ | |
1205 | ||
1206 | #define MAX_POOL_SIZE (1020/4) | |
1207 | static pool_node pool_vector[MAX_POOL_SIZE]; | |
1208 | static int pool_size; | |
1209 | ||
0d7e008e | 1210 | /* Add a constant to the pool and return its label. */ |
bc45ade3 | 1211 | |
0d7e008e | 1212 | static rtx |
bc45ade3 SC |
1213 | add_constant (x, mode) |
1214 | rtx x; | |
1215 | enum machine_mode mode; | |
1216 | { | |
1217 | int i; | |
0d7e008e | 1218 | rtx lab; |
bc45ade3 SC |
1219 | /* First see if we've already got it */ |
1220 | ||
1221 | for (i = 0; i < pool_size; i++) | |
1222 | { | |
bc45ade3 SC |
1223 | if (x->code == pool_vector[i].value->code |
1224 | && mode == pool_vector[i].mode) | |
1225 | { | |
1226 | if (x->code == CODE_LABEL) | |
1227 | { | |
1228 | if (XINT (x, 3) != XINT (pool_vector[i].value, 3)) | |
1229 | continue; | |
1230 | } | |
1231 | } | |
bc45ade3 | 1232 | if (rtx_equal_p (x, pool_vector[i].value)) |
0d7e008e | 1233 | return pool_vector[i].label; |
bc45ade3 | 1234 | } |
b9654711 | 1235 | |
0d7e008e | 1236 | /* Need a new one */ |
bc45ade3 SC |
1237 | |
1238 | pool_vector[pool_size].value = x; | |
0d7e008e | 1239 | lab = gen_label_rtx (); |
bc45ade3 | 1240 | pool_vector[pool_size].mode = mode; |
0d7e008e | 1241 | pool_vector[pool_size].label = lab; |
bc45ade3 | 1242 | pool_size++; |
0d7e008e | 1243 | return lab; |
bc45ade3 SC |
1244 | } |
1245 | ||
0d7e008e | 1246 | /* Dump out interesting debug info */ |
bc45ade3 | 1247 | |
0d7e008e SC |
1248 | rtx |
1249 | final_prescan_insn (insn, opvec, noperands) | |
bc45ade3 | 1250 | rtx insn; |
0d7e008e SC |
1251 | rtx *opvec; |
1252 | int noperands; | |
bc45ade3 | 1253 | { |
0d7e008e | 1254 | if (target_flags & ISIZE_BIT) |
bc45ade3 | 1255 | { |
0d7e008e SC |
1256 | extern int *insn_addresses; |
1257 | fprintf (asm_out_file, "\n! at %04x\n", | |
1258 | insn_addresses[INSN_UID (insn)]); | |
bc45ade3 | 1259 | } |
bc45ade3 SC |
1260 | } |
1261 | ||
bc45ade3 | 1262 | |
0d7e008e | 1263 | \f |
bc45ade3 | 1264 | |
0d7e008e | 1265 | /* Stuff taken from m88k.c */ |
b9654711 | 1266 | |
0d7e008e | 1267 | /* Output to FILE the start of the assembler file. */ |
bc45ade3 | 1268 | |
0d7e008e | 1269 | struct option |
bc45ade3 | 1270 | { |
0d7e008e SC |
1271 | char *string; |
1272 | int *variable; | |
1273 | int on_value; | |
1274 | }; | |
b9654711 | 1275 | |
0d7e008e SC |
1276 | static int |
1277 | output_option (file, sep, type, name, indent, pos, max) | |
1278 | FILE *file; | |
1279 | char *sep; | |
1280 | char *type; | |
1281 | char *name; | |
1282 | char *indent; | |
1283 | int pos; | |
1284 | int max; | |
1285 | { | |
1286 | if (strlen (sep) + strlen (type) + strlen (name) + pos > max) | |
bc45ade3 | 1287 | { |
0d7e008e SC |
1288 | fprintf (file, indent); |
1289 | return fprintf (file, "%s%s", type, name); | |
b9654711 | 1290 | } |
0d7e008e SC |
1291 | return pos + fprintf (file, "%s%s%s", sep, type, name); |
1292 | } | |
bc45ade3 | 1293 | |
0d7e008e SC |
1294 | static struct |
1295 | { | |
1296 | char *name; | |
1297 | int value; | |
1298 | } | |
bc45ade3 | 1299 | |
0d7e008e | 1300 | m_options[] = TARGET_SWITCHES; |
bc45ade3 | 1301 | |
0d7e008e SC |
1302 | static void |
1303 | output_options (file, f_options, f_len, W_options, W_len, | |
1304 | pos, max, sep, indent, term) | |
1305 | FILE *file; | |
1306 | struct option *f_options; | |
1307 | struct option *W_options; | |
1308 | int f_len, W_len; | |
1309 | int pos; | |
1310 | int max; | |
1311 | char *sep; | |
1312 | char *indent; | |
1313 | char *term; | |
1314 | { | |
1315 | register int j; | |
bc45ade3 | 1316 | |
bc45ade3 | 1317 | |
0d7e008e SC |
1318 | if (optimize) |
1319 | pos = output_option (file, sep, "-O", "", indent, pos, max); | |
1320 | if (write_symbols != NO_DEBUG) | |
1321 | pos = output_option (file, sep, "-g", "", indent, pos, max); | |
1322 | if (flag_traditional) | |
1323 | pos = output_option (file, sep, "-traditional", "", indent, pos, max); | |
1324 | if (profile_flag) | |
1325 | pos = output_option (file, sep, "-p", "", indent, pos, max); | |
1326 | if (profile_block_flag) | |
1327 | pos = output_option (file, sep, "-a", "", indent, pos, max); | |
bc45ade3 | 1328 | |
0d7e008e SC |
1329 | for (j = 0; j < f_len; j++) |
1330 | if (*f_options[j].variable == f_options[j].on_value) | |
1331 | pos = output_option (file, sep, "-f", f_options[j].string, | |
1332 | indent, pos, max); | |
bc45ade3 | 1333 | |
0d7e008e SC |
1334 | for (j = 0; j < W_len; j++) |
1335 | if (*W_options[j].variable == W_options[j].on_value) | |
1336 | pos = output_option (file, sep, "-W", W_options[j].string, | |
1337 | indent, pos, max); | |
bc45ade3 | 1338 | |
0d7e008e SC |
1339 | for (j = 0; j < sizeof m_options / sizeof m_options[0]; j++) |
1340 | if (m_options[j].name[0] != '\0' | |
1341 | && m_options[j].value > 0 | |
1342 | && ((m_options[j].value & target_flags) | |
1343 | == m_options[j].value)) | |
1344 | pos = output_option (file, sep, "-m", m_options[j].name, | |
1345 | indent, pos, max); | |
bc45ade3 | 1346 | |
bc45ade3 | 1347 | |
0d7e008e SC |
1348 | fprintf (file, term); |
1349 | fprintf (file, "! %d %d\n", max_count_si, max_count_hi); | |
1350 | } | |
b9654711 | 1351 | |
0d7e008e SC |
1352 | void |
1353 | output_file_start (file, f_options, f_len, W_options, W_len) | |
1354 | FILE *file; | |
1355 | struct option *f_options; | |
1356 | struct option *W_options; | |
1357 | int f_len, W_len; | |
bc45ade3 | 1358 | { |
0d7e008e | 1359 | register int pos; |
b9654711 | 1360 | |
0d7e008e | 1361 | output_file_directive (file, main_input_filename); |
b9654711 | 1362 | |
0d7e008e SC |
1363 | /* Switch to the data section so that the coffsem symbol and the |
1364 | gcc2_compiled. symbol aren't in the text section. */ | |
1365 | data_section (); | |
b9654711 | 1366 | |
b9654711 | 1367 | |
d3ae8277 | 1368 | pos = fprintf (file, "\n! Hitachi SH cc1 (%s) (release D-1) arguments:", version_string); |
0d7e008e SC |
1369 | output_options (file, f_options, f_len, W_options, W_len, |
1370 | pos, 75, " ", "\n! ", "\n\n"); | |
bc45ade3 | 1371 | } |
0d7e008e | 1372 | \f |
bc45ade3 SC |
1373 | |
1374 | ||
0d7e008e | 1375 | /* Return the cost of a shift */ |
bc45ade3 | 1376 | |
0d7e008e SC |
1377 | int |
1378 | shiftcosts (RTX) | |
1379 | rtx RTX; | |
bc45ade3 | 1380 | { |
0d7e008e SC |
1381 | /* If shift by a non constant, then this will be expensive. */ |
1382 | if (GET_CODE (XEXP (RTX, 1)) != CONST_INT) | |
1383 | return 20; | |
bc45ade3 | 1384 | |
0d7e008e SC |
1385 | /* otherwise, it will be very cheap if by one of the constants |
1386 | we can cope with. */ | |
1387 | if (CONST_OK_FOR_K (INTVAL (XEXP (RTX, 1)))) | |
1388 | return 1; | |
b9654711 | 1389 | |
0d7e008e SC |
1390 | /* otherwise it will be several insns, but we pretend that it will be more than |
1391 | just the components, so that combine doesn't glue together a load of shifts into | |
1392 | one shift which has to be emitted as a bunch anyway - breaking scheduling */ | |
d3ae8277 | 1393 | return 1; |
0d7e008e | 1394 | } |
b9654711 | 1395 | |
0d7e008e SC |
1396 | int |
1397 | andcosts (RTX) | |
1398 | rtx RTX; | |
1399 | { | |
1400 | int i; | |
1401 | if (GET_CODE (XEXP (RTX, 1)) != CONST_INT) | |
1402 | return 2; | |
1403 | i = INTVAL (XEXP (RTX, 1)); | |
1404 | /* And can use the extend insns cheaply */ | |
1405 | if (i == 0xff || i == 0xffff) | |
1406 | return 2; | |
1407 | /* Any small constant is reasonably cheap - but requires r0 */ | |
1408 | if (CONST_OK_FOR_I (i)) | |
1409 | return 3; | |
1410 | return 5; | |
1411 | } | |
d3ae8277 SC |
1412 | |
1413 | int howshift (i) | |
1414 | int i; | |
1415 | { | |
1416 | int total = 0; | |
1417 | while (i > 0) | |
1418 | { | |
1419 | if (i >= 16) { | |
1420 | total++; | |
1421 | i -= 16; | |
1422 | } | |
1423 | else if (i >= 8) { | |
1424 | total++; | |
1425 | i -= 8; | |
1426 | } | |
1427 | else if (i >= 2) { | |
1428 | total++; | |
1429 | i -= 2; | |
1430 | } | |
1431 | else if (i>=1) { | |
1432 | total++; | |
1433 | i--; | |
1434 | } | |
1435 | } | |
1436 | return total; | |
1437 | } | |
1438 | ||
0d7e008e SC |
1439 | /* Return the cost of a multiply */ |
1440 | int | |
1441 | multcosts (RTX) | |
1442 | rtx RTX; | |
1443 | { | |
d3ae8277 SC |
1444 | /* If mult by a power of 2 then work out how we'd shift to make it */ |
1445 | int insn_cost; | |
1446 | ||
1447 | if (GET_CODE (XEXP (RTX, 1)) == CONST_INT) | |
1448 | { | |
1449 | int i = exact_log2 (INTVAL (XEXP (RTX, 1))); | |
1450 | if (i >= 0) | |
1451 | insn_cost = howshift (i); | |
1452 | else | |
1453 | insn_cost = 100000; | |
1454 | } | |
0d7e008e | 1455 | if (TARGET_SH2) |
d3ae8277 SC |
1456 | { |
1457 | /* We have a mul insn, so we can never take more than the mul and the | |
1458 | read of the mac reg, but count more because of the latency and extra reg | |
1459 | usage */ | |
1460 | if (TARGET_SMALLCODE) | |
1461 | return 2; | |
1462 | if (insn_cost > 5) | |
1463 | return 5; | |
1464 | return insn_cost; | |
1465 | } | |
1466 | ||
0d7e008e | 1467 | /* If we we're aiming at small code, then just count the number of |
d3ae8277 SC |
1468 | insns in a multiply call sequence */ |
1469 | ||
1470 | if (TARGET_SMALLCODE) | |
1471 | { | |
1472 | if (insn_cost > 6) | |
1473 | return 6; | |
1474 | return insn_cost; | |
1475 | } | |
1476 | ||
1477 | /* Otherwise count all the insns in the routine we'd be calling too */ | |
1478 | return 20; | |
0d7e008e | 1479 | } |
b9654711 | 1480 | |
0d7e008e | 1481 | /* Code to expand a shift */ |
b9654711 | 1482 | |
0d7e008e SC |
1483 | void |
1484 | gen_ashift (type, n, reg) | |
1485 | int type; | |
1486 | int n; | |
1487 | rtx reg; | |
1488 | { | |
1489 | switch (type) | |
bc45ade3 | 1490 | { |
0d7e008e SC |
1491 | case ASHIFTRT: |
1492 | emit_insn (gen_ashrsi3_k (reg, reg, GEN_INT (n))); | |
1493 | break; | |
1494 | case LSHIFTRT: | |
1495 | emit_insn (gen_lshrsi3_k (reg, reg, GEN_INT (n))); | |
1496 | break; | |
1497 | case ASHIFT: | |
1498 | if (n == 1) | |
1499 | emit_insn (gen_addsi3 (reg, reg, reg)); | |
1500 | else | |
1501 | emit_insn (gen_ashlsi3_k (reg, reg, GEN_INT (n))); | |
1502 | break; | |
bc45ade3 | 1503 | } |
bc45ade3 | 1504 | } |
bc45ade3 | 1505 | |
0d7e008e SC |
1506 | int |
1507 | gen_shifty_op (code, operands) | |
1508 | int code; | |
1509 | rtx *operands; | |
bc45ade3 | 1510 | { |
0d7e008e SC |
1511 | rtx wrk = gen_reg_rtx (SImode); |
1512 | rtx t; | |
1513 | char *func; | |
1514 | if (GET_CODE (operands[2]) == CONST_INT) | |
bc45ade3 | 1515 | { |
0d7e008e SC |
1516 | int value = INTVAL (operands[2]); |
1517 | top: | |
1518 | switch (code) | |
1519 | { | |
1520 | case ASHIFTRT: | |
1521 | if (value < 0) | |
1522 | { | |
1523 | code = ASHIFT; | |
1524 | value = -value; | |
1525 | goto top; | |
1526 | } | |
bc45ade3 | 1527 | |
0d7e008e | 1528 | /* Expand a short sequence inline, longer call a magic routine */ |
d3ae8277 | 1529 | if (value <= 5) |
0d7e008e SC |
1530 | { |
1531 | emit_move_insn (wrk, operands[1]); | |
1532 | while (value--) | |
1533 | { | |
1534 | gen_ashift (ASHIFTRT, 1, wrk); | |
1535 | } | |
1536 | emit_move_insn (operands[0], wrk); | |
1537 | return 1; | |
1538 | } | |
1539 | t = gen_reg_rtx (Pmode); | |
1540 | /* Load the value into an arg reg and call a helper */ | |
1541 | emit_move_insn (gen_rtx (REG, SImode, 4), operands[1]); | |
1542 | if (!shiftsyms[value]) | |
1543 | { | |
1544 | func = xmalloc (18); | |
1545 | sprintf (func, "__ashiftrt_r4_%d", value); | |
1546 | shiftsyms[value] = gen_rtx (SYMBOL_REF, Pmode, func); | |
1547 | } | |
1548 | emit_move_insn (t, shiftsyms[value]); | |
1549 | emit_insn (gen_ashrsi3_n (GEN_INT (value), t)); | |
1550 | emit_move_insn (operands[0], gen_rtx (REG, SImode, 4)); | |
1551 | return 1; | |
bc45ade3 | 1552 | |
0d7e008e SC |
1553 | case ASHIFT: |
1554 | if (value < 0) | |
1555 | { | |
1556 | code = LSHIFTRT; | |
1557 | value = -value; | |
1558 | goto top; | |
1559 | } | |
1560 | /* Fall through */ | |
1561 | case LSHIFTRT: | |
bc45ade3 | 1562 | |
0d7e008e SC |
1563 | if (value < 0) |
1564 | { | |
1565 | code = ASHIFT; | |
1566 | value = -value; | |
1567 | goto top; | |
1568 | } | |
a9f71ad8 | 1569 | |
0d7e008e SC |
1570 | emit_move_insn (wrk, operands[1]); |
1571 | while (value) | |
1572 | { | |
1573 | if (value >= 16) | |
1574 | { | |
1575 | gen_ashift (code, 16, wrk); | |
1576 | value -= 16; | |
1577 | } | |
1578 | else if (value >= 8) | |
1579 | { | |
1580 | gen_ashift (code, 8, wrk); | |
1581 | value -= 8; | |
1582 | } | |
1583 | else if (value >= 2) | |
1584 | { | |
1585 | gen_ashift (code, 2, wrk); | |
1586 | value -= 2; | |
1587 | } | |
1588 | else | |
1589 | { | |
1590 | gen_ashift (code, 1, wrk); | |
1591 | value--; | |
1592 | } | |
1593 | } | |
1594 | emit_move_insn (operands[0], wrk); | |
1595 | return 1; | |
a9f71ad8 | 1596 | |
0d7e008e | 1597 | } |
a9f71ad8 | 1598 | } |
0d7e008e | 1599 | return 0; |
bc45ade3 SC |
1600 | } |
1601 | ||
0d7e008e SC |
1602 | /* Dump out any constants accumulated in the final pass - |
1603 | which will only be labels */ | |
1604 | char * | |
1605 | output_jump_label_table () | |
1606 | { | |
1607 | int i; | |
1608 | if (pool_size) | |
1609 | { | |
1610 | fprintf (asm_out_file, "\t.align 2\n"); | |
1611 | for (i = 0; i < pool_size; i++) | |
1612 | { | |
1613 | pool_node *p = pool_vector + i; | |
a9f71ad8 | 1614 | |
0d7e008e SC |
1615 | ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (p->label)); |
1616 | output_asm_insn (".long %O0", &p->value); | |
1617 | } | |
1618 | pool_size = 0; | |
1619 | } | |
b9654711 | 1620 | |
0d7e008e SC |
1621 | return ""; |
1622 | } | |
1623 | /* Output the literal table */ | |
b9654711 | 1624 | |
b9654711 | 1625 | static void |
0d7e008e SC |
1626 | dump_table (scan) |
1627 | rtx scan; | |
b9654711 | 1628 | { |
0d7e008e SC |
1629 | int i; |
1630 | int pass; | |
1631 | int need_align = 1; | |
b9654711 SC |
1632 | |
1633 | ||
0d7e008e | 1634 | /* Do two passes, first time dump out the HI sized constants */ |
b9654711 | 1635 | |
0d7e008e | 1636 | for (i = 0; i < pool_size; i++) |
b9654711 | 1637 | { |
0d7e008e SC |
1638 | pool_node *p = pool_vector + i; |
1639 | if (p->mode == HImode) | |
1640 | { | |
1641 | if (need_align) | |
1642 | { | |
1643 | scan = emit_insn_after (gen_align_2 (), scan); | |
1644 | need_align = 0; | |
1645 | } | |
1646 | scan = emit_label_after (p->label, scan); | |
1647 | scan = emit_insn_after (gen_consttable_2 (p->value), scan); | |
1648 | } | |
b9654711 | 1649 | } |
0d7e008e | 1650 | need_align = 1; |
b9654711 | 1651 | |
0d7e008e | 1652 | for (i = 0; i < pool_size; i++) |
b9654711 | 1653 | { |
0d7e008e | 1654 | pool_node *p = pool_vector + i; |
b9654711 | 1655 | |
0d7e008e | 1656 | switch (p->mode) |
b9654711 | 1657 | { |
0d7e008e SC |
1658 | case HImode: |
1659 | break; | |
1660 | case SImode: | |
1661 | if (need_align) | |
b9654711 | 1662 | { |
0d7e008e | 1663 | need_align = 0; |
d3ae8277 | 1664 | scan = emit_label_after (gen_label_rtx (), scan); |
0d7e008e | 1665 | scan = emit_insn_after (gen_align_4 (), scan); |
b9654711 | 1666 | } |
0d7e008e SC |
1667 | scan = emit_label_after (p->label, scan); |
1668 | scan = emit_insn_after (gen_consttable_4 (p->value), scan); | |
1669 | break; | |
1670 | case DImode: | |
1671 | if (need_align) | |
1672 | { | |
1673 | need_align = 0; | |
d3ae8277 | 1674 | scan = emit_label_after (gen_label_rtx (), scan); |
0d7e008e SC |
1675 | scan = emit_insn_after (gen_align_4 (), scan); |
1676 | } | |
1677 | scan = emit_label_after (p->label, scan); | |
1678 | scan = emit_insn_after (gen_consttable_8 (p->value), scan); | |
1679 | break; | |
1680 | default: | |
1681 | abort (); | |
1682 | break; | |
b9654711 SC |
1683 | } |
1684 | } | |
b9654711 | 1685 | |
0d7e008e SC |
1686 | scan = emit_insn_after (gen_consttable_end (), scan); |
1687 | scan = emit_barrier_after (scan); | |
1688 | pool_size = 0; | |
1689 | } | |
b9654711 | 1690 | |
b9654711 | 1691 | |
b9654711 | 1692 | |
0d7e008e SC |
1693 | /* Non zero if the src operand needs to be fixed up */ |
1694 | static | |
1695 | int | |
1696 | fixit (src, mode) | |
1697 | rtx src; | |
1698 | enum machine_mode mode; | |
1699 | { | |
1700 | if (mode == QImode) | |
1701 | return 0; /* QIs never need to be fixed */ | |
1702 | if (GET_CODE (src) == CONST) | |
1703 | return 1; | |
b9654711 | 1704 | |
0d7e008e | 1705 | if (GET_CODE (src) == SYMBOL_REF) |
b9654711 | 1706 | { |
0d7e008e SC |
1707 | return 1; |
1708 | } | |
1709 | if (GET_CODE (src) == CONST_INT) | |
1710 | { | |
1711 | /* All QI insns are ok */ | |
1712 | if (mode == QImode) | |
1713 | return 1; | |
1714 | /* The rest may need to be fixed */ | |
1715 | return !CONST_OK_FOR_I (INTVAL (src)); | |
b9654711 | 1716 | } |
0d7e008e | 1717 | return 0; |
b9654711 SC |
1718 | } |
1719 | ||
0d7e008e SC |
1720 | /* Return Non-zero if constant would be an ok source for a |
1721 | mov.w instead of a mov.l */ | |
1722 | int | |
1723 | hi_const (src) | |
1724 | rtx src; | |
b9654711 | 1725 | { |
d3ae8277 SC |
1726 | if (GET_CODE (src) == CONST |
1727 | && GET_CODE (XEXP (src, 0)) == SIGN_EXTEND | |
1728 | && GET_CODE (XEXP (XEXP (src, 0), 0)) == SYMBOL_REF) | |
1729 | return 1; | |
1730 | ||
1731 | if (TARGET_SHORTADDR | |
1732 | && GET_CODE (src) == SYMBOL_REF) | |
1733 | return 1; | |
1734 | ||
0d7e008e SC |
1735 | return (GET_CODE (src) == CONST_INT |
1736 | && INTVAL (src) >= -32768 | |
1737 | && INTVAL (src) <= 32767); | |
b9654711 | 1738 | } |
b9654711 | 1739 | |
0d7e008e SC |
1740 | /* Find the last barrier less than MAX_COUNT bytes from FROM, or create one. |
1741 | If an HI move is found, then make sure that MAX_COUNT_HI isn't broken from that one. */ | |
b9654711 | 1742 | |
0d7e008e SC |
1743 | static |
1744 | rtx | |
1745 | find_barrier (from) | |
1746 | rtx from; | |
b9654711 | 1747 | { |
0d7e008e SC |
1748 | int count_si = 0; |
1749 | int count_hi = 0; | |
1750 | int found_hi = 0; | |
1751 | int found_si = 0; | |
1752 | rtx found_barrier = 0; | |
1753 | ||
1754 | while (from | |
1755 | && count_si < max_count_si | |
1756 | && count_hi < max_count_hi) | |
1757 | { | |
1758 | int inc; | |
1759 | if (GET_CODE (from) == BARRIER) | |
1760 | { | |
1761 | found_barrier = from; | |
1762 | } | |
1763 | /* Count the length of this insn - we assume that all the pcrelloads | |
1764 | will work out to be only 2 bytes long */ | |
b9654711 | 1765 | |
0d7e008e SC |
1766 | if (GET_CODE (from) == INSN && |
1767 | GET_CODE (PATTERN (from)) == SET) | |
1768 | { | |
1769 | rtx src = SET_SRC (PATTERN (from)); | |
1770 | if (hi_const (src)) | |
1771 | found_hi = 1; | |
1772 | else | |
1773 | found_si = 1; | |
1774 | inc = 2; | |
1775 | } | |
1776 | else | |
1777 | { | |
1778 | inc = get_attr_length (from); | |
1779 | } | |
1780 | if (found_si) | |
1781 | count_si += inc; | |
1782 | if (found_hi) | |
1783 | count_hi += inc; | |
1784 | from = NEXT_INSN (from); | |
1785 | } | |
1786 | ||
1787 | if (!found_barrier) | |
b9654711 | 1788 | { |
0d7e008e SC |
1789 | /* Insert a jump around the barrier here */ |
1790 | rtx label = gen_label_rtx (); | |
1791 | /* Walk back to be just before any jump */ | |
1792 | while (GET_CODE (from) == JUMP_INSN | |
1793 | || GET_CODE (from) == NOTE) | |
1794 | { | |
1795 | from = PREV_INSN (from); | |
1796 | } | |
1797 | from = emit_jump_insn_after (gen_jump (label), from); | |
1798 | JUMP_LABEL (from) = label; | |
1799 | found_barrier = emit_barrier_after (from); | |
1800 | emit_label_after (label, found_barrier); | |
1801 | return found_barrier; | |
b9654711 | 1802 | } |
0d7e008e | 1803 | return found_barrier; |
b9654711 SC |
1804 | } |
1805 | ||
0d7e008e | 1806 | /* Non zero if the insn is a move instruction which needs to be fixed. */ |
b9654711 | 1807 | |
0d7e008e SC |
1808 | static |
1809 | int | |
1810 | broken_move (insn) | |
1811 | rtx insn; | |
b9654711 | 1812 | { |
0d7e008e SC |
1813 | if (!INSN_DELETED_P (insn) |
1814 | && GET_CODE (insn) == INSN | |
1815 | && GET_CODE (PATTERN (insn)) == SET) | |
1816 | { | |
1817 | rtx pat = PATTERN (insn); | |
1818 | rtx src = SET_SRC (pat); | |
1819 | rtx dst = SET_DEST (pat); | |
1820 | enum machine_mode mode = GET_MODE (dst); | |
1821 | if (dst == pc_rtx) | |
1822 | return 0; | |
1823 | return fixit (src, mode); | |
1824 | } | |
1825 | return 0; | |
1826 | } | |
b9654711 | 1827 | |
b9654711 | 1828 | |
0d7e008e | 1829 | /* Exported to toplev.c |
b9654711 | 1830 | |
0d7e008e SC |
1831 | Scan the function looking for move instructions which have to be changed to |
1832 | pcrel loads and insert the literal tables. */ | |
b9654711 | 1833 | |
0d7e008e SC |
1834 | void |
1835 | machine_dependent_reorg (first) | |
1836 | rtx first; | |
1837 | { | |
1838 | rtx insn; | |
1839 | int limit; | |
1840 | for (insn = first; insn; insn = NEXT_INSN (insn)) | |
1841 | { | |
1842 | if (broken_move (insn)) | |
1843 | { | |
1844 | /* This is a broken move instruction, scan ahead looking for | |
1845 | a barrier to stick the constant table behind */ | |
1846 | rtx scan; | |
1847 | rtx barrier = find_barrier (insn); | |
b9654711 | 1848 | |
0d7e008e SC |
1849 | /* Now find all the moves between the points and modify them */ |
1850 | for (scan = insn; scan != barrier; scan = NEXT_INSN (scan)) | |
1851 | { | |
1852 | if (broken_move (scan)) | |
1853 | { | |
1854 | rtx pat = PATTERN (scan); | |
1855 | rtx src = SET_SRC (pat); | |
1856 | rtx dst = SET_DEST (pat); | |
1857 | enum machine_mode mode = GET_MODE (dst); | |
1858 | rtx lab; | |
1859 | rtx newinsn; | |
1860 | rtx newsrc; | |
1861 | /* This is a broken move instruction, add it to the pool */ | |
1862 | ||
1863 | if (mode == SImode && hi_const (src)) | |
1864 | { | |
1865 | /* This is an HI source, clobber the dest to get the mode right too */ | |
1866 | mode = HImode; | |
d3ae8277 SC |
1867 | while (GET_CODE (dst) == SUBREG) |
1868 | dst = SUBREG_REG (dst); | |
0d7e008e SC |
1869 | dst = gen_rtx (REG, HImode, REGNO (dst)); |
1870 | } | |
1871 | lab = add_constant (src, mode); | |
1872 | newsrc = gen_rtx (MEM, mode, | |
1873 | gen_rtx (LABEL_REF, VOIDmode, lab)); | |
1874 | ||
1875 | /* Build a jump insn wrapper around the move instead | |
1876 | of an ordinary insn, because we want to have room for | |
1877 | the target label rtx in fld[7], which an ordinary | |
1878 | insn doesn't have. */ | |
1879 | newinsn = emit_jump_insn_after (gen_rtx (SET, VOIDmode, | |
1880 | dst, newsrc), scan); | |
1881 | JUMP_LABEL (newinsn) = lab; | |
1882 | ||
1883 | /* But it's still an ordinary insn */ | |
1884 | PUT_CODE (newinsn, INSN); | |
1885 | ||
1886 | /* Kill old insn */ | |
1887 | delete_insn (scan); | |
1888 | scan = newinsn; | |
1889 | } | |
1890 | } | |
1891 | dump_table (barrier); | |
1892 | } | |
1893 | } | |
b9654711 SC |
1894 | } |
1895 | ||
0d7e008e SC |
1896 | /* Called from the md file, set up the operands of a compare instruction */ |
1897 | ||
1898 | int | |
1899 | from_compare (operands, code) | |
1900 | rtx *operands; | |
1901 | int code; | |
b9654711 | 1902 | { |
d3ae8277 SC |
1903 | if (code != EQ && code != NE) |
1904 | { | |
1905 | /* Force args into regs, since we can't use constants here */ | |
1906 | sh_compare_op0 = force_reg (SImode, sh_compare_op0); | |
1907 | if (sh_compare_op1 != const0_rtx) | |
1908 | sh_compare_op1 = force_reg (SImode, sh_compare_op1); | |
1909 | } | |
0d7e008e | 1910 | operands[1] = sh_compare_op0; |
d3ae8277 | 1911 | operands[2] = sh_compare_op1; |
0d7e008e | 1912 | } |
b9654711 | 1913 | |
0d7e008e | 1914 | /* Non-zero if x is EQ or NE */ |
b9654711 | 1915 | |
0d7e008e SC |
1916 | int |
1917 | equality_operator (x, mode) | |
1918 | rtx x; | |
1919 | enum machine_mode mode; | |
1920 | { | |
1921 | enum rtx_code code = GET_CODE (x); | |
1922 | return (code == EQ || code == NE); | |
1923 | } | |
b9654711 SC |
1924 | |
1925 | ||
0d7e008e SC |
1926 | /* Framefull frame looks like: |
1927 | ||
1928 | arg-5 | |
1929 | arg-4 | |
1930 | [ if current_function_anonymous_args | |
1931 | arg-3 | |
1932 | arg-2 | |
1933 | arg-1 | |
1934 | arg-0 ] | |
1935 | saved-fp | |
1936 | saved-r10 | |
1937 | saved-r11 | |
1938 | saved-r12 | |
1939 | saved-pr | |
1940 | local-n | |
1941 | .. | |
1942 | local-1 | |
1943 | local-0 <- fp points here | |
1944 | ||
1945 | ||
1946 | If TARGET_SMALLCALL, then the preserved registers are pushed by a | |
1947 | wrapper before the routine is entered, so the regs are always pushed | |
1948 | and there are two pr's on the stack - the caller and the wrapper. | |
1949 | */ | |
1950 | ||
b9654711 SC |
1951 | |
1952 | /* Code to generate prologue and epilogue sequences */ | |
1953 | ||
0d7e008e | 1954 | |
b9654711 SC |
1955 | void |
1956 | sh_expand_prologue () | |
1957 | { | |
1958 | int live_regs_mask; | |
1959 | int d; | |
1960 | ||
1961 | live_regs_mask = calc_live_regs (&d); | |
1962 | ||
0d7e008e SC |
1963 | /* We have pretend args if we had an object sent partially in registers |
1964 | and partially on the stack - eg a large structure */ | |
1965 | output_stack_adjust (-current_function_pretend_args_size); | |
b9654711 SC |
1966 | |
1967 | if (current_function_anonymous_args) | |
1968 | { | |
1969 | /* Push arg regs as if they'd been provided by caller in stack */ | |
1970 | int i; | |
1971 | for (i = 0; i < NPARM_REGS; i++) | |
1972 | { | |
1973 | int rn = NPARM_REGS + FIRST_PARM_REG - i - 1; | |
1974 | if (i > NPARM_REGS - current_function_args_info) | |
1975 | break; | |
1976 | push (rn); | |
b9654711 SC |
1977 | extra_push += 4; |
1978 | } | |
1979 | } | |
0d7e008e SC |
1980 | push_regs (live_regs_mask); |
1981 | output_stack_adjust (-get_frame_size ()); | |
b9654711 SC |
1982 | |
1983 | if (frame_pointer_needed) | |
1984 | { | |
b9654711 SC |
1985 | emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx)); |
1986 | } | |
b9654711 SC |
1987 | } |
1988 | ||
1989 | void | |
1990 | sh_expand_epilogue () | |
1991 | { | |
1992 | int live_regs_mask; | |
1993 | int d; | |
1994 | int i; | |
1995 | ||
1996 | live_regs_mask = calc_live_regs (&d); | |
1997 | ||
1998 | if (frame_pointer_needed) | |
1999 | { | |
2000 | emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx)); | |
2001 | } | |
0d7e008e | 2002 | output_stack_adjust (get_frame_size ()); |
b9654711 SC |
2003 | |
2004 | /* Pop all the registers */ | |
0d7e008e | 2005 | |
b9654711 SC |
2006 | for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
2007 | { | |
2008 | int j = (FIRST_PSEUDO_REGISTER - 1) - i; | |
2009 | if (live_regs_mask & (1 << j)) | |
2010 | { | |
2011 | pop (j); | |
2012 | } | |
2013 | } | |
b9654711 | 2014 | |
0d7e008e | 2015 | output_stack_adjust (extra_push + current_function_pretend_args_size); |
b9654711 | 2016 | |
0d7e008e SC |
2017 | extra_push = 0; |
2018 | current_function_pretend_args_size = 0; | |
b9654711 | 2019 | current_function_anonymous_args = 0; |
0d7e008e SC |
2020 | for (i = 0; i < 32; i++) |
2021 | shiftsyms[i] = 0; | |
d3ae8277 | 2022 | |
b9654711 SC |
2023 | } |
2024 | ||
0d7e008e SC |
2025 | /* Define the offset between two registers, one to be eliminated, and |
2026 | the other its replacement, at the start of a routine. */ | |
2027 | ||
2028 | int | |
2029 | initial_elimination_offset (from, to) | |
2030 | { | |
2031 | int regs_saved; | |
2032 | int regs_saved_mask = calc_live_regs (®s_saved); | |
2033 | int total_saved_regs_space; | |
2034 | int total_auto_space = get_frame_size (); | |
2035 | total_saved_regs_space = (regs_saved) * 4; | |
b9654711 | 2036 | |
0d7e008e SC |
2037 | if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) |
2038 | { | |
2039 | return total_saved_regs_space + total_auto_space; | |
2040 | } | |
2041 | if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) | |
2042 | { | |
2043 | return total_saved_regs_space + total_auto_space; | |
2044 | } | |
2045 | if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) | |
2046 | { | |
2047 | /* Initial gap between fp and sp is 0 */ | |
2048 | return 0; | |
2049 | } | |
2050 | abort (); | |
2051 | } | |
2052 | ||
2053 | /* Handle machine specific pragmas to be semi-compatible with Hitachi | |
2054 | compiler */ | |
b9654711 SC |
2055 | |
2056 | int | |
0d7e008e SC |
2057 | handle_pragma (file) |
2058 | FILE *file; | |
b9654711 | 2059 | { |
0d7e008e SC |
2060 | int c; |
2061 | char pbuf[200]; | |
2062 | int psize = 0; | |
b9654711 | 2063 | |
0d7e008e SC |
2064 | c = getc (file); |
2065 | while (c == ' ' || c == '\t') | |
2066 | c = getc (file); | |
2067 | ||
2068 | if (c == '\n' || c == EOF) | |
2069 | return c; | |
2070 | ||
2071 | while (psize < sizeof (pbuf) - 1 && c != '\n') | |
2072 | { | |
2073 | pbuf[psize++] = c; | |
2074 | if (psize == 9 && strncmp (pbuf, "interrupt", 9) == 0) | |
2075 | { | |
2076 | pragma_interrupt = 1; | |
2077 | return; | |
2078 | } | |
2079 | if (psize == 5 && strncmp (pbuf, "trapa", 5) == 0) | |
2080 | { | |
2081 | pragma_interrupt = pragma_trapa = 1; | |
2082 | return; | |
2083 | } | |
2084 | c = getc (file); | |
2085 | } | |
2086 | return c; | |
2087 | } | |
2088 | \f | |
2089 | /* insn expand helpers */ | |
2090 | ||
d3ae8277 SC |
2091 | /* Emit insns to perform a call. |
2092 | If TARGET_SHORTADDR then use a bsr. If TARGET_SMALLCALL, then load the | |
0d7e008e SC |
2093 | target address into r1 and call __saveargs, otherwise |
2094 | perform the standard call sequence */ | |
2095 | ||
2096 | void | |
2097 | expand_acall (isa_retval, operands) | |
2098 | int isa_retval; | |
2099 | rtx *operands; | |
2100 | { | |
2101 | rtx call; | |
2102 | rtx ret = operands[0]; | |
2103 | rtx call_target = operands[isa_retval + 0]; | |
2104 | rtx numargs = operands[isa_retval + 1]; | |
2105 | ||
d3ae8277 | 2106 | if (TARGET_BSR) |
0d7e008e | 2107 | { |
d3ae8277 | 2108 | call = gen_rtx (CALL, VOIDmode, call_target, numargs); |
0d7e008e | 2109 | } |
d3ae8277 | 2110 | else { |
0d7e008e | 2111 | |
d3ae8277 SC |
2112 | if (GET_CODE (call_target) == MEM) |
2113 | { | |
2114 | call_target = force_reg (Pmode, | |
2115 | XEXP (call_target, 0)); | |
2116 | } | |
2117 | if (TARGET_SMALLCALL) | |
2118 | { | |
2119 | rtx tmp = gen_reg_rtx (SImode); | |
2120 | rtx r1 = gen_rtx (REG, SImode, 1); | |
2121 | emit_move_insn (tmp, gen_rtx (SYMBOL_REF, SImode, "__saveargs")); | |
2122 | emit_move_insn (r1, call_target); | |
2123 | emit_insn (gen_rtx (USE, VOIDmode, r1)); | |
2124 | call_target = tmp; | |
2125 | } | |
0d7e008e | 2126 | |
d3ae8277 SC |
2127 | call = gen_rtx (CALL, VOIDmode, gen_rtx (MEM, SImode, call_target), numargs); |
2128 | } | |
0d7e008e SC |
2129 | if (isa_retval) |
2130 | { | |
2131 | call = gen_rtx (SET, VOIDmode, ret, call); | |
2132 | } | |
2133 | ||
2134 | emit_call_insn (gen_rtx (PARALLEL, VOIDmode, | |
2135 | gen_rtvec (2, | |
2136 | call, | |
d3ae8277 | 2137 | gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, SImode, 17))))); |
0d7e008e SC |
2138 | |
2139 | } | |
2140 | \f | |
2141 | ||
2142 | /* Predicates used by the templates */ | |
2143 | ||
2144 | ||
2145 | /* Returns 1 if OP can be source of a simple move operation. | |
2146 | Same as general_operand, but a LABEL_REF is valid, PRE_DEC is | |
2147 | invalid as are subregs of system registers. */ | |
2148 | ||
2149 | int | |
2150 | general_movsrc_operand (op, mode) | |
2151 | rtx op; | |
2152 | enum machine_mode mode; | |
2153 | { | |
2154 | /* Any MEM(label_ref) is ok, that's a pcrel load */ | |
2155 | if (GET_CODE (op) == MEM && | |
2156 | GET_CODE (XEXP (op, 0)) == LABEL_REF) | |
b9654711 SC |
2157 | return 1; |
2158 | ||
0d7e008e SC |
2159 | /* No predec allowed */ |
2160 | ||
2161 | if (GET_CODE (op) == MEM | |
2162 | && GET_CODE (XEXP (op, 0)) == PRE_DEC) | |
2163 | return 0; | |
2164 | ||
2165 | if ((mode == QImode || mode == HImode) | |
2166 | && (GET_CODE (op) == SUBREG | |
2167 | && GET_CODE (XEXP (op, 0)) == REG | |
2168 | && system_reg_operand (XEXP (op, 0), mode))) | |
2169 | return 0; | |
2170 | ||
2171 | if (GET_CODE (op) == CONST_INT) | |
2172 | { | |
2173 | int i = INTVAL (op); | |
2174 | return CONST_OK_FOR_I (i); | |
2175 | } | |
2176 | return general_operand (op, mode); | |
b9654711 SC |
2177 | } |
2178 | ||
0d7e008e SC |
2179 | |
2180 | /* Returns 1 if OP can be a destination of a move. | |
2181 | Same as general_operand, but no preinc allowed. */ | |
2182 | ||
b9654711 | 2183 | int |
0d7e008e SC |
2184 | general_movdst_operand (op, mode) |
2185 | rtx op; | |
2186 | enum machine_mode mode; | |
b9654711 | 2187 | { |
0d7e008e | 2188 | if (GET_CODE (op) == MEM |
d3ae8277 SC |
2189 | && (GET_CODE (XEXP (op, 0)) == PRE_INC |
2190 | || GET_CODE (XEXP (op, 0)) == POST_INC | |
2191 | || GET_CODE (XEXP (op, 0)) == POST_DEC)) | |
0d7e008e | 2192 | return 0; |
d3ae8277 | 2193 | |
0d7e008e SC |
2194 | return general_operand (op, mode); |
2195 | } | |
2196 | ||
2197 | ||
d3ae8277 SC |
2198 | |
2199 | /* Returns 1 if OP is valid destination for a bsr. */ | |
2200 | ||
2201 | int | |
2202 | bsr_operand (op, mode) | |
2203 | rtx op; | |
2204 | enum machine_mode mode; | |
2205 | { | |
2206 | if (GET_CODE (op) == SYMBOL_REF) | |
2207 | return 1; | |
2208 | return 0; | |
2209 | } | |
2210 | ||
0d7e008e SC |
2211 | /* Returns 1 if OP is an immediate ok for a byte index. */ |
2212 | ||
2213 | int | |
2214 | byte_index_operand (op, mode) | |
2215 | rtx op; | |
2216 | enum machine_mode mode; | |
2217 | { | |
2218 | return (GET_CODE (op) == CONST_INT | |
2219 | && INTVAL (op) >= 0 | |
2220 | && INTVAL (op) <= 15); | |
2221 | } | |
2222 | ||
2223 | /* Returns 1 if OP is a pop operand. */ | |
2224 | ||
2225 | int | |
2226 | pop_operand (op, mode) | |
2227 | rtx op; | |
2228 | enum machine_mode mode; | |
2229 | { | |
2230 | if (GET_CODE (op) != MEM) | |
2231 | return 0; | |
2232 | ||
2233 | if (GET_MODE (op) != mode) | |
2234 | return 0; | |
2235 | ||
2236 | op = XEXP (op, 0); | |
2237 | ||
2238 | if (GET_CODE (op) != POST_INC) | |
2239 | return 0; | |
2240 | ||
2241 | return XEXP (op, 0) == stack_pointer_rtx; | |
2242 | } | |
2243 | ||
2244 | ||
2245 | /* Returns 1 if OP is a normal arithmetic register. */ | |
2246 | ||
2247 | int | |
2248 | arith_reg_operand (op, mode) | |
2249 | rtx op; | |
2250 | enum machine_mode mode; | |
2251 | { | |
2252 | if (register_operand (op, mode)) | |
2253 | { | |
2254 | if (GET_CODE (op) == REG) | |
2255 | return (REGNO (op) != T_REG | |
2256 | && REGNO (op) != PR_REG); | |
2257 | return 1; | |
2258 | } | |
2259 | return 0; | |
2260 | } | |
2261 | ||
2262 | /* Returns 1 if OP is MACL, MACH or PR. */ | |
2263 | ||
2264 | int | |
2265 | system_reg_operand (op, mode) | |
2266 | rtx op; | |
2267 | enum machine_mode mode; | |
2268 | { | |
2269 | if (GET_CODE (op) == REG) | |
2270 | { | |
2271 | switch (REGNO (op)) | |
2272 | { | |
2273 | case PR_REG: | |
2274 | case MACL_REG: | |
2275 | case MACH_REG: | |
2276 | return 1; | |
2277 | } | |
2278 | } | |
2279 | return 0; | |
2280 | } | |
2281 | ||
2282 | ||
2283 | /* Returns 1 if OP is a valid source operand for an arithmetic insn. */ | |
2284 | ||
2285 | int | |
2286 | arith_operand (op, mode) | |
2287 | rtx op; | |
2288 | enum machine_mode mode; | |
2289 | { | |
2290 | if (arith_reg_operand (op, mode)) | |
2291 | return 1; | |
2292 | ||
2293 | if (GET_CODE (op) == CONST_INT) | |
2294 | { | |
2295 | if (CONST_OK_FOR_I (INTVAL (op))) | |
2296 | return 1; | |
2297 | } | |
2298 | return 0; | |
2299 | } | |
2300 | ||
2301 | ||
2302 | /* Returns 1 if OP is a valid source operand for a logical operation. */ | |
2303 | ||
2304 | int | |
2305 | logical_operand (op, mode) | |
2306 | rtx op; | |
2307 | enum machine_mode mode; | |
2308 | { | |
2309 | if (arith_reg_operand (op, mode)) | |
2310 | return 1; | |
2311 | ||
2312 | if (GET_CODE (op) == CONST_INT) | |
2313 | { | |
2314 | if (CONST_OK_FOR_L (INTVAL (op))) | |
2315 | return 1; | |
2316 | } | |
2317 | return 0; | |
b9654711 | 2318 | } |
0d7e008e | 2319 | |
d3ae8277 SC |
2320 | /* Returns 1 if OP is a valid operand for a MAC instruction, |
2321 | either a register or indirect memory. For now we don't | |
2322 | try and recognise a mac insn */ | |
2323 | ||
2324 | int | |
2325 | mac_operand (op, mode) | |
2326 | rtx op; | |
2327 | enum machine_mode mode; | |
2328 | { | |
2329 | if (arith_reg_operand (op, mode)) | |
2330 | return 1; | |
2331 | #if 0 | |
2332 | Turned off till mac is understood | |
2333 | if (GET_CODE (op) == MEM) | |
2334 | return 1; | |
2335 | #endif | |
2336 | return 0; | |
2337 | } | |
2338 | ||
2339 | /* Determine where to put an argument to a function. | |
2340 | Value is zero to push the argument on the stack, | |
2341 | or a hard register in which to store the argument. | |
2342 | ||
2343 | MODE is the argument's machine mode. | |
2344 | TYPE is the data type of the argument (as a tree). | |
2345 | This is null for libcalls where that information may | |
2346 | not be available. | |
2347 | CUM is a variable of type CUMULATIVE_ARGS which gives info about | |
2348 | the preceding args and about the function being called. | |
2349 | NAMED is nonzero if this argument is a named parameter | |
2350 | (otherwise it is an extra parameter matching an ellipsis). */ | |
2351 | ||
2352 | rtx | |
2353 | sh_function_arg (cum, mode, type, named) | |
2354 | CUMULATIVE_ARGS cum; | |
2355 | enum machine_mode mode; | |
2356 | tree type; | |
2357 | int named; | |
2358 | { | |
2359 | if (named) | |
2360 | { | |
2361 | int rr = (ROUND_REG ((cum), (mode))); | |
2362 | ||
2363 | if (rr < NPARM_REGS) | |
2364 | { | |
2365 | return ((((mode) != BLKmode | |
2366 | && ((type)==0 || ! TREE_ADDRESSABLE ((tree)(type))) | |
2367 | && ((type)==0 || (mode) != BLKmode | |
2368 | || (TYPE_ALIGN ((type)) % PARM_BOUNDARY == 0)) | |
2369 | ? gen_rtx (REG, (mode), | |
2370 | (FIRST_PARM_REG + rr)): 0))); | |
2371 | ||
2372 | } | |
2373 | } | |
2374 | return 0; | |
2375 | } | |
2376 | ||
2377 | /* For an arg passed partly in registers and partly in memory, | |
2378 | this is the number of registers used. | |
2379 | For args passed entirely in registers or entirely in memory, zero. | |
2380 | Any arg that starts in the first 4 regs but won't entirely fit in them | |
2381 | needs partial registers on the SH. */ | |
2382 | ||
2383 | int | |
2384 | sh_function_arg_partial_nregs (CUM, MODE, TYPE, NAMED) | |
2385 | CUMULATIVE_ARGS CUM; | |
2386 | enum machine_mode MODE; | |
2387 | tree TYPE; | |
2388 | int NAMED; | |
2389 | { | |
2390 | if ((CUM) < NPARM_REGS) | |
2391 | { | |
2392 | if (((TYPE)==0 || ! TREE_ADDRESSABLE ((tree)(TYPE))) | |
2393 | && ((TYPE)==0 || (MODE) != BLKmode | |
2394 | || (TYPE_ALIGN ((TYPE)) % PARM_BOUNDARY == 0)) | |
2395 | && ((CUM) + ((MODE) == BLKmode | |
2396 | ? ROUND_ADVANCE (int_size_in_bytes (TYPE)) | |
2397 | : ROUND_ADVANCE (GET_MODE_SIZE (MODE))) - NPARM_REGS > 0)) | |
2398 | { | |
2399 | return NPARM_REGS - CUM; | |
2400 | } | |
2401 | } | |
2402 | return 0; | |
2403 | } | |
2404 |