gcc/gcc/config/sh/sh.c

2514 lines
57 KiB
C
Raw Normal View History

1993-04-30 16:29:19 +02:00
/* Output routines for GCC for Hitachi Super-H
Copyright (C) 1993, 1994 Free Software Foundation, Inc.
1993-04-30 16:29:19 +02:00
This file is part of GNU CC.
1993-04-30 16:29:19 +02:00
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
1993-04-30 16:29:19 +02:00
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
1993-04-30 16:29:19 +02:00
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
1993-04-30 16:29:19 +02:00
/* Contributed by Steve Chamberlain (sac@cygnus.com) */
#include <stdio.h>
#include "assert.h"
#include "config.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "insn-flags.h"
#include "tree.h"
#include "output.h"
1993-04-30 16:29:19 +02:00
#include "insn-attr.h"
#include "flags.h"
#include "obstack.h"
#include "expr.h"
#define MSW (TARGET_LITTLE_ENDIAN ? 1 : 0)
#define LSW (TARGET_LITTLE_ENDIAN ? 0 : 1)
static rtx add_constant ();
1993-04-30 16:29:19 +02:00
int pragma_interrupt;
int pragma_trapa;
1993-04-30 16:29:19 +02:00
int current_function_anonymous_args;
extern int current_function_pretend_args_size;
1993-06-29 02:10:35 +02:00
extern char *version_string;
extern int flag_traditional;
static rtx shiftsyms[32];
struct rtx_def *table_lab;
1993-06-29 02:10:35 +02:00
enum attr_cpu sh_cpu; /* target cpu */
1993-04-30 16:29:19 +02:00
char *max_si;
char *max_hi;
int max_count_si;
int max_count_hi;
1993-04-30 16:29:19 +02:00
/* Global variables for machine-dependent things. */
/* Saved operands from the last compare to use when we generate an scc
or bcc insn. */
1993-04-30 16:29:19 +02:00
rtx sh_compare_op0;
rtx sh_compare_op1;
/* Provides the class number of the smallest class containing
reg number */
int regno_reg_class[FIRST_PSEUDO_REGISTER] =
{
R0_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
1993-04-30 16:29:19 +02:00
GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
GENERAL_REGS, PR_REGS, T_REGS, NO_REGS,
MAC_REGS, MAC_REGS,
1993-04-30 16:29:19 +02:00
};
/* Provide reg_class from a letter such as appears in the machine
description. */
enum reg_class reg_class_from_letter[] =
{
/* a */ NO_REGS, /* b */ NO_REGS, /* c */ NO_REGS, /* d */ NO_REGS,
/* e */ NO_REGS, /* f */ NO_REGS, /* g */ NO_REGS, /* h */ NO_REGS,
/* i */ NO_REGS, /* j */ NO_REGS, /* k */ NO_REGS, /* l */ PR_REGS,
/* m */ NO_REGS, /* n */ NO_REGS, /* o */ NO_REGS, /* p */ NO_REGS,
/* q */ NO_REGS, /* r */ NO_REGS, /* s */ NO_REGS, /* t */ T_REGS,
/* u */ NO_REGS, /* v */ NO_REGS, /* w */ NO_REGS, /* x */ MAC_REGS,
/* y */ NO_REGS, /* z */ R0_REGS
};
/* Value is 1 if register/mode pair is acceptable on SH. Even
registers can hold DIs and DF values. The rest can only hold
SI's efficiently */
#define REG_ODD \
( (1 << (int) QImode) | (1 << (int) HImode) | (1 << (int) SImode) \
| (1 << (int) QFmode) | (1 << (int) HFmode) | (1 << (int) SFmode) \
| (1 << (int) CQImode) | (1 << (int) CHImode)| (1<< (int)DFmode) | (1<<(int)DImode))
1993-04-30 16:29:19 +02:00
#define REG_EVEN \
(REG_ODD | (1 << (int) CSImode) | (1 << (int) SCmode))
1993-06-29 02:10:35 +02:00
#define SI_ONLY (1<<(int)SImode)
int hard_regno_mode_ok[] =
{
REG_EVEN, REG_ODD, REG_EVEN, REG_ODD,
REG_EVEN, REG_ODD, REG_EVEN, REG_ODD,
REG_EVEN, REG_ODD, REG_EVEN, REG_ODD,
REG_EVEN, REG_ODD, REG_EVEN, REG_ODD,
REG, 0, SI_ONLY, SI_ONLY,
SI_ONLY, SI_ONLY
};
1993-06-29 02:10:35 +02:00
1993-04-30 16:29:19 +02:00
/* Local label counter, used for constants in the pool and inside
pattern branches. */
static int lf = 100;
1993-04-30 16:29:19 +02:00
/* Number of bytes pushed for anonymous args, used to pass information
between expand_prologue and expand_epilogue. */
static int extra_push;
1993-04-30 16:29:19 +02:00
1993-06-29 02:10:35 +02:00
void
push (rn)
int rn;
1993-04-30 16:29:19 +02:00
{
rtx x ;
x= emit_insn (gen_push (gen_rtx (REG, SImode, rn)));
REG_NOTES (x) = gen_rtx (EXPR_LIST, REG_INC,
gen_rtx(REG, SImode, STACK_POINTER_REGNUM), 0);
1993-04-30 16:29:19 +02:00
}
1993-06-29 02:10:35 +02:00
void
pop (rn)
int rn;
1993-04-30 16:29:19 +02:00
{
rtx x;
x = emit_insn (gen_pop (gen_rtx (REG, SImode, rn)));
REG_NOTES (x) = gen_rtx (EXPR_LIST, REG_INC,
gen_rtx(REG, SImode, STACK_POINTER_REGNUM), 0);
1993-04-30 16:29:19 +02:00
}
1993-06-29 02:10:35 +02:00
/* Adjust the stack and return the number of bytes taken to do it */
static rtx lastreg;
int lastval;
1993-06-29 02:10:35 +02:00
static void
output_stack_adjust (size)
1993-06-29 02:10:35 +02:00
int size;
1993-04-30 16:29:19 +02:00
{
1993-06-29 02:10:35 +02:00
if (size)
1993-04-30 16:29:19 +02:00
{
1993-06-29 02:10:35 +02:00
rtx val = GEN_INT (size);
rtx insn;
1993-04-30 16:29:19 +02:00
if (!CONST_OK_FOR_I (size))
1993-06-29 02:10:35 +02:00
{
lastreg = gen_rtx (REG, SImode, 3);
lastval = size;
emit_insn (gen_movsi (lastreg, val));
val = lastreg;
1993-06-29 02:10:35 +02:00
}
1993-04-30 16:29:19 +02:00
insn = gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, val);
1993-06-29 02:10:35 +02:00
emit_insn (insn);
}
1993-04-30 16:29:19 +02:00
}
1993-06-29 02:10:35 +02:00
/* Generate code to push the regs specified in the mask, and return
the number of bytes the insns take. */
1993-04-30 16:29:19 +02:00
static void
1993-06-29 02:10:35 +02:00
push_regs (mask)
int mask;
1993-04-30 16:29:19 +02:00
{
int i;
1993-06-29 02:10:35 +02:00
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
1993-04-30 16:29:19 +02:00
{
1993-06-29 02:10:35 +02:00
if (mask & (1 << i))
1993-04-30 16:29:19 +02:00
{
1993-06-29 02:10:35 +02:00
push (i);
1993-04-30 16:29:19 +02:00
}
}
}
/* Print an instruction which would have gone into a delay slot after
another instruction, but couldn't because the other instruction expanded
into a sequence where putting the slot insn at the end wouldn't work. */
1993-04-30 16:29:19 +02:00
static void
1993-06-29 02:10:35 +02:00
print_slot (insn)
rtx insn;
{
final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file, optimize, 0, 1);
1993-04-30 16:29:19 +02:00
1993-06-29 02:10:35 +02:00
INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
1993-04-30 16:29:19 +02:00
}
/* Work out the registers which need to be saved, both as a mask and a
count.
1993-04-30 16:29:19 +02:00
If doing a pragma interrupt function, then push all regs used by the function,
and if we call another function (we can tell by looking at PR), make sure that all the
regs it clobbers are safe too.
*/
static int
calc_live_regs (count_ptr)
int *count_ptr;
1993-04-30 16:29:19 +02:00
{
int reg;
int live_regs_mask = 0;
int count = 0;
1993-04-30 16:29:19 +02:00
for (reg = 0; reg < FIRST_PSEUDO_REGISTER; reg++)
{
if (reg == ARG_POINTER_REGNUM)
continue;
if (reg == T_REG)
continue;
if (reg == GBR_REG)
continue;
if (pragma_interrupt && !pragma_trapa)
{
/* Need to save all the regs ever live */
if ((regs_ever_live[reg]
|| (call_used_regs[reg] && regs_ever_live[PR_REG]))
&& reg != 15)
{
live_regs_mask |= 1 << reg;
count++;
}
}
else
1993-04-30 16:29:19 +02:00
{
/* Only push those regs which are used and need to be saved */
if (regs_ever_live[reg] && !call_used_regs[reg])
{
count++;
live_regs_mask |= (1 << reg);
}
1993-04-30 16:29:19 +02:00
}
}
*count_ptr = count;
1993-04-30 16:29:19 +02:00
return live_regs_mask;
}
1993-06-29 02:10:35 +02:00
/* This returns true if INSN is a conditional branch whose delay slot
has been filled. This indicates that it must be a bf.s/bt.s.
??? This function could be eliminated. */
1993-04-30 16:29:19 +02:00
1993-06-29 02:10:35 +02:00
static int
need_slot (insn)
rtx insn;
1993-04-30 16:29:19 +02:00
{
return insn;
1993-04-30 16:29:19 +02:00
}
1993-06-29 02:10:35 +02:00
1993-04-30 16:29:19 +02:00
/* Print the operand address in x to the stream */
void
print_operand_address (stream, x)
FILE *stream;
rtx x;
{
switch (GET_CODE (x))
{
case REG:
fprintf (stream, "@%s", reg_names[REGNO (x)]);
break;
case PLUS:
{
rtx base = XEXP (x, 0);
rtx index = XEXP (x, 1);
if (GET_CODE (base) != REG)
{
/* Ensure that BASE is a register (one of them must be). */
rtx temp = base;
base = index;
index = temp;
}
switch (GET_CODE (index))
{
case CONST_INT:
fprintf (stream, "@(%d,%s)",
INTVAL (index),
reg_names[REGNO (base)]);
break;
case REG:
1993-06-29 02:10:35 +02:00
fprintf (stream, "@(r0,%s)",
reg_names[MAX (REGNO (base), REGNO (index))]);
1993-04-30 16:29:19 +02:00
break;
default:
1993-06-29 02:10:35 +02:00
debug_rtx (x);
1993-04-30 16:29:19 +02:00
abort ();
}
}
break;
case PRE_DEC:
fprintf (stream, "@-%s", reg_names[REGNO (XEXP (x, 0))]);
break;
case POST_INC:
fprintf (stream, "@%s+", reg_names[REGNO (XEXP (x, 0))]);
break;
default:
output_addr_const (stream, x);
break;
}
}
/* Print operand x (an rtx) in assembler syntax to file stream
according to modifier code.
1993-06-29 02:10:35 +02:00
'.' print a .s if insn needs delay slot
'#' output a nop if there is nothing to put in the delay slot
'@' print rte or rts depending upon pragma interruptness
'R' print the LSW of a dp value - changes if in little endian
'T' print the next word of a dp value - same as 'R' in big endian mode.
'S' print the MSW of a dp value - changes if in little endian
1993-06-29 02:10:35 +02:00
'O' print a constant without the #
'M' print a constant as its negative
'N' print insides of a @++ or @-- o */
1993-04-30 16:29:19 +02:00
void
print_operand (stream, x, code)
FILE *stream;
rtx x;
int code;
{
switch (code)
{
1993-06-29 02:10:35 +02:00
case '.':
if (need_slot (final_sequence))
fprintf (stream, ".s");
break;
case '@':
if (pragma_interrupt)
fprintf (stream, "rte");
else
fprintf (stream, "rts");
break;
1993-04-30 16:29:19 +02:00
case '#':
/* Output a nop if there's nothing in the delay slot */
if (dbr_sequence_length () == 0)
{
fprintf (stream, "\n\tnop");
1993-04-30 16:29:19 +02:00
}
break;
1993-06-29 02:10:35 +02:00
case 'O':
output_addr_const (stream, x);
1993-04-30 16:29:19 +02:00
break;
1993-06-29 02:10:35 +02:00
case 'M':
fprintf (asm_out_file, "#%d", -INTVAL (x));
break;
case 'N':
fputs (reg_names[REGNO (XEXP (XEXP (x, 0), 0))], (stream));
break;
1993-04-30 16:29:19 +02:00
case 'R':
/* LSW of a double */
switch (GET_CODE (x))
{
case REG:
fputs (reg_names[REGNO (x) + LSW], (stream));
break;
case MEM:
print_operand_address (stream, XEXP (adj_offsettable_operand (x, LSW *4), 0));
break;
}
break;
case 'T':
/* Next word of a double */
1993-04-30 16:29:19 +02:00
switch (GET_CODE (x))
{
case REG:
fputs (reg_names[REGNO (x) + 1], (stream));
break;
case MEM:
print_operand_address (stream, XEXP (adj_offsettable_operand (x,1 *4), 0));
break;
}
break;
case 'S':
/* MSW of a double */
switch (GET_CODE (x))
{
case REG:
fputs (reg_names[REGNO (x) + MSW], (stream));
break;
case MEM:
print_operand_address (stream, XEXP (adj_offsettable_operand (x, MSW *4), 0));
1993-04-30 16:29:19 +02:00
break;
}
break;
default:
switch (GET_CODE (x))
{
case REG:
fputs (reg_names[REGNO (x)], (stream));
break;
case MEM:
output_address (XEXP (x, 0));
break;
default:
fputc ('#', stream);
output_addr_const (stream, x);
break;
}
break;
}
}
static int
sextb (x)
int x;
{
x &= 0xff;
if (x > 127)
{
x = -256 + x;
}
return x;
}
1993-06-29 02:10:35 +02:00
1993-04-30 16:29:19 +02:00
/* Take a move with integer constant source in OPERANDS, see if it can be generated by
devious shifting. If so, generate the instruction sequence and return 1, otherwise
return 0.
OPERANDS[0] Destination register
OPERANDS[1] Source constant
00000000 00000000 00000000 0NNNNNNNN simple load
00000000 00000000 00000000 NNNNNNNN0 load and shift by 1
00000000 00000000 0000000N NNNNNNN00 load and shift by 2
00000000 00000000 0NNNNNNN 000000000 load and shift by 8
00000000 0NNNNNNN 00000000 000000000 load and shift by 16
N0000000 00000000 00000000 00NNNNNNN load and rotate right
11111111 11111111 11111111 1NNNNNNNN simple load
11111111 11111111 11111111 NNNNNNNN0 load and shift by 1
11111111 11111111 1111111N NNNNNNN00 load and shift by 2
11111111 11111111 1NNNNNNN 000000000 load and shift by 8
11111111 1NNNNNNN 00000000 000000000 load and shift by 16
N1111111 11111111 11111111 11NNNNNNN load and rotate right
00000000 00000000 00000000 1NNNNNNNN load and zero extend byte
00000000 00000000 11111111 1NNNNNNNN load and zero extend word
??? Can add cases using swap.b and swap.w.
Can add cases using andi to get `1s 1s 1s 0NNNNNN1'.
Can add many more cases for TARGET_CLEN3, but doubt their usefulness.
*/
static int
synth_constant (operands, mode)
rtx operands[];
enum machine_mode mode;
1993-04-30 16:29:19 +02:00
{
rtx dst;
int i = INTVAL (operands[1]) & 0xffffffff;
if (CONST_OK_FOR_I (i))
return 0;
1993-04-30 16:29:19 +02:00
if (TARGET_CLEN0 && mode != QImode)
return 0;
if (mode != SImode)
{
if (reload_in_progress)
return 0;
dst = gen_reg_rtx (SImode);
}
else
{
dst = operands[0];
}
1993-04-30 16:29:19 +02:00
/* 00000000 00000000 11111111 1NNNNNNNN load and zero extend word */
if ((i & 0xffffff80) == 0x0000ff80)
{
emit_move_insn (dst, GEN_INT (sextb (i)));
emit_insn (gen_zero_extendhisi2 (dst, gen_lowpart (HImode, dst)));
}
/* 00000000 00000000 00000000 1NNNNNNNN load and zero extend byte */
else if ((i & 0xffffff80) == 0x00000080)
1993-04-30 16:29:19 +02:00
{
emit_move_insn (dst, GEN_INT (sextb (i)));
emit_insn (gen_zero_extendqisi2 (dst, gen_lowpart (QImode, dst)));
1993-04-30 16:29:19 +02:00
}
/* 00000000 00000000 00000000 NNNNNNNN0 load and shift by 1
11111111 11111111 11111111 NNNNNNNN0 load and shift by 1 */
else if ((i & 0xffffff01) == 0
|| (i & 0xffffff01) == 0xffffff00)
{
emit_move_insn (dst, GEN_INT (sextb (i >> 1)));
emit_insn (gen_ashlsi3_k (dst, dst, GEN_INT (1)));
}
/* 00000000 00000000 0000000N NNNNNNN00 load and shift by 2
11111111 11111111 1111111N NNNNNNN00 load and shift by 2*/
else if ((i & 0xfffffe03) == 0
|| (i & 0xfffffe03) == 0xfffffe00)
{
emit_move_insn (dst, GEN_INT (sextb (i >> 2)));
emit_insn (gen_ashlsi3_k (dst, dst, GEN_INT (2)));
}
/* 00000000 00000000 0NNNNNNN 000000000 load and shift by 8
11111111 11111111 1NNNNNNN 000000000 load and shift by 8 */
1993-04-30 16:29:19 +02:00
else if ((i & 0xffff80ff) == 0
|| (i & 0xffff80ff) == 0xffff8000)
1993-04-30 16:29:19 +02:00
{
emit_move_insn (dst, GEN_INT (sextb (i >> 8)));
emit_insn (gen_ashlsi3_k (dst, dst, GEN_INT (8)));
}
/* 00000000 0NNNNNNN 00000000 000000000 load and shift by 16
11111111 1NNNNNNN 00000000 000000000 load and shift by 16 */
else if ((i & 0xff80ffff) == 0x00000000
|| (i & 0xff80ffff) == 0xff800000)
{
emit_move_insn (dst, GEN_INT (sextb (i >> 16)));
emit_insn (gen_ashlsi3_k (dst, dst, GEN_INT (16)));
1993-04-30 16:29:19 +02:00
}
/* 00000000 00000000 0NNNNNNN 0NNNNNNNN load shift 8 and add */
else if ((i & 0xffff8080) == 0 && TARGET_CLEN3)
{
emit_move_insn (dst, GEN_INT (sextb (i >> 8)));
emit_insn (gen_ashlsi3_k (dst, dst, GEN_INT (8)));
emit_insn (gen_addsi3 (dst, dst, GEN_INT (i & 0x7f)));
}
else
return 0;
1993-04-30 16:29:19 +02:00
if (mode == DImode)
{
/* Moving from SI to DI, we've got to zero out the high part */
emit_insn (gen_rtx (SET, VOIDmode,
gen_rtx (SUBREG, SImode, operands[0], 0),
dst));
emit_insn (gen_rtx (SET, VOIDmode,
gen_rtx (SUBREG, SImode, operands[0], 1),
const0_rtx));
}
else if (mode != SImode)
1993-04-30 16:29:19 +02:00
{
emit_insn (gen_rtx (SET, VOIDmode, operands[0],
gen_rtx (SUBREG, mode, dst, 0)));
1993-04-30 16:29:19 +02:00
}
return 1;
1993-04-30 16:29:19 +02:00
}
/* Emit code to perform a block move. Choose the best method.
OPERANDS[0] is the destination.
OPERANDS[1] is the source.
OPERANDS[2] is the size.
OPERANDS[3] is the alignment safe to use. */
int
expand_block_move (operands)
rtx *operands;
{
int align = INTVAL (operands[3]);
int constp = (GET_CODE (operands[2]) == CONST_INT);
int bytes = (constp ? INTVAL (operands[2]) : 0);
enum machine_mode mode;
/* IF odd then fail */
if (!constp || bytes <= 0)
return 0;
/* Don't expand if we'd make the code bigger and we don't want big code */
if (bytes > 8 && TARGET_SMALLCODE)
return 0;
switch (align)
{
case 1:
mode = QImode;
break;
case 2:
mode = HImode;
break;
default:
mode = SImode;
align = 4;
}
if (mode == SImode && constp && bytes < 64 && (bytes % 4 == 0))
{
char entry[30];
tree entry_name;
rtx func_addr_rtx;
rtx r4 = gen_rtx (REG, SImode, 4);
rtx r5 = gen_rtx (REG, SImode, 5);
sprintf (entry, "__movstr%s%d", GET_MODE_NAME (mode), bytes);
entry_name = get_identifier (entry);
func_addr_rtx = copy_to_mode_reg (Pmode,
gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (entry_name)));
emit_insn (gen_move_insn (r4, XEXP (operands[0], 0)));
emit_insn (gen_move_insn (r5, XEXP (operands[1], 0)));
emit_insn (gen_block_move_real (func_addr_rtx));
return 1;
}
if (mode == SImode && constp && (bytes % 4 == 0))
{
tree entry_name;
rtx func_addr_rtx;
rtx r4 = gen_rtx (REG, SImode, 4);
rtx r5 = gen_rtx (REG, SImode, 5);
rtx r6 = gen_rtx (REG, SImode, 6);
entry_name = get_identifier ("__movstr");
func_addr_rtx = copy_to_mode_reg (Pmode,
gen_rtx (SYMBOL_REF, Pmode,
IDENTIFIER_POINTER (entry_name)));
emit_insn (gen_move_insn (r4, XEXP (operands[0], 0)));
emit_insn (gen_move_insn (r5, XEXP (operands[1], 0)));
/* r6 controls the size of the move, 16 is decremented from it
for each 64 bytes moved, then the -ve bit is used as an index into a
list of move instructions like this:
{
do {
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
..etc.. 16 in all
*dst++ = *src++;
*dst++ = *src++;
size -= 16;
} while (size > 0);
switch (size)
{
case -15:
*dst++ = *src++;
case -14:
*dst++ = *src++;
.. etc.. ;
case -2:
*dst++ = *src++;
case -1:
*dst++ = *src++;
case 0:
;
}
}
eg, a 72 byte move would be set up with size(r6) = 14, for one
iteration through the big while loop, and a switch of -2 for the last part */
{
int final_switch = 16 - ((bytes / 4) % 16);
int while_loop = ((bytes / 4) / 16 - 1) * 16;
emit_insn (gen_move_insn (r6, GEN_INT (while_loop + final_switch)));
emit_insn (gen_block_lump_real (func_addr_rtx));
return 1;
}
}
return 0;
}
1993-04-30 16:29:19 +02:00
/* Prepare operands for a move define_expand; specifically, one of the
1993-06-29 02:10:35 +02:00
operands must be in a register. Take this chance to remove
addressing modes which can't be coped with very well. */
1993-04-30 16:29:19 +02:00
1993-06-29 02:10:35 +02:00
int
1993-04-30 16:29:19 +02:00
prepare_move_operands (operands, mode)
rtx operands[];
enum machine_mode mode;
{
if (!(reload_in_progress || reload_completed)
&& ((!register_operand (operands[0], mode)
&& !register_operand (operands[1], mode))
|| GET_CODE (operands[1]) == PLUS))
1993-04-30 16:29:19 +02:00
{
/* copy the source to a register */
operands[1] = copy_to_mode_reg (mode, operands[1]);
}
if ((mode == SImode || mode == HImode || mode == QImode)
&& GET_CODE (operands[1]) == CONST_INT)
1993-06-29 02:10:35 +02:00
{
return synth_constant (operands, mode);
}
if (mode == DFmode || mode == DImode)
{
rtx src = operands[1];
rtx dst = operands[0];
rtx insns;
1993-06-29 02:10:35 +02:00
if (src == dst)
1993-06-29 02:10:35 +02:00
{
emit_insn (gen_rtx (SET, VOIDmode, dst, src));
return 1;
}
1993-06-29 02:10:35 +02:00
if (GET_CODE (src) == REG &&
REGNO (src) >= FIRST_PSEUDO_REGISTER)
return 0;
if (GET_CODE (dst) == REG &&
REGNO (dst) >= FIRST_PSEUDO_REGISTER)
return 0;
if (push_operand (dst, mode))
return 0;
if (GET_CODE (src) == CONST_DOUBLE)
src = force_const_mem (DFmode, src);
if (reload_in_progress)
{
if (!(offsettable_memref_p (src) || register_operand (src, mode)))
return 0;
if (!(offsettable_memref_p (dst) || register_operand (dst,
mode)))
return 0;
}
start_sequence ();
if (GET_CODE (operands[0]) != REG
|| !refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1, operands[1], 0))
{
emit_move_insn (operand_subword (dst, 0, 1, mode),
operand_subword_force (src, 0, mode));
emit_move_insn (operand_subword (dst, 1, 1, mode),
operand_subword_force (src, 1, mode));
}
else
{
emit_move_insn (operand_subword (dst, 1, 1, mode),
operand_subword_force (src, 1, mode));
emit_move_insn (operand_subword (dst, 0, 1, mode),
operand_subword_force (src, 0, mode));
1993-06-29 02:10:35 +02:00
}
insns = get_insns ();
end_sequence ();
emit_no_conflict_block (insns, dst, src, 0, src);
return 1;
1993-06-29 02:10:35 +02:00
}
1993-06-29 02:10:35 +02:00
return 0;
1993-04-30 16:29:19 +02:00
}
/* Prepare the operands for an scc instruction; make sure that the
compare has been done. */
rtx
prepare_scc_operands (code)
int code;
1993-04-30 16:29:19 +02:00
{
1993-06-29 02:10:35 +02:00
if (GET_CODE (sh_compare_op0) != REG
|| REGNO (sh_compare_op0) != T_REG)
1993-04-30 16:29:19 +02:00
{
int newcode = code;
1993-04-30 16:29:19 +02:00
/* First need a compare insn */
switch (code)
{
case NE:
/* It isn't possible to handle this case. */
abort ();
case LT:
newcode = GT;
break;
case LE:
newcode = GE;
break;
case LTU:
newcode = GTU;
break;
case LEU:
newcode = GEU;
break;
}
if (newcode != code)
{
rtx tmp = sh_compare_op0;
sh_compare_op0 = sh_compare_op1;
sh_compare_op1 = tmp;
code = newcode;
}
sh_compare_op0 = force_reg (SImode, sh_compare_op0);
if (code != EQ && code != NE
&& (sh_compare_op1 != const0_rtx
|| code == GTU || code == GEU || code == LTU || code == LEU))
sh_compare_op1 = force_reg (SImode, sh_compare_op1);
emit_insn (gen_rtx (SET, VOIDmode,
1993-04-30 16:29:19 +02:00
gen_rtx (REG, SImode, T_REG),
gen_rtx (code, SImode, sh_compare_op0, sh_compare_op1)));
1993-04-30 16:29:19 +02:00
}
1993-06-29 02:10:35 +02:00
return gen_rtx (REG, SImode, T_REG);
1993-04-30 16:29:19 +02:00
}
1993-06-29 02:10:35 +02:00
/* Functions to output assembly code. */
1993-04-30 16:29:19 +02:00
1993-06-29 02:10:35 +02:00
/* Return a sequence of instructions to perform DI or DF move.
1993-04-30 16:29:19 +02:00
1993-06-29 02:10:35 +02:00
Since the SH cannot move a DI or DF in one instruction, we have
to take care when we see overlapping source and dest registers.
*/
1993-04-30 16:29:19 +02:00
char *
output_movedouble (insn, operands, mode)
rtx insn;
1993-04-30 16:29:19 +02:00
rtx operands[];
enum machine_mode mode;
{
1993-06-29 02:10:35 +02:00
rtx dst = operands[0];
rtx src = operands[1];
if (GET_CODE (dst) == MEM
&& GET_CODE (XEXP (dst, 0)) == POST_INC)
{
operands[0] = XEXP (XEXP (dst, 0), 0);
return "mov.l %T1,@(4,%0)\n\tmov.l %1,@%0\n\tadd #8,%0";
}
1993-06-29 02:10:35 +02:00
if (register_operand (dst, mode)
&& register_operand (src, mode))
1993-04-30 16:29:19 +02:00
{
1993-06-29 02:10:35 +02:00
if (REGNO (src) == MACH_REG)
return "sts mach,%S0\n\tsts macl,%R0";
1993-04-30 16:29:19 +02:00
1993-06-29 02:10:35 +02:00
/*
when mov.d r1,r2 do r2->r3 then r1->r2
when mov.d r1,r0 do r1->r0 then r2->r1
*/
1993-06-29 02:10:35 +02:00
if (REGNO (src) + 1 == REGNO (dst))
return "mov %T1,%T0\n\tmov %1,%0";
1993-06-29 02:10:35 +02:00
else
return "mov %1,%0\n\tmov %T1,%T0";
1993-06-29 02:10:35 +02:00
}
else if (GET_CODE (src) == CONST_INT)
1993-04-30 16:29:19 +02:00
{
HOST_WIDE_INT val = INTVAL (src);
int rn = REGNO (operands[0]);
int msw = rn + MSW;
int lsw = rn + LSW;
if (val < 0)
{
fprintf (asm_out_file, "\tmov #-1,r%d\n", msw);
}
1993-04-30 16:29:19 +02:00
else
{
fprintf (asm_out_file, "\tmov #0,r%d\n", msw);
}
1993-04-30 16:29:19 +02:00
fprintf (asm_out_file, "\tmov #%d,r%d\n", val, lsw);
return "";
}
1993-06-29 02:10:35 +02:00
else if (GET_CODE (src) == MEM)
1993-04-30 16:29:19 +02:00
{
1993-06-29 02:10:35 +02:00
int ptrreg1 = -1;
int ptrreg2 = -1;
int dreg = REGNO (dst);
rtx inside = XEXP (src, 0);
1993-04-30 16:29:19 +02:00
if (GET_CODE (inside) == REG)
1993-06-29 02:10:35 +02:00
{
ptrreg1 = REGNO (inside);
}
1993-04-30 16:29:19 +02:00
else if (GET_CODE (inside) == PLUS)
{
rtx lhs = XEXP (inside, 0);
rtx rhs = XEXP (inside, 1);
if (GET_CODE (lhs) == REG)
1993-06-29 02:10:35 +02:00
ptrreg1 = REGNO (lhs);
if (GET_CODE (rhs) == REG)
ptrreg2 = REGNO (rhs);
1993-04-30 16:29:19 +02:00
}
else if (GET_CODE (inside) == LABEL_REF)
{
return "mov.l %1,%0\n\tmov.l %1+4,%T0";
}
else if (GET_CODE (inside) == POST_INC)
{
return "mov.l %1,%0\n\tmov.l %1,%T0";
}
1993-04-30 16:29:19 +02:00
else
abort ();
1993-06-29 02:10:35 +02:00
if ((ptrreg1 >= 0 && ptrreg2 >= 0)
&& (dreg == ptrreg1
|| dreg == ptrreg2
|| dreg + 1 == ptrreg1
|| dreg + 1 == ptrreg2))
{
/* This move clobbers both index registers,
calculate the sum in one register. */
fprintf (asm_out_file, " add %s,%s\n",
1993-06-29 02:10:35 +02:00
reg_names[ptrreg2], reg_names[ptrreg1]);
if (dreg == ptrreg1)
{
/* Copy into dreg+1 first. */
fprintf (asm_out_file, " mov.l @(4,%s),%s\n",
reg_names[ptrreg1],
reg_names[dreg + 1]);
fprintf (asm_out_file, " mov.l @(%s),%s\n",
reg_names[ptrreg1],
reg_names[dreg]);
}
else
{
/* Copy into dreg first. */
fprintf (asm_out_file, " mov.l @(%s),%s\n",
reg_names[ptrreg1],
reg_names[dreg]);
fprintf (asm_out_file, " mov.l @(4,%s),%s\n",
reg_names[ptrreg1],
reg_names[dreg + 1]);
}
warning ("generated complex amode");
return "";
}
/* Work out the safe way to copy */
if (dreg == ptrreg1)
1993-04-30 16:29:19 +02:00
{
1993-06-29 02:10:35 +02:00
/* Copy into the second half first */
return "mov.l %T1,%T0\n\tmov.l %1,%0";
1993-04-30 16:29:19 +02:00
}
}
return "mov.l %1,%0\n\tmov.l %T1,%T0";
1993-04-30 16:29:19 +02:00
}
/* Emit assembly to shift reg by k bits */
char *
1993-06-29 02:10:35 +02:00
output_shift (string, reg, k, code)
1993-04-30 16:29:19 +02:00
char *string;
rtx reg;
rtx k;
1993-06-29 02:10:35 +02:00
int code;
1993-04-30 16:29:19 +02:00
{
int s = INTVAL (k);
if (s < 0)
{
s = -s;
switch (code)
{
case LSHIFTRT:
case ASHIFTRT:
code = ASHIFT;
break;
case ASHIFT:
code = ASHIFTRT;
break;
default:
abort ();
}
}
1993-06-29 02:10:35 +02:00
if (code == ASHIFT && s == 31)
{
/* Shift left by 31 moving into the t bit, clearing and rotating the other way */
fprintf (asm_out_file, "\trotr r%d\n", REGNO (reg));
fprintf (asm_out_file, "\tmov #0,r%d\n", REGNO (reg));
fprintf (asm_out_file, "\trotcr r%d\n", REGNO (reg));
s = 0;
}
if (code == LSHIFTRT && s == 31)
{
fprintf (asm_out_file, "\trotl r%d\n", REGNO (reg));
fprintf (asm_out_file, "\tmov #0,r%d\n", REGNO (reg));
fprintf (asm_out_file, "\trotcl r%d\n", REGNO (reg));
s = 0;
}
1993-04-30 16:29:19 +02:00
while (s)
{
char *out;
int d;
if (s >= 16)
{
d = 16;
out = "16";
}
else if (s >= 8)
{
d = 8;
out = "8";
}
else if (s >= 2)
{
d = 2;
out = "2";
}
else
{
d = 1;
out = "";
}
fprintf (asm_out_file, "\t%s%s\tr%d\n", string, out, REGNO (reg));
s -= d;
}
return "";
}
void
function_epilogue (stream, size)
FILE *stream;
int size;
{
pragma_interrupt = pragma_trapa = 0;
}
1993-04-30 16:29:19 +02:00
/* Return the text of the branch instruction which matches its length
1993-06-29 02:10:35 +02:00
attribute.
This gets tricky if we have an insn in the delay slot of a branch
and the branch needs more than 1 insn to complete. */
int pending_const_table;
/* We can't tell if we need a register as a scratch for the jump
until after branch shortening, and then it's too late to allocate a
register the 'proper' way. These instruction sequences are rare
anyway, so to avoid always using a reg up from our limited set, we'll
grab one when we need one on output. */
char *
output_far_jump (insn, op)
rtx insn;
rtx op;
{
rtx thislab = gen_label_rtx ();
if (dbr_sequence_length ())
{
/* Something to go in what would have been the delay
slot if this had been a short branch. Make sure the
reg we use to generate the branch target address
doesn't conflict */
int i;
rtx vec[2];
vec[0] = thislab;
for (i = 0; i < 8; i++)
{
vec[1] = gen_rtx (REG, SImode, i);
if (!reg_referenced_p (vec[1],
PATTERN (XVECEXP (final_sequence, 0, 1))))
break;
}
1993-06-29 02:10:35 +02:00
print_slot (final_sequence);
output_asm_insn ("mov.l %1,@-r15", vec);
output_asm_insn ("mov.l %O0,%1", vec);
output_asm_insn ("jmp @%1", vec);
output_asm_insn ("mov.l @r15+,%1", vec);
}
else
{
output_asm_insn ("mov.l r13,@-r15", 0);
output_asm_insn ("mov.l %O0,r13", &thislab);
output_asm_insn ("jmp @r13", 0);
output_asm_insn ("mov.l @r15+,r13", 0);
}
1993-06-29 02:10:35 +02:00
output_asm_insn (".align 2", 0);
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (thislab));
output_asm_insn (".long %O0", &op);
return "";
}
1993-04-30 16:29:19 +02:00
char *
output_branch (logic, insn)
int logic;
1993-06-29 02:10:35 +02:00
rtx insn;
1993-04-30 16:29:19 +02:00
{
extern rtx recog_operand[];
int label = lf++;
1993-04-30 16:29:19 +02:00
switch (get_attr_length (insn))
{
case 2:
/* A branch with an unfilled delay slot. */
case 4:
/* Simple branch in range -252..+258 bytes */
1993-06-29 02:10:35 +02:00
return logic ? "bt%. %l0" : "bf%. %l0";
1993-04-30 16:29:19 +02:00
case 6:
/* A branch with an unfilled delay slot. */
case 8:
/* Branch in range -4092..+4098 bytes */
1993-06-29 02:10:35 +02:00
{
rtx oldop = recog_operand[0];
if (need_slot (final_sequence))
{
fprintf (asm_out_file, "\tb%c.s\tLF%d\n", logic ? 'f' : 't',
label);
print_slot (final_sequence);
}
else
{
fprintf (asm_out_file, "\tb%c\tLF%d\n", logic ? 'f' : 't',
label);
}
recog_operand[0] = oldop;
output_asm_insn ("bra %l0", recog_operand);
1993-06-29 02:10:35 +02:00
fprintf (asm_out_file, "\tor r0,r0\n");
fprintf (asm_out_file, "LF%d:\n", label);
}
1993-04-30 16:29:19 +02:00
return "";
case 16:
/* A branch with an unfilled delay slot. */
case 18:
1993-04-30 16:29:19 +02:00
/* Branches a long way away */
1993-06-29 02:10:35 +02:00
{
rtx oldop = recog_operand[0];
if (need_slot (final_sequence))
{
fprintf (asm_out_file, "\tb%c.s\tLF%d\n", logic ? 'f' : 't', label);
print_slot (final_sequence);
}
else
{
fprintf (asm_out_file, "\tb%c\tLF%d\n", logic ? 'f' : 't', label);
}
output_far_jump (insn, oldop);
1993-06-29 02:10:35 +02:00
fprintf (asm_out_file, "LF%d:\n", label);
return "";
}
1993-04-30 16:29:19 +02:00
}
return "bad";
}
1993-06-29 02:10:35 +02:00
/* The SH cannot load a large constant into a register, constants have to
come from a pc relative load. The reference of a pc relative load
instruction must be less than 1k infront of the instruction. This
means that we often have to dump a constant inside a function, and
generate code to branch around it.
1993-06-29 02:10:35 +02:00
It is important to minimize this, since the branches will slow things
down and make things bigger.
1993-06-29 02:10:35 +02:00
Worst case code looks like:
1993-06-29 02:10:35 +02:00
mov.l L1,rn
bra L2
nop
align
L1: .long value
L2:
..
1993-04-30 16:29:19 +02:00
mov.l L3,rn
bra L4
nop
align
L3: .long value
L4:
..
1993-04-30 16:29:19 +02:00
We fix this by performing a scan before scheduling, which notices which
instructions need to have their operands fetched from the constant table
and builds the table.
1993-04-30 16:29:19 +02:00
The algorithm is:
1993-04-30 16:29:19 +02:00
scan, find an instruction which needs a pcrel move. Look forward, find the
last barrier which is within MAX_COUNT bytes of the requirement.
If there isn't one, make one. Process all the instructions between
the find and the barrier.
1993-04-30 16:29:19 +02:00
In the above example, we can tell that L3 is within 1k of L1, so
the first move can be shrunk from the 3 insn+constant sequence into
just 1 insn, and the constant moved to L3 to make:
mov.l L1,rn
1993-04-30 16:29:19 +02:00
..
mov.l L3,rn
bra L4
1993-04-30 16:29:19 +02:00
nop
align
L3:.long value
L4:.long value
1993-04-30 16:29:19 +02:00
Then the second move becomes the target for the shortening process.
*/
1993-04-30 16:29:19 +02:00
typedef struct
{
rtx value; /* Value in table */
rtx label; /* Label of value */
enum machine_mode mode; /* Mode of value */
}
pool_node;
1993-04-30 16:29:19 +02:00
/* The maximum number of constants that can fit into one pool, since
the pc relative range is 0...1020 bytes and constants are at least 4
bytes long */
#define MAX_POOL_SIZE (1020/4)
static pool_node pool_vector[MAX_POOL_SIZE];
static int pool_size;
/* Add a constant to the pool and return its label. */
1993-04-30 16:29:19 +02:00
static rtx
1993-04-30 16:29:19 +02:00
add_constant (x, mode)
rtx x;
enum machine_mode mode;
{
int i;
rtx lab;
1993-04-30 16:29:19 +02:00
/* First see if we've already got it */
for (i = 0; i < pool_size; i++)
{
if (x->code == pool_vector[i].value->code
&& mode == pool_vector[i].mode)
{
if (x->code == CODE_LABEL)
{
if (XINT (x, 3) != XINT (pool_vector[i].value, 3))
continue;
}
if (rtx_equal_p (x, pool_vector[i].value))
return pool_vector[i].label;
1993-04-30 16:29:19 +02:00
}
}
1993-06-29 02:10:35 +02:00
/* Need a new one */
1993-04-30 16:29:19 +02:00
pool_vector[pool_size].value = x;
lab = gen_label_rtx ();
1993-04-30 16:29:19 +02:00
pool_vector[pool_size].mode = mode;
pool_vector[pool_size].label = lab;
1993-04-30 16:29:19 +02:00
pool_size++;
return lab;
1993-04-30 16:29:19 +02:00
}
/* Dump out interesting debug info */
1993-04-30 16:29:19 +02:00
void
final_prescan_insn (insn, opvec, noperands)
1993-04-30 16:29:19 +02:00
rtx insn;
rtx *opvec;
int noperands;
1993-04-30 16:29:19 +02:00
{
if (target_flags & ISIZE_BIT)
1993-04-30 16:29:19 +02:00
{
extern int *insn_addresses;
fprintf (asm_out_file, "\n! at %04x\n",
insn_addresses[INSN_UID (insn)]);
1993-04-30 16:29:19 +02:00
}
}
/* Output to FILE the start of the assembler file. */
1993-04-30 16:29:19 +02:00
struct option
1993-04-30 16:29:19 +02:00
{
char *string;
int *variable;
int on_value;
};
1993-06-29 02:10:35 +02:00
static int
output_option (file, sep, type, name, indent, pos, max)
FILE *file;
char *sep;
char *type;
char *name;
char *indent;
int pos;
int max;
{
if (strlen (sep) + strlen (type) + strlen (name) + pos > max)
1993-04-30 16:29:19 +02:00
{
fprintf (file, indent);
return fprintf (file, "%s%s", type, name);
1993-06-29 02:10:35 +02:00
}
return pos + fprintf (file, "%s%s%s", sep, type, name);
}
1993-04-30 16:29:19 +02:00
static struct
{
char *name;
int value;
}
1993-04-30 16:29:19 +02:00
m_options[] = TARGET_SWITCHES;
1993-04-30 16:29:19 +02:00
static void
output_options (file, f_options, f_len, W_options, W_len,
pos, max, sep, indent, term)
FILE *file;
struct option *f_options;
struct option *W_options;
int f_len, W_len;
int pos;
int max;
char *sep;
char *indent;
char *term;
{
register int j;
1993-04-30 16:29:19 +02:00
if (optimize)
pos = output_option (file, sep, "-O", "", indent, pos, max);
if (write_symbols != NO_DEBUG)
pos = output_option (file, sep, "-g", "", indent, pos, max);
if (flag_traditional)
pos = output_option (file, sep, "-traditional", "", indent, pos, max);
if (profile_flag)
pos = output_option (file, sep, "-p", "", indent, pos, max);
if (profile_block_flag)
pos = output_option (file, sep, "-a", "", indent, pos, max);
1993-04-30 16:29:19 +02:00
for (j = 0; j < f_len; j++)
if (*f_options[j].variable == f_options[j].on_value)
pos = output_option (file, sep, "-f", f_options[j].string,
indent, pos, max);
1993-04-30 16:29:19 +02:00
for (j = 0; j < W_len; j++)
if (*W_options[j].variable == W_options[j].on_value)
pos = output_option (file, sep, "-W", W_options[j].string,
indent, pos, max);
1993-04-30 16:29:19 +02:00
for (j = 0; j < sizeof m_options / sizeof m_options[0]; j++)
if (m_options[j].name[0] != '\0'
&& m_options[j].value > 0
&& ((m_options[j].value & target_flags)
== m_options[j].value))
pos = output_option (file, sep, "-m", m_options[j].name,
indent, pos, max);
1993-04-30 16:29:19 +02:00
fprintf (file, term);
fprintf (file, "! %d %d\n", max_count_si, max_count_hi);
if (TARGET_LITTLE_ENDIAN)
fprintf (file, "\t.little\n");
}
1993-06-29 02:10:35 +02:00
void
output_file_start (file, f_options, f_len, W_options, W_len)
FILE *file;
struct option *f_options;
struct option *W_options;
int f_len, W_len;
1993-04-30 16:29:19 +02:00
{
register int pos;
1993-06-29 02:10:35 +02:00
output_file_directive (file, main_input_filename);
1993-06-29 02:10:35 +02:00
/* Switch to the data section so that the coffsem symbol and the
gcc2_compiled. symbol aren't in the text section. */
data_section ();
1993-06-29 02:10:35 +02:00
pos = fprintf (file, "\n! Hitachi SH cc1 (%s) arguments:", version_string);
output_options (file, f_options, f_len, W_options, W_len,
pos, 75, " ", "\n! ", "\n\n");
1993-04-30 16:29:19 +02:00
}
1993-04-30 16:29:19 +02:00
/* Actual number of instructions used to make a shift by N */
char ashiftrt_insns[] = { 0,1,2,3,4,5,8,8,8,8,8,8,8,8,8,8,2,3,4,5,8,8,8,8,8,8,8,8,8,8,8,2};
char lshiftrt_insns[] = { 0,1,1,2,2,3,3,4,1,2,2,3,3,4,4,5,1,2,2,3,3,4,4,5,2,3,3,4,4,5,5,6};
char shift_insns[] = { 0,1,1,2,2,3,3,4,1,2,2,3,3,4,4,5,1,2,2,3,3,4,4,5,2,3,3,4,4,5,5,6};
int
shiftinsns (shift, n)
enum rtx_code shift;
int n;
{
switch (shift)
{
case ASHIFTRT:
return ashiftrt_insns[n];
case LSHIFTRT:
return lshiftrt_insns[n];
case ASHIFT:
return shift_insns[n];
default:
abort();
}
}
1993-04-30 16:29:19 +02:00
/* Return the cost of a shift */
1993-04-30 16:29:19 +02:00
int
shiftcosts (RTX)
rtx RTX;
1993-04-30 16:29:19 +02:00
{
/* If shift by a non constant, then this will be expensive. */
if (GET_CODE (XEXP (RTX, 1)) != CONST_INT)
{
return 20;
}
1993-04-30 16:29:19 +02:00
/* otherwise, it will be very cheap if by one of the constants
we can cope with. */
if (CONST_OK_FOR_K (INTVAL (XEXP (RTX, 1))))
return 1;
/* otherwise it will be several insns, but we pretend that it will be more than
just the components, so that combine doesn't glue together a load of shifts into
one shift which has to be emitted as a bunch anyway - breaking scheduling */
return 1;
}
1993-06-29 02:10:35 +02:00
int
andcosts (RTX)
rtx RTX;
{
int i;
if (GET_CODE (XEXP (RTX, 1)) != CONST_INT)
return 2;
i = INTVAL (XEXP (RTX, 1));
/* And can use the extend insns cheaply */
if (i == 0xff || i == 0xffff)
return 2;
/* Any small constant is reasonably cheap - but requires r0 */
if (CONST_OK_FOR_I (i))
return 3;
return 5;
}
int
howshift (i)
int i;
{
int total = 0;
while (i > 0)
{
if (i >= 16)
{
total++;
i -= 16;
}
else if (i >= 8)
{
total++;
i -= 8;
}
else if (i >= 2)
{
total++;
i -= 2;
}
else if (i >= 1)
{
total++;
i--;
}
}
return total;
}
/* Return the cost of a multiply */
int
multcosts (RTX)
rtx RTX;
{
if (TARGET_SH2)
{
/* We have a mul insn, so we can never take more than the mul and the
read of the mac reg, but count more because of the latency and extra
reg usage */
if (TARGET_SMALLCODE)
return 2;
return 3;
}
/* If we're aiming at small code, then just count the number of
insns in a multiply call sequence */
if (TARGET_SMALLCODE)
return 6;
/* Otherwise count all the insns in the routine we'd be calling too */
return 20;
}
1993-06-29 02:10:35 +02:00
/* Code to expand a shift */
1993-06-29 02:10:35 +02:00
void
gen_ashift (type, n, reg)
int type;
int n;
rtx reg;
{
switch (type)
1993-04-30 16:29:19 +02:00
{
case ASHIFTRT:
emit_insn (gen_ashrsi3_k (reg, reg, GEN_INT (n)));
break;
case LSHIFTRT:
emit_insn (gen_lshrsi3_k (reg, reg, GEN_INT (n)));
break;
case ASHIFT:
emit_insn (gen_ashlsi3_k (reg, reg, GEN_INT (n)));
break;
1993-04-30 16:29:19 +02:00
}
}
int
gen_shifty_op (code, operands)
int code;
rtx *operands;
1993-04-30 16:29:19 +02:00
{
rtx wrk = gen_reg_rtx (SImode);
rtx t;
char *func;
if (GET_CODE (operands[2]) == CONST_INT)
1993-04-30 16:29:19 +02:00
{
int value = INTVAL (operands[2]);
top:
switch (code)
{
case ASHIFTRT:
if (value < 0)
{
code = ASHIFT;
value = -value;
goto top;
}
if (value == 31)
{
emit_insn (gen_ashrsi2_31 (operands[0], operands[1]));
return 1;
}
else if (value >= 16 && value <= 19)
{
emit_insn (gen_ashrsi2_16 (wrk, operands[1]));
value -= 16;
while (value --)
gen_ashift (ASHIFTRT,1, wrk);
emit_move_insn (operands[0], wrk);
return 1;
}
/* Expand a short sequence inline, longer call a magic routine */
if (value <= 5)
{
emit_move_insn (wrk, operands[1]);
while (value--)
{
gen_ashift (ASHIFTRT, 1, wrk);
}
emit_move_insn (operands[0], wrk);
return 1;
}
t = gen_reg_rtx (Pmode);
/* Load the value into an arg reg and call a helper */
emit_move_insn (gen_rtx (REG, SImode, 4), operands[1]);
if (!shiftsyms[value])
{
func = xmalloc (18);
sprintf (func, "__ashiftrt_r4_%d", value);
shiftsyms[value] = gen_rtx (SYMBOL_REF, Pmode, func);
}
emit_move_insn (t, shiftsyms[value]);
emit_insn (gen_ashrsi3_n (GEN_INT (value), t));
emit_move_insn (operands[0], gen_rtx (REG, SImode, 4));
return 1;
1993-04-30 16:29:19 +02:00
case ASHIFT:
if (value < 0)
{
code = LSHIFTRT;
value = -value;
goto top;
}
/* Fall through */
case LSHIFTRT:
1993-04-30 16:29:19 +02:00
if (value < 0)
{
code = ASHIFT;
value = -value;
goto top;
}
emit_move_insn (wrk, operands[1]);
while (value)
{
if (value >= 16)
{
gen_ashift (code, 16, wrk);
value -= 16;
}
else if (value >= 8)
{
gen_ashift (code, 8, wrk);
value -= 8;
}
else if (value >= 2)
{
gen_ashift (code, 2, wrk);
value -= 2;
}
else
{
gen_ashift (code, 1, wrk);
value--;
}
}
emit_move_insn (operands[0], wrk);
return 1;
}
}
return 0;
1993-04-30 16:29:19 +02:00
}
/* Dump out any constants accumulated in the final pass -
which will only be labels */
char *
output_jump_label_table ()
{
int i;
if (pool_size)
{
fprintf (asm_out_file, "\t.align 2\n");
for (i = 0; i < pool_size; i++)
{
pool_node *p = pool_vector + i;
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (p->label));
output_asm_insn (".long %O0", &p->value);
}
pool_size = 0;
}
1993-06-29 02:10:35 +02:00
return "";
}
/* Output the literal table */
1993-06-29 02:10:35 +02:00
static void
dump_table (scan)
rtx scan;
1993-06-29 02:10:35 +02:00
{
int i;
int need_align = 1;
1993-06-29 02:10:35 +02:00
/* Do two passes, first time dump out the HI sized constants */
1993-06-29 02:10:35 +02:00
for (i = 0; i < pool_size; i++)
1993-06-29 02:10:35 +02:00
{
pool_node *p = pool_vector + i;
if (p->mode == HImode)
{
if (need_align)
{
scan = emit_insn_after (gen_align_2 (), scan);
need_align = 0;
}
scan = emit_label_after (p->label, scan);
scan = emit_insn_after (gen_consttable_2 (p->value), scan);
}
1993-06-29 02:10:35 +02:00
}
need_align = 1;
1993-06-29 02:10:35 +02:00
for (i = 0; i < pool_size; i++)
1993-06-29 02:10:35 +02:00
{
pool_node *p = pool_vector + i;
1993-06-29 02:10:35 +02:00
switch (p->mode)
1993-06-29 02:10:35 +02:00
{
case HImode:
break;
case SImode:
if (need_align)
1993-06-29 02:10:35 +02:00
{
need_align = 0;
scan = emit_label_after (gen_label_rtx (), scan);
scan = emit_insn_after (gen_align_4 (), scan);
1993-06-29 02:10:35 +02:00
}
scan = emit_label_after (p->label, scan);
scan = emit_insn_after (gen_consttable_4 (p->value), scan);
break;
case DImode:
if (need_align)
{
need_align = 0;
scan = emit_label_after (gen_label_rtx (), scan);
scan = emit_insn_after (gen_align_4 (), scan);
}
scan = emit_label_after (p->label, scan);
scan = emit_insn_after (gen_consttable_8 (p->value), scan);
break;
default:
abort ();
break;
1993-06-29 02:10:35 +02:00
}
}
scan = emit_insn_after (gen_consttable_end (), scan);
scan = emit_barrier_after (scan);
pool_size = 0;
}
1993-06-29 02:10:35 +02:00
/* Non zero if the src operand needs to be fixed up */
static
int
fixit (src, mode)
rtx src;
enum machine_mode mode;
{
if (mode == QImode)
return 0; /* QIs never need to be fixed */
if (GET_CODE (src) == CONST)
return 1;
1993-06-29 02:10:35 +02:00
if (GET_CODE (src) == SYMBOL_REF)
1993-06-29 02:10:35 +02:00
{
return 1;
}
if (GET_CODE (src) == LABEL_REF)
return 1;
if (GET_CODE (src) == CONST_INT)
{
return !CONST_OK_FOR_I (INTVAL (src));
1993-06-29 02:10:35 +02:00
}
return 0;
1993-06-29 02:10:35 +02:00
}
/* Return Non-zero if constant would be an ok source for a
mov.w instead of a mov.l */
int
hi_const (src)
rtx src;
1993-06-29 02:10:35 +02:00
{
if (GET_CODE (src) == CONST
&& GET_CODE (XEXP (src, 0)) == SIGN_EXTEND
&& GET_CODE (XEXP (XEXP (src, 0), 0)) == SYMBOL_REF)
return 1;
return (GET_CODE (src) == CONST_INT
&& INTVAL (src) >= -32768
&& INTVAL (src) <= 32767);
1993-06-29 02:10:35 +02:00
}
/* Find the last barrier less than MAX_COUNT bytes from FROM, or create one.
If an HI move is found, then make sure that MAX_COUNT_HI isn't broken from that one. */
1993-06-29 02:10:35 +02:00
/* ??? It would be good to put constant pool tables between a case jump and
the jump table. This fails for two reasons. First, there is no
barrier after the case jump. This is a bug in the casesi pattern.
Second, inserting the table here may break the mova instruction that
loads the jump table address, by moving the jump table too far away.
We fix that problem by never outputting the constant pool between a mova
and its label. */
static
rtx
find_barrier (from)
rtx from;
1993-06-29 02:10:35 +02:00
{
int count_si = 0;
int count_hi = 0;
int found_hi = 0;
int found_si = 0;
rtx found_barrier = 0;
rtx found_mova = 0;
while (from
&& count_si < max_count_si
&& count_hi < max_count_hi)
{
int inc;
if (GET_CODE (from) == BARRIER)
{
found_barrier = from;
}
/* Count the length of this insn - we assume that all moves will
be 2 bytes long, except the DIs */
1993-06-29 02:10:35 +02:00
if (GET_CODE (from) == INSN
&& GET_CODE (PATTERN (from)) == SET
&& CONSTANT_P (SET_SRC (PATTERN (from)))
&& (GET_CODE (SET_SRC (PATTERN (from))) != CONST_INT
|| ! CONST_OK_FOR_I (INTVAL (SET_SRC (PATTERN (from))))))
{
rtx src = SET_SRC (PATTERN (from));
if (hi_const (src))
found_hi = 1;
else
found_si = 1;
inc = (GET_MODE_SIZE (GET_MODE (src)) > 4) ? 4 : 2;
}
else
{
inc = get_attr_length (from);
}
/* ??? This isn't correct anymore. The mova RTL has changed. */
if (GET_CODE (from) == INSN
&& GET_CODE (PATTERN (from)) == SET
&& GET_CODE (SET_DEST (PATTERN (from))) == REG
&& GET_CODE (SET_SRC (PATTERN (from))) == LABEL_REF)
found_mova = from;
else if (GET_CODE (from) == JUMP_INSN
&& (GET_CODE (PATTERN (from)) == ADDR_VEC
|| GET_CODE (PATTERN (from)) == ADDR_DIFF_VEC))
found_mova = 0;
if (found_si)
count_si += inc;
if (found_hi)
count_hi += inc;
from = NEXT_INSN (from);
}
/* Insert the constant pool table before the mova instruction, to prevent
the mova label reference from going out of range. */
if (found_mova)
from = found_mova;
if (!found_barrier)
1993-06-29 02:10:35 +02:00
{
/* We didn't find a barrier in time to
dump our stuff, so we'll make one */
rtx label = gen_label_rtx ();
/* Walk back to be just before any jump */
from = PREV_INSN (from);
while (GET_CODE (from) == JUMP_INSN
|| GET_CODE (from) == NOTE
|| GET_CODE (from) == CODE_LABEL)
{
from = PREV_INSN (from);
}
from = emit_jump_insn_after (gen_jump (label), from);
JUMP_LABEL (from) = label;
found_barrier = emit_barrier_after (from);
emit_label_after (label, found_barrier);
return found_barrier;
1993-06-29 02:10:35 +02:00
}
return found_barrier;
1993-06-29 02:10:35 +02:00
}
/* Non zero if the insn is a move instruction which needs to be fixed. */
1993-06-29 02:10:35 +02:00
static
int
broken_move (insn)
rtx insn;
1993-06-29 02:10:35 +02:00
{
if (!INSN_DELETED_P (insn)
&& GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) == SET)
{
rtx pat = PATTERN (insn);
rtx src = SET_SRC (pat);
rtx dst = SET_DEST (pat);
enum machine_mode mode = GET_MODE (dst);
if (dst == pc_rtx)
return 0;
return fixit (src, mode);
}
return 0;
}
1993-06-29 02:10:35 +02:00
/* Exported to toplev.c
1993-06-29 02:10:35 +02:00
Scan the function looking for move instructions which have to be changed to
pcrel loads and insert the literal tables. */
1993-06-29 02:10:35 +02:00
void
machine_dependent_reorg (first)
rtx first;
{
rtx insn;
for (insn = first; insn; insn = NEXT_INSN (insn))
{
if (broken_move (insn))
{
/* This is a broken move instruction, scan ahead looking for
a barrier to stick the constant table behind */
rtx scan;
rtx barrier = find_barrier (insn);
1993-06-29 02:10:35 +02:00
/* Now find all the moves between the points and modify them */
for (scan = insn; scan != barrier; scan = NEXT_INSN (scan))
{
if (broken_move (scan))
{
rtx pat = PATTERN (scan);
rtx src = SET_SRC (pat);
rtx dst = SET_DEST (pat);
enum machine_mode mode = GET_MODE (dst);
rtx lab;
rtx newinsn;
rtx newsrc;
/* This is a broken move instruction, add it to the pool */
if (mode == SImode && hi_const (src))
{
/* This is an HI source, clobber the dest to get the mode right too */
int offset = 0;
mode = HImode;
while (GET_CODE (dst) == SUBREG)
{
offset += SUBREG_WORD (dst);
dst = SUBREG_REG (dst);
}
dst = gen_rtx (REG, HImode, REGNO (dst) + offset);
}
lab = add_constant (src, mode);
newsrc = gen_rtx (MEM, mode,
gen_rtx (LABEL_REF, VOIDmode, lab));
/* Build a jump insn wrapper around the move instead
of an ordinary insn, because we want to have room for
the target label rtx in fld[7], which an ordinary
insn doesn't have. */
newinsn = emit_jump_insn_after (gen_rtx (SET, VOIDmode,
dst, newsrc), scan);
JUMP_LABEL (newinsn) = lab;
/* But it's still an ordinary insn */
PUT_CODE (newinsn, INSN);
/* Kill old insn */
delete_insn (scan);
scan = newinsn;
}
}
dump_table (barrier);
}
}
1993-06-29 02:10:35 +02:00
}
/* Called from the md file, set up the operands of a compare instruction */
void
from_compare (operands, code)
rtx *operands;
int code;
1993-06-29 02:10:35 +02:00
{
if (code != EQ && code != NE)
{
/* Force args into regs, since we can't use constants here */
sh_compare_op0 = force_reg (SImode, sh_compare_op0);
if (sh_compare_op1 != const0_rtx
|| code == GTU || code == GEU || code == LTU || code == LEU)
sh_compare_op1 = force_reg (SImode, sh_compare_op1);
}
operands[1] = sh_compare_op0;
operands[2] = sh_compare_op1;
}
1993-06-29 02:10:35 +02:00
/* Non-zero if x is EQ or NE */
1993-06-29 02:10:35 +02:00
int
equality_operator (x, mode)
rtx x;
enum machine_mode mode;
{
enum rtx_code code = GET_CODE (x);
return (code == EQ || code == NE);
}
1993-06-29 02:10:35 +02:00
/* Framefull frame looks like:
arg-5
arg-4
[ if current_function_anonymous_args
arg-3
arg-2
arg-1
arg-0 ]
saved-fp
saved-r10
saved-r11
saved-r12
saved-pr
local-n
..
local-1
local-0 <- fp points here
*/
/* Code to generate prologue and epilogue sequences */
1993-06-29 02:10:35 +02:00
1993-06-29 02:10:35 +02:00
void
sh_expand_prologue ()
{
int live_regs_mask;
int d, i;
extern tree current_function_decl;
1993-06-29 02:10:35 +02:00
live_regs_mask = calc_live_regs (&d);
/* We have pretend args if we had an object sent partially in registers
and partially on the stack - eg a large structure */
output_stack_adjust (-current_function_pretend_args_size);
1993-06-29 02:10:35 +02:00
extra_push = 0;
/* This is set by SETUP_VARARGS to indicate that this is a varargs
routine. Clear it here so that the next function isn't affected. */
1993-06-29 02:10:35 +02:00
if (current_function_anonymous_args)
{
current_function_anonymous_args = 0;
1993-06-29 02:10:35 +02:00
/* Push arg regs as if they'd been provided by caller in stack */
for (i = 0; i < NPARM_REGS; i++)
{
int rn = NPARM_REGS + FIRST_PARM_REG - i - 1;
if (i > NPARM_REGS - current_function_args_info)
break;
push (rn);
extra_push += 4;
}
}
push_regs (live_regs_mask);
output_stack_adjust (-get_frame_size ());
1993-06-29 02:10:35 +02:00
if (frame_pointer_needed)
{
emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
}
/* ??? Hack. Clear out the table set up by gen_shifty_op since this
info does not apply to the next function. */
for (i = 0; i < 32; i++)
shiftsyms[i] = 0;
1993-06-29 02:10:35 +02:00
}
void
sh_expand_epilogue ()
{
int live_regs_mask;
int d, i;
1993-06-29 02:10:35 +02:00
live_regs_mask = calc_live_regs (&d);
1993-06-29 02:10:35 +02:00
if (frame_pointer_needed)
{
emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx));
}
output_stack_adjust (get_frame_size ());
1993-06-29 02:10:35 +02:00
/* Pop all the registers */
1993-06-29 02:10:35 +02:00
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
int j = (FIRST_PSEUDO_REGISTER - 1) - i;
if (live_regs_mask & (1 << j))
{
pop (j);
}
}
output_stack_adjust (extra_push + current_function_pretend_args_size);
1993-06-29 02:10:35 +02:00
}
/* Define the offset between two registers, one to be eliminated, and
the other its replacement, at the start of a routine. */
int
initial_elimination_offset (from, to)
int from;
int to;
{
int regs_saved;
int total_saved_regs_space;
int total_auto_space = get_frame_size ();
calc_live_regs (&regs_saved);
total_saved_regs_space = (regs_saved) * 4;
1993-06-29 02:10:35 +02:00
if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
{
return total_saved_regs_space + total_auto_space;
}
if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
{
return total_saved_regs_space + total_auto_space;
}
if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
{
/* Initial gap between fp and sp is 0 */
return 0;
}
abort ();
}
/* Handle machine specific pragmas to be semi-compatible with Hitachi
compiler */
1993-06-29 02:10:35 +02:00
int
handle_pragma (file)
FILE *file;
1993-06-29 02:10:35 +02:00
{
int c;
char pbuf[200];
int psize = 0;
1993-06-29 02:10:35 +02:00
c = getc (file);
while (c == ' ' || c == '\t')
c = getc (file);
if (c == '\n' || c == EOF)
return c;
while (psize < sizeof (pbuf) - 1 && c != '\n')
{
pbuf[psize++] = c;
if (psize == 9 && strncmp (pbuf, "interrupt", 9) == 0)
{
pragma_interrupt = 1;
return ' ';
}
if (psize == 5 && strncmp (pbuf, "trapa", 5) == 0)
{
pragma_interrupt = pragma_trapa = 1;
return ' ';
}
c = getc (file);
}
return c;
}
/* insn expand helpers */
/* Emit insns to perform a call. */
void
expand_acall (isa_retval, operands)
int isa_retval;
rtx *operands;
{
rtx call;
rtx ret = operands[0];
rtx call_target = operands[isa_retval + 0];
rtx numargs = operands[isa_retval + 1];
if (GET_CODE (call_target) == MEM)
{
call_target = force_reg (Pmode, XEXP (call_target, 0));
}
call = gen_rtx (CALL, VOIDmode, gen_rtx (MEM, SImode, call_target), numargs);
if (isa_retval)
{
call = gen_rtx (SET, VOIDmode, ret, call);
}
emit_call_insn (gen_rtx (PARALLEL, VOIDmode,
gen_rtvec (2,
call,
gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, SImode, 17)))));
}
/* Predicates used by the templates */
/* Returns 1 if OP can be source of a simple move operation.
Same as general_operand, but a LABEL_REF is valid, PRE_DEC is
invalid as are subregs of system registers. */
int
general_movsrc_operand (op, mode)
rtx op;
enum machine_mode mode;
{
/* Any MEM(label_ref) is ok, that's a pcrel load */
if (GET_CODE (op) == MEM
&& GET_CODE (XEXP (op, 0)) == LABEL_REF)
1993-06-29 02:10:35 +02:00
return 1;
if (GET_CODE (op) == MEM)
{
rtx inside = XEXP (op, 0);
if (GET_CODE (inside) == CONST)
inside = XEXP (inside, 0);
if (GET_CODE (inside) == LABEL_REF)
return 1;
if (GET_CODE (inside) == PLUS
&& GET_CODE (XEXP (inside,0)) == LABEL_REF
&& GET_CODE (XEXP (inside,1)) == CONST_INT)
return 1;
/* No post inc allowed */
if (GET_CODE (inside) == POST_DEC
|| GET_CODE (inside) == PRE_INC
|| GET_CODE (inside) == PRE_DEC)
return 0;
/* Can't do that with large modes */
if (GET_CODE (inside) == POST_INC
&& GET_MODE_SIZE (mode) > 4)
return 0;
}
if ((mode == QImode || mode == HImode)
&& (GET_CODE (op) == SUBREG
&& GET_CODE (XEXP (op, 0)) == REG
&& system_reg_operand (XEXP (op, 0), mode)))
return 0;
return general_operand (op, mode);
1993-06-29 02:10:35 +02:00
}
/* Returns 1 if OP can be a destination of a move.
Same as general_operand, but no preinc allowed. */
1993-06-29 02:10:35 +02:00
int
general_movdst_operand (op, mode)
rtx op;
enum machine_mode mode;
1993-06-29 02:10:35 +02:00
{
/* No pre dec allowed */
if (GET_CODE (op) == MEM
&& (GET_CODE (XEXP (op, 0)) == PRE_INC
|| GET_CODE (XEXP (op, 0)) == POST_INC
|| GET_CODE (XEXP (op, 0)) == POST_DEC))
return 0;
if (GET_CODE (op) == MEM
&& GET_CODE (XEXP (op, 0)) == PRE_DEC
&& GET_MODE_SIZE (mode) > 4)
return 0;
return general_operand (op, mode);
}
/* Returns 1 if OP is an immediate ok for a byte index. */
int
byte_index_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (GET_CODE (op) == CONST_INT
&& INTVAL (op) >= 0
&& INTVAL (op) <= 15);
}
/* Returns 1 if OP is a pop operand. */
int
pop_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) != MEM)
return 0;
if (GET_MODE (op) != mode)
return 0;
op = XEXP (op, 0);
if (GET_CODE (op) != POST_INC)
return 0;
return XEXP (op, 0) == stack_pointer_rtx;
}
/* Returns 1 if OP is a normal arithmetic register. */
int
arith_reg_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (register_operand (op, mode))
{
if (GET_CODE (op) == REG)
return (REGNO (op) != T_REG
&& REGNO (op) != PR_REG
&& REGNO (op) != MACH_REG
&& REGNO (op) != MACL_REG);
return 1;
}
return 0;
}
/* Returns 1 if OP is MACL, MACH or PR. */
int
system_reg_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == REG)
{
switch (REGNO (op))
{
case PR_REG:
case MACL_REG:
case MACH_REG:
return 1;
}
}
return 0;
}
/* Returns 1 if OP is a valid source operand for an arithmetic insn. */
int
arith_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (arith_reg_operand (op, mode))
return 1;
if (GET_CODE (op) == CONST_INT)
{
if (CONST_OK_FOR_I (INTVAL (op)))
return 1;
}
return 0;
}
/* Returns 1 if OP is a valid source operand for a compare insn. */
int
arith_reg_or_0_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (arith_reg_operand (op, mode))
return 1;
if (GET_CODE (op) == CONST_INT)
{
if (CONST_OK_FOR_N (INTVAL (op)))
return 1;
}
return 0;
}
/* Returns 1 if OP is a valid count operand for a shift operation. */
int
shiftby_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (immediate_operand (op, mode))
return 1;
return 0;
}
/* Returns 1 if OP is a valid source operand for a logical operation. */
int
logical_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (arith_reg_operand (op, mode))
return 1;
if (GET_CODE (op) == CONST_INT)
{
if (CONST_OK_FOR_L (INTVAL (op)))
return 1;
}
return 0;
1993-06-29 02:10:35 +02:00
}
/* Determine where to put an argument to a function.
Value is zero to push the argument on the stack,
or a hard register in which to store the argument.
MODE is the argument's machine mode.
TYPE is the data type of the argument (as a tree).
This is null for libcalls where that information may
not be available.
CUM is a variable of type CUMULATIVE_ARGS which gives info about
the preceding args and about the function being called.
NAMED is nonzero if this argument is a named parameter
(otherwise it is an extra parameter matching an ellipsis). */
rtx
sh_function_arg (cum, mode, type, named)
CUMULATIVE_ARGS cum;
enum machine_mode mode;
tree type;
int named;
{
if (named)
{
int rr = (ROUND_REG ((cum), (mode)));
if (rr < NPARM_REGS)
{
return (((type) == 0 || !TREE_ADDRESSABLE ((tree) (type)))
? gen_rtx (REG, (mode),
(FIRST_PARM_REG + rr))
: 0);
}
}
return 0;
}
/* For an arg passed partly in registers and partly in memory,
this is the number of registers used.
For args passed entirely in registers or entirely in memory, zero.
Any arg that starts in the first 4 regs but won't entirely fit in them
needs partial registers on the SH. */
int
sh_function_arg_partial_nregs (CUM, MODE, TYPE, NAMED)
CUMULATIVE_ARGS CUM;
enum machine_mode MODE;
tree TYPE;
int NAMED;
{
if ((CUM) < NPARM_REGS)
{
if (((TYPE) == 0 || !TREE_ADDRESSABLE ((tree) (TYPE)))
&& ((CUM) + ((MODE) == BLKmode
? ROUND_ADVANCE (int_size_in_bytes (TYPE))
: ROUND_ADVANCE (GET_MODE_SIZE (MODE))) - NPARM_REGS > 0))
{
return NPARM_REGS - CUM;
}
}
return 0;
}