Commit 48837e29 by Doug Evans

h8300.c (cpu_type, [...]): New variables.

	* h8300/h8300.c (cpu_type, names_extended, names_upper_extended,
	h8_reg_names, h8_push_ops, h8_pop_ops, h8_mov_ops, h8_push_op,
	h8_pop_op, h8_mov_op, current_function_anonymous_args, extra_pop,
	hand_list): New variables.
	(h8300_init_once, asm_file_start, asm_file_end, ok_for_bclr, o_operand,
	p_operand, call_insn_operand, jump_address_operand, bit_operand,
	eq_operator, const_costs, notice_update_cc, bit_operator,
	nshift_operator, expand_a_shift, get_shift_alg, emit_a_shift,
	fix_bit_operand): New functions.
	(shift_alg, shift_type, shift_mode): New enums.
	(shift_insn): New struct.
	(shift_n_bits, can_shift): Deleted.
	(shift_one, rotate_one): New variables.
	(WORD_REG_USED): New macro (was function word_reg_used).
	(dosize, function_prologue, function_epilogue, print_operand_address):
	Add h8/300h support.
	(small_power_of_two): Renamed from potl8.
	(potg8): Deleted.
	(general_operand_src): Fix POST_INC case.
	(general_operand_dst): Fix PRE_DEC case.
	(function_arg): 3 regs of args are passed if -mquickcall.
	4 regs of args are passed to handwritten assembler routines.
	(print_operand): New cases 'A', 'P', 'S', 'U', 'W', 'b', 'c', 'd', 'g'.
	Delete case 'O'.  Sort cases.  Add h8/300h support.
	(do_movsi): Renamed from domovsi.
	Handle reload_in_progress and reload_completed.
	(initial_offset): Renamed from io.  Add h8/300h support.

From-SVN: r7729
parent 7bc8cb2b
/* Subroutines for insn-output.c for Hitachi H8/300. /* Subroutines for insn-output.c for Hitachi H8/300.
Copyright (C) 1992,1993 Free Software Foundation, Inc. Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
Contributed by Steve Chamberlain (sac@cygnus.com),
Contributed by Steve Chamberlain (sac@cygnus.com) and Jim Wilson (wilson@cygnus.com), and Doug Evans (dje@cygnus.com).
Jim Wilson (wilson@cygnus.com).
This file is part of GNU CC. This file is part of GNU CC.
...@@ -40,42 +39,85 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ ...@@ -40,42 +39,85 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
void print_operand_address (); void print_operand_address ();
char *index (); char *index ();
/* CPU_TYPE, says what cpu we're compiling for. */
int cpu_type;
/* True if a #pragma interrupt has been seen for the current function. */ /* True if a #pragma interrupt has been seen for the current function. */
int pragma_interrupt; int pragma_interrupt;
/* True if a #pragma saveall has been seen for the current function. */ /* True if a #pragma saveall has been seen for the current function. */
int pragma_saveall; int pragma_saveall;
char *names_big[] static char *names_big[] =
= {"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7"}; {"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7"};
static char *names_extended[] =
{"er0", "er1", "er2", "er3", "er4", "er5", "er6", "er7"};
static char *names_upper_extended[] =
{"e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7"};
/* Points to one of the above. */
/* ??? The above could be put in an array indexed by CPU_TYPE. */
char **h8_reg_names;
/* Various operations needed by the following, indexed by CPU_TYPE. */
/* ??? The h8/300 assembler doesn't understand pop.w (yet). */
static char *h8_push_ops[2] =
{"push", "push.l"};
static char *h8_pop_ops[2] =
{"pop", "pop.l"};
static char *h8_mov_ops[2] =
{"mov.w", "mov.l"};
char *h8_push_op, *h8_pop_op, *h8_mov_op;
/* Initialize various cpu specific globals at start up. */
void
h8300_init_once ()
{
if (TARGET_H8300)
{
cpu_type = (int) CPU_H8300;
h8_reg_names = names_big;
}
else
{
cpu_type = (int) CPU_H8300H;
h8_reg_names = names_extended;
}
h8_push_op = h8_push_ops[cpu_type];
h8_pop_op = h8_pop_ops[cpu_type];
h8_mov_op = h8_mov_ops[cpu_type];
}
char * char *
byte_reg (x, b) byte_reg (x, b)
rtx x; rtx x;
int b; int b;
{ {
static char *names_small[] static char *names_small[] =
= {"r0l", "r0h", "r1l", "r1h", "r2l", "r2h", "r3l", "r3h", {"r0l", "r0h", "r1l", "r1h", "r2l", "r2h", "r3l", "r3h",
"r4l", "r4h", "r5l", "r5h", "r6l", "r6h", "r7lBAD", "r7hBAD"}; "r4l", "r4h", "r5l", "r5h", "r6l", "r6h", "r7lBAD", "r7hBAD"};
return names_small[REGNO (x) * 2 + b]; return names_small[REGNO (x) * 2 + b];
} }
/* REGNO must be saved/restored across calls if this macro is true. */ /* REGNO must be saved/restored across calls if this macro is true. */
static int
word_reg_used (regno) #define WORD_REG_USED(regno) \
int regno; (regno < 7 && \
{ (pragma_interrupt \
if (regno < 7 || pragma_saveall \
&& (pragma_interrupt || pragma_saveall || (regno == FRAME_POINTER_REGNUM && regs_ever_live[regno]) \
|| (regno == FRAME_POINTER_REGNUM && regs_ever_live[regno]) || (regs_ever_live[regno] & !call_used_regs[regno])))
|| (regs_ever_live[regno] & ! call_used_regs[regno])))
return 1;
return 0;
}
/* Output assembly language to FILE for the operation OP with operand size /* Output assembly language to FILE for the operation OP with operand size
SIZE. */ SIZE to adjust the stack pointer. */
/* ??? FPED is currently unused. */
static void static void
dosize (file, op, size, fped) dosize (file, op, size, fped)
FILE *file; FILE *file;
...@@ -86,6 +128,7 @@ dosize (file, op, size, fped) ...@@ -86,6 +128,7 @@ dosize (file, op, size, fped)
switch (size) switch (size)
{ {
case 4: case 4:
/* ??? TARGET_H8300H can do this in one insn. */
case 3: case 3:
fprintf (file, "\t%ss\t#%d,sp\n", op, 2); fprintf (file, "\t%ss\t#%d,sp\n", op, 2);
size -= 2; size -= 2;
...@@ -98,66 +141,112 @@ dosize (file, op, size, fped) ...@@ -98,66 +141,112 @@ dosize (file, op, size, fped)
case 0: case 0:
break; break;
default: default:
fprintf (file, "\tmov.w\t#%d,r5\n\t%s.w\tr5,sp\n", size, op); if (TARGET_H8300)
fprintf (file, "\tmov.w\t#%d,r3\n\t%s.w\tr3,sp\n", size, op);
else
fprintf (file, "\t%s\t#%d,sp\n", op, size);
size = 0; size = 0;
break; break;
} }
} }
/* Output assembly language code for the function prologue. */ /* Output assembly language code for the function prologue. */
static int push_order[FIRST_PSEUDO_REGISTER] static int push_order[FIRST_PSEUDO_REGISTER] =
= {6, 5, 4, 3, 2, 1, 0, -1, -1}; {6, 5, 4, 3, 2, 1, 0, -1, -1};
static int pop_order[FIRST_PSEUDO_REGISTER] static int pop_order[FIRST_PSEUDO_REGISTER] =
= {0, 1, 2, 3, 4, 5, 6, -1, -1}; {0, 1, 2, 3, 4, 5, 6, -1, -1};
/* This is what the stack looks like after the prolog of /* This is what the stack looks like after the prolog of
a function with a frame has been set up: a function with a frame has been set up:
<pushed args> <args>
return pc PC
fp-> old fp FP <- fp
<locals> <locals>
<saved register-0> <saved registers> <- sp
<saved register-1>
sp-> <saved register-n>
This is what the stack looks like after the prolog of This is what the stack looks like after the prolog of
a function which doesn't have a frame: a function which doesn't have a frame:
<pushed args> <args>
return pc PC
<locals> <locals>
<saved register-0> <saved registers> <- sp
sp-> <saved register-n>
*/ */
int current_function_anonymous_args;
/* Extra arguments to pop, in words (IE: 2 bytes for 300, 4 for 300h */
static int extra_pop;
void void
function_prologue (file, size) function_prologue (file, size)
FILE *file; FILE *file;
int size; int size;
{ {
register int mask = 0; register int mask = 0;
int fsize = (size + 1) & -2; int fsize = (size + STACK_BOUNDARY / 8 - 1) & -STACK_BOUNDARY / 8;
int idx; int idx;
extra_pop = 0;
if (frame_pointer_needed) if (current_function_anonymous_args && TARGET_QUICKCALL)
{ {
/* Push the fp. */ /* Push regs as if done by caller, and move around return address. */
fprintf (file, "\tpush\t%s\n", names_big[FRAME_POINTER_REGNUM]);
fprintf (file, "\tmov.w\tr7,r6\n"); switch (current_function_args_info.nbytes / UNITS_PER_WORD)
{
case 0:
/* get ret addr */
fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[3]);
fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[2]);
fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[1]);
fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[0]);
/* push it again */
fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[3]);
extra_pop = 3;
break;
case 1:
/* get ret addr */
fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[3]);
fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[2]);
fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[1]);
/* push it again */
fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[3]);
extra_pop = 2;
break;
case 2:
/* get ret addr */
fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[3]);
fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[2]);
/* push it again */
fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[3]);
extra_pop = 1;
break;
default:
fprintf (file, "; varargs\n");
break;
}
}
/* Leave room for the locals. */ if (frame_pointer_needed)
{
/* Push fp */
fprintf (file, "\t%s\t%s\n", h8_push_op,
h8_reg_names[FRAME_POINTER_REGNUM]);
fprintf (file, "\t%s\t%s,%s\n", h8_mov_op,
h8_reg_names[STACK_POINTER_REGNUM],
h8_reg_names[FRAME_POINTER_REGNUM]);
/* leave room for locals */
dosize (file, "sub", fsize, 1); dosize (file, "sub", fsize, 1);
/* Push the rest of the registers. */ /* Push the rest of the registers */
for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++) for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++)
{ {
int regno = push_order[idx]; int regno = push_order[idx];
if (regno >= 0 && word_reg_used (regno) if (regno >= 0 && WORD_REG_USED (regno) && regno != FRAME_POINTER_REGNUM)
&& regno != FRAME_POINTER_REGNUM) fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[regno]);
fprintf (file, "\tpush\t%s\n", names_big[regno]);
} }
} }
else else
...@@ -167,8 +256,8 @@ function_prologue (file, size) ...@@ -167,8 +256,8 @@ function_prologue (file, size)
{ {
int regno = push_order[idx]; int regno = push_order[idx];
if (regno >= 0 && word_reg_used (regno)) if (regno >= 0 && WORD_REG_USED (regno))
fprintf (file, "\tpush\t%s\n", names_big[regno]); fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[regno]);
} }
} }
} }
...@@ -182,7 +271,7 @@ function_epilogue (file, size) ...@@ -182,7 +271,7 @@ function_epilogue (file, size)
{ {
register int regno; register int regno;
register int mask = 0; register int mask = 0;
int fsize = (size + 1) & -2; int fsize = (size + STACK_BOUNDARY / 8 - 1) & -STACK_BOUNDARY / 8;
int nregs; int nregs;
int offset; int offset;
int idx; int idx;
...@@ -198,43 +287,87 @@ function_epilogue (file, size) ...@@ -198,43 +287,87 @@ function_epilogue (file, size)
if (frame_pointer_needed) if (frame_pointer_needed)
{ {
/* Pop saved registers. */ /* Pop saved registers */
for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++) for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++)
{ {
regno = pop_order[idx]; regno = pop_order[idx];
if (regno >= 0 && regno != FRAME_POINTER_REGNUM if (regno >= 0 && regno != FRAME_POINTER_REGNUM && WORD_REG_USED (regno))
&& word_reg_used (regno)) fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[regno]);
fprintf (file, "\tpop\t%s\n", names_big[regno]);
} }
/* Deallocate locals. */ /* deallocate locals */
dosize (file, "add", fsize, 1); dosize (file, "add", fsize, 1);
/* Pop frame pointer. */ /* pop frame pointer */
fprintf (file, "\tpop\t%s\n", names_big[FRAME_POINTER_REGNUM]); fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[FRAME_POINTER_REGNUM]);
} }
else else
{ {
/* Deallocate locals and pop saved registers. */ /* pop saved registers */
for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++) for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++)
{ {
regno = pop_order[idx]; regno = pop_order[idx];
if (regno >= 0 && word_reg_used (regno)) if (regno >= 0 && WORD_REG_USED (regno))
fprintf (file, "\tpop\t%s\n", names_big[regno]); fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[regno]);
} }
/* deallocate locals */
dosize (file, "add", fsize, 0); dosize (file, "add", fsize, 0);
} }
if (extra_pop)
{
fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[3]);
while (extra_pop)
{
fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[2]);
extra_pop--;
}
fprintf (file, "\tjmp @%s\n", h8_reg_names[3]);
}
else
{
if (pragma_interrupt) if (pragma_interrupt)
fprintf (file, "\trte\n"); fprintf (file, "\trte\n");
else else
fprintf (file, "\trts\n"); fprintf (file, "\trts\n");
}
pragma_interrupt = 0; pragma_interrupt = 0;
pragma_saveall = 0; pragma_saveall = 0;
current_function_anonymous_args = 0;
}
/* Output assembly code for the start of the file. */
asm_file_start (file)
FILE *file;
{
fprintf (file, ";\tGCC For the Hitachi H8/300\n");
fprintf (file, ";\tBy Hitachi America Ltd and Cygnus Support\n");
fprintf (file, ";\trelease F-1\n");
if (optimize)
fprintf (file, "; -O%d\n", optimize);
if (TARGET_H8300H)
fprintf (file, "\n\t.h8300h\n");
else
fprintf (file, "\n\n");
output_file_directive (file, main_input_filename);
}
/* Output assembly language code for the end of file. */
void
asm_file_end (file)
FILE *file;
{
fprintf (file, "\t.end\n");
} }
/* Return true if VALUE is a valid constant for constraint 'P'. */ /* Return true if VALUE is a valid constant for constraint 'P'.
IE: VALUE is a power of two <= 2**15. */
int int
potl8 (value) small_power_of_two (value)
int value;
{ {
switch (value) switch (value)
{ {
...@@ -246,18 +379,6 @@ potl8 (value) ...@@ -246,18 +379,6 @@ potl8 (value)
case 32: case 32:
case 64: case 64:
case 128: case 128:
return 1;
}
return 0;
}
/* Return true if VALUE is a valid constant for constraint 'O'. */
int
potg8 (value)
int value;
{
switch (value)
{
case 256: case 256:
case 512: case 512:
case 1024: case 1024:
...@@ -271,34 +392,152 @@ potg8 (value) ...@@ -271,34 +392,152 @@ potg8 (value)
return 0; return 0;
} }
/* Return true if VALUE is a valid constant for constraint 'O', which
means that the constant would be ok to use as a bit for a bclr
instruction. */
int
ok_for_bclr (value)
int value;
{
return small_power_of_two ((~value) & 0xff);
}
/* Return true is OP is a valid source operand for an integer move /* Return true is OP is a valid source operand for an integer move
instruction. */ instruction. */
int int
general_operand_src (op, mode) general_operand_src (op, mode)
rtx op; rtx op;
enum machine_mode mode; enum machine_mode mode;
{ {
/* We can't have a pre-dec as a source. */ if (GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) == POST_INC)
if (GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) == PRE_DEC) return 1;
return 0;
return general_operand (op, mode); return general_operand (op, mode);
} }
/* Return true if OP is a valid destination operand for an integer move /* Return true if OP is a valid destination operand for an integer move
instruction. */ instruction. */
int int
general_operand_dst (op, mode) general_operand_dst (op, mode)
rtx op; rtx op;
enum machine_mode mode; enum machine_mode mode;
{ {
/* We can't have a post-inc as a dest. */ if (GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) == PRE_DEC)
if (GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) == POST_INC) return 1;
return 0;
return general_operand (op, mode); return general_operand (op, mode);
} }
/* Return true if OP is a const valid for a bit clear instruction. */
int
o_operand (operand, mode)
rtx operand;
enum machine_mode mode;
{
return (GET_CODE (operand) == CONST_INT
&& CONST_OK_FOR_O (INTVAL (operand)));
}
/* Return true if OP is a const valid for a bit set or bit xor instruction. */
int
p_operand (operand, mode)
rtx operand;
enum machine_mode mode;
{
return (GET_CODE (operand) == CONST_INT
&& CONST_OK_FOR_P (INTVAL (operand)));
}
/* Return true if OP is a valid call operand. */
int
call_insn_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == MEM)
{
rtx inside = XEXP (op, 0);
if (register_operand (inside, Pmode))
return 1;
if (CONSTANT_ADDRESS_P (inside))
return 1;
}
return 0;
}
/* Return true if OP is a valid jump operand. */
int
jump_address_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == REG)
return mode == Pmode;
if (GET_CODE (op) == MEM)
{
rtx inside = XEXP (op, 0);
if (register_operand (inside, Pmode))
return 1;
if (CONSTANT_ADDRESS_P (inside))
return 1;
}
return 0;
}
/* Recognize valid operands for bitfield instructions. */
extern int rtx_equal_function_value_matters;
int
bit_operand (op, mode)
rtx op;
enum machine_mode mode;
{
/* We can except any general operand, expept that MEM operands must
be limited to those that use addresses valid for the 'U' constraint. */
if (!general_operand (op, mode))
return 0;
/* Accept any mem during RTL generation. Otherwise, the code that does
insv and extzv will think that we can not handle memory. However,
to avoid reload problems, we only accept 'U' MEM operands after RTL
generation. This means that any named pattern which uses this predicate
must force its operands to match 'U' before emitting RTL. */
if (GET_CODE (op) == REG)
return 1;
if (GET_CODE (op) == SUBREG)
return 1;
if (!rtx_equal_function_value_matters)
{
/* We're building rtl */
return GET_CODE (op) == MEM;
}
else
{
return (GET_CODE (op) == MEM
&& EXTRA_CONSTRAINT (op, 'U'));
}
}
/* Recognize valid operators for bit test. */
int
eq_operator (x, mode)
rtx x;
enum machine_mode mode;
{
return (GET_CODE (x) == EQ || GET_CODE (x) == NE);
}
/* Handle machine specific pragmas for compatibility with existing /* Handle machine specific pragmas for compatibility with existing
compilers for the H8/300 compilers for the H8/300.
pragma saveall generates prolog/epilog code which saves and pragma saveall generates prolog/epilog code which saves and
restores all the registers on function entry. restores all the registers on function entry.
...@@ -306,13 +545,14 @@ general_operand_dst (op, mode) ...@@ -306,13 +545,14 @@ general_operand_dst (op, mode)
pragma interrupt saves and restores all registers, and exits with pragma interrupt saves and restores all registers, and exits with
an rte instruction rather than an rts. A pointer to a function an rte instruction rather than an rts. A pointer to a function
with this attribute may be safely used in an interrupt vector. */ with this attribute may be safely used in an interrupt vector. */
int int
handle_pragma (file) handle_pragma (file)
FILE *file; FILE *file;
{ {
int c; int c;
char pbuf[20]; char pbuf[20];
int psize; int psize = 0;
c = getc (file); c = getc (file);
while (c == ' ' || c == '\t') while (c == ' ' || c == '\t')
...@@ -321,12 +561,13 @@ handle_pragma (file) ...@@ -321,12 +561,13 @@ handle_pragma (file)
if (c == '\n' || c == EOF) if (c == '\n' || c == EOF)
return c; return c;
for (psize = 0; psize < sizeof (pbuf) - 1 && isalpha (c); psize++) /* The only pragmas we understand are interrupt and saveall. */
while (psize < sizeof (pbuf) - 1
&& isalpha (c))
{ {
pbuf[psize] = c; pbuf[psize++] = c;
c = getc (file); c = getc (file);
} }
pbuf[psize] = 0; pbuf[psize] = 0;
if (strcmp (pbuf, "interrupt") == 0) if (strcmp (pbuf, "interrupt") == 0)
...@@ -335,6 +576,22 @@ handle_pragma (file) ...@@ -335,6 +576,22 @@ handle_pragma (file)
if (strcmp (pbuf, "saveall") == 0) if (strcmp (pbuf, "saveall") == 0)
pragma_saveall = 1; pragma_saveall = 1;
/* ??? This is deprecated. Use section attributes. */
if (strcmp (pbuf, "section") == 0)
{
while (c && !isalpha (c))
c = getc (file);
psize = 0;
while (psize < sizeof (pbuf) - 1
&& isalpha (c) || isdigit (c) || c == '_')
{
pbuf[psize++] = c;
c = getc (file);
}
pbuf[psize] = 0;
named_section (pbuf);
}
ungetc (c, file);
return c; return c;
} }
...@@ -342,6 +599,29 @@ handle_pragma (file) ...@@ -342,6 +599,29 @@ handle_pragma (file)
the rtx to represent where it is passed. CUM represents the state after the rtx to represent where it is passed. CUM represents the state after
the last argument. NAMED is not used. */ the last argument. NAMED is not used. */
static char *hand_list[] =
{
"__main",
"__cmpsi2",
"__divhi3",
"__modhi3",
"__udivhi3",
"__umodhi3",
"__divsi3",
"__modsi3",
"__udivsi3",
"__umodsi3",
"__mulhi3",
"__mulsi3",
"__reg_memcpy",
"__reg_memset",
"__ucmpsi2",
0,
};
/* Return an RTX to represent where a value with mode MODE will be returned
from a function. If the result is 0, the argument is pushed. */
rtx rtx
function_arg (cum, mode, type, named) function_arg (cum, mode, type, named)
CUMULATIVE_ARGS *cum; CUMULATIVE_ARGS *cum;
...@@ -350,83 +630,154 @@ function_arg (cum, mode, type, named) ...@@ -350,83 +630,154 @@ function_arg (cum, mode, type, named)
int named; int named;
{ {
rtx result = 0; rtx result = 0;
int libcall = 0; char *fname;
int regpass = 0;
/* Pass 3 regs worth of data in regs when user asked on the command line. */
if (TARGET_QUICKCALL)
regpass = 3;
/* If calling hand written assembler, use 4 regs of args. */
/* Right now reload has a problem with reg passing with small reg if (cum->libcall)
classes. */ {
char **p;
fname = XSTR (cum->libcall, 0);
/* See if this libcall is one of the hand coded ones. */
for (p = hand_list; *p && strcmp (*p, fname) != 0; p++)
;
if (cum->libcall || (named && TARGET_QUICKCALL)) if (*p)
libcall = 1; regpass = 4;
}
if (regpass)
{
int size;
if (TARGET_NOQUICK) if (mode == BLKmode)
libcall = 0; size = int_size_in_bytes (type);
else
size = GET_MODE_SIZE (mode);
if (mode != VOIDmode && libcall && mode != DFmode && mode != SFmode) if (size + cum->nbytes > regpass * UNITS_PER_WORD)
{
result = 0;
}
else
{ {
switch (cum->nbytes) switch (cum->nbytes / UNITS_PER_WORD)
{ {
case 0: case 0:
result = gen_rtx (REG, mode, 0); result = gen_rtx (REG, mode, 0);
break; break;
case 2: case 1:
result = gen_rtx (REG, mode, 1); result = gen_rtx (REG, mode, 1);
break; break;
case 4: case 2:
result = gen_rtx (REG, mode, 4); result = gen_rtx (REG, mode, 2);
break; break;
case 6: case 3:
result = gen_rtx (REG, mode, 5); result = gen_rtx (REG, mode, 3);
break; break;
default: default:
return 0; result = 0;
} }
} }
}
return result; return result;
} }
/* Return the cost of the rtx R with code CODE. */
int
const_costs (r, c)
rtx r;
enum rtx_code c;
{
switch (c)
{
case CONST_INT:
switch (INTVAL (r))
{
case 0:
case 1:
case 2:
case -1:
case -2:
return 0;
default:
return 1;
}
case CONST:
case LABEL_REF:
case SYMBOL_REF:
return 3;
case CONST_DOUBLE:
return 20;
default:
return 4;
}
}
/* Documentation for the machine specific operand escapes: /* Documentation for the machine specific operand escapes:
'A' print rn in h8/300 mode, erN in H8/300H mode
'C' print (operand - 2). 'C' print (operand - 2).
'E' low byte of reg or -ve lsb of constant 'E' like s but negative.
'F' high byte of reg of -ve msb of constant 'F' like t but negative.
'G' constant just the negative
'G' negate constant
'L' fake label, changed after used twice. 'L' fake label, changed after used twice.
'M' turn a 'M' constant into its negative mod 2. 'M' turn a 'M' constant into its negative mod 2.
'P' if operand is incing/decing sp, print .w, otherwise .b.
'S' print operand as a long word
'T' print operand as a word 'T' print operand as a word
'V' print log2 of constant - used for bset instructions 'U' if operand is incing/decing sp, print l, otherwise nothing.
'X' 8 bit register or other operand 'V' find the set bit, and print its number.
'W' find the clear bit, and print its number.
'X' print operand as a byte
'Y' print either l or h depending on whether last 'Z' operand < 8 or >= 8. 'Y' print either l or h depending on whether last 'Z' operand < 8 or >= 8.
'Z' print int & 7 'Z' print int & 7.
'b' print the bit opcode
'e' first word of 32 bit value 'c' print the ibit opcode
'f' second word of 32 bit value 'd' bcc if EQ, bcs if NE
'e' first word of 32 bit value - if reg, then least reg. if mem
then least. if const then most sig word
'f' second word of 32 bit value - if reg, then biggest reg. if mem
then +2. if const then least sig word
'g' bcs if EQ, bcc if NE
'j' print operand as condition code. 'j' print operand as condition code.
'k' print operand as reverse condition code. 'k' print operand as reverse condition code.
's' print as low byte of 16 bit value
's' low byte of 16 bit value 't' print as high byte of 16 bit value
't' high byte of 16 bit value 'w' print as low byte of 32 bit value
'x' print as 2nd byte of 32 bit value
'w' 1st byte of 32 bit value zzzzzzzz yyyyyyyy xxxxxxxx wwwwwwww 'y' print as 3rd byte of 32 bit value
'x' 2nd byte of 32 bit value 'z' print as msb of 32 bit value
'y' 3rd byte of 32 bit value */
'z' 4th byte of 32 bit value
*/
/* Return assembly language string which identifies a comparison type. */ /* Return assembly language string which identifies a comparison type. */
char * static char *
cond_string (code) cond_string (code)
enum rtx_code code; enum rtx_code code;
{ {
switch (code) switch (code)
{ {
case NE: case NE:
if (cc_prev_status.flags & CC_DONE_CBIT)
return "cs";
return "ne"; return "ne";
case EQ: case EQ:
if (cc_prev_status.flags & CC_DONE_CBIT)
return "cc";
return "eq"; return "eq";
case GE: case GE:
return "ge"; return "ge";
...@@ -465,109 +816,77 @@ print_operand (file, x, code) ...@@ -465,109 +816,77 @@ print_operand (file, x, code)
static char *last_p; static char *last_p;
/* This is used for communication between the 'Z' and 'Y' codes. */ /* This is used for communication between the 'Z' and 'Y' codes. */
/* ??? 'V' and 'W' use it too. */
static int bitint; static int bitint;
switch (code) switch (code)
{ {
case 'L': case 'A':
/* 'L' must always be used twice in a single pattern. It generates
the same lable twice, and then will generate a unique label the
next time it is used. */
asm_fprintf (file, "tl%d", (lab++) / 2);
break;
case 'X':
if (GET_CODE (x) == REG) if (GET_CODE (x) == REG)
fprintf (file, "%s", byte_reg (x, 0)); fprintf (file, "%s", h8_reg_names[REGNO (x)]);
else else
goto def; goto def;
break; break;
case 'C':
case 'G': fprintf (file, "#%d", INTVAL (x) - 2);
if (GET_CODE (x) != CONST_INT)
abort ();
fprintf (file, "#%d", 0xff & (-INTVAL (x)));
break; break;
case 'E':
case 'T': switch (GET_CODE (x))
if (GET_CODE (x) == REG) {
fprintf (file, "%s", names_big[REGNO (x)]); case REG:
else fprintf (file, "%sl", names_big[REGNO (x)]);
goto def;
break; break;
case CONST_INT:
case 'w': fprintf (file, "#%d", (-INTVAL (x)) & 0xff);
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%d", INTVAL (x) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 2));
break; break;
default:
case 'x': abort ();
if (GET_CODE (x) == CONST_INT) }
fprintf (file, "#%d", (INTVAL (x) >> 8) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 3));
break;
case 'y':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%d", (INTVAL (x) >> 16) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 0));
break; break;
case 'F':
case 'z': switch (GET_CODE (x))
if (GET_CODE (x) == CONST_INT) {
fprintf (file, "#%d", (INTVAL (x) >> 24) & 0xff); case REG:
else fprintf (file, "%sh", names_big[REGNO (x)]);
fprintf (file, "%s", byte_reg (x, 1));
break; break;
case CONST_INT:
/* FOR 16 bits. */ fprintf (file, "#%d", ((-INTVAL (x)) & 0xff00) >> 8);
case 't':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%d", (INTVAL (x) >> 8) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 1));
break; break;
default:
case 's': abort ();
if (GET_CODE (x) == CONST_INT) }
fprintf (file, "#%d", (INTVAL (x)) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 0));
break; break;
case 'G':
case 'u':
if (GET_CODE (x) != CONST_INT) if (GET_CODE (x) != CONST_INT)
abort (); abort ();
fprintf (file, "%d", INTVAL (x)); fprintf (file, "#%d", 0xff & (-INTVAL (x)));
break; break;
case 'L':
case 'Z': /* 'L' must always be used twice in a single pattern. It generates
bitint = INTVAL (x); the same lable twice, and then will generate a unique label the
fprintf (file, "#%d", bitint & 7); next time it is used. */
asm_fprintf (file, "tl%d", (lab++) / 2);
break; break;
case 'M':
case 'Y': /* For 3/-3 and 4/-4, the other 2 is handled separately. */
fprintf (file, "%c", bitint > 7 ? 'h' : 'l'); switch (INTVAL (x))
{
case 2:
case 4:
case -2:
case -4:
fprintf (file, "#2");
break; break;
case 1:
case 'O': case 3:
bitint = exact_log2 ((~INTVAL (x)) & 0xff); case -1:
if (bitint == -1) case -3:
abort (); fprintf (file, "#1");
fprintf (file, "#%d", bitint & 7);
break; break;
default:
case 'V':
bitint = exact_log2 (INTVAL (x));
if (bitint == -1)
abort (); abort ();
fprintf (file, "#%d", bitint & 7); }
break; break;
case 'P': case 'P':
if (REGNO (XEXP (XEXP (x, 0), 0)) == STACK_POINTER_REGNUM) if (REGNO (XEXP (XEXP (x, 0), 0)) == STACK_POINTER_REGNUM)
{ {
...@@ -580,34 +899,101 @@ print_operand (file, x, code) ...@@ -580,34 +899,101 @@ print_operand (file, x, code)
fprintf (file, ".b"); fprintf (file, ".b");
} }
break; break;
case 'S':
if (GET_CODE (x) == REG)
fprintf (file, "%s", names_extended[REGNO (x)]);
else
goto def;
break;
case 'T':
if (GET_CODE (x) == REG)
fprintf (file, "%s", names_big[REGNO (x)]);
else
goto def;
break;
case 'U': case 'U':
fprintf (file, "%s%s", names_big[REGNO (x)], last_p); fprintf (file, "%s%s", names_big[REGNO (x)], last_p);
break; break;
case 'V':
case 'M': bitint = exact_log2 (INTVAL (x));
/* For -4 and -2, the other 2 is handled separately. */ if (bitint == -1)
switch (INTVAL (x)) abort ();
fprintf (file, "#%d", bitint & 7);
break;
case 'W':
bitint = exact_log2 ((~INTVAL (x)) & 0xff);
if (bitint == -1)
abort ();
fprintf (file, "#%d", bitint & 7);
break;
case 'X':
if (GET_CODE (x) == REG)
fprintf (file, "%s", byte_reg (x, 0));
else
goto def;
break;
case 'Y':
if (bitint == -1)
abort ();
if (GET_CODE (x) == REG)
fprintf (file, "%s%c", names_big[REGNO (x)], bitint > 7 ? 'h' : 'l');
else
print_operand (file, x, 0);
bitint = -1;
break;
case 'Z':
bitint = INTVAL (x);
fprintf (file, "#%d", bitint & 7);
break;
case 'b':
switch (GET_CODE (x))
{ {
case -2: case IOR:
case -4: fprintf (file, "bor");
fprintf (file, "#2");
break; break;
case -1: case XOR:
case -3: fprintf (file, "bxor");
fprintf (file, "#1"); break;
case AND:
fprintf (file, "band");
break;
}
break;
case 'c':
switch (GET_CODE (x))
{
case IOR:
fprintf (file, "bior");
break;
case XOR:
fprintf (file, "bixor");
break;
case AND:
fprintf (file, "biand");
break;
}
break;
case 'd':
switch (GET_CODE (x))
{
case EQ:
fprintf (file, "bcc");
break;
case NE:
fprintf (file, "bcs");
break; break;
default: default:
abort (); abort ();
} }
break; break;
case 'e': case 'e':
switch (GET_CODE (x)) switch (GET_CODE (x))
{ {
case REG: case REG:
if (TARGET_H8300)
fprintf (file, "%s", names_big[REGNO (x)]); fprintf (file, "%s", names_big[REGNO (x)]);
else
fprintf (file, "%s", names_upper_extended[REGNO (x)]);
break; break;
case MEM: case MEM:
x = adj_offsettable_operand (x, 0); x = adj_offsettable_operand (x, 0);
...@@ -621,78 +1007,111 @@ print_operand (file, x, code) ...@@ -621,78 +1007,111 @@ print_operand (file, x, code)
break; break;
} }
break; break;
case 'f': case 'f':
switch (GET_CODE (x)) switch (GET_CODE (x))
{ {
case REG: case REG:
if (TARGET_H8300)
fprintf (file, "%s", names_big[REGNO (x) + 1]); fprintf (file, "%s", names_big[REGNO (x) + 1]);
else
fprintf (file, "%s", names_big[REGNO (x)]);
break; break;
case MEM: case MEM:
x = adj_offsettable_operand (x, 2); x = adj_offsettable_operand (x, 2);
print_operand (file, x, 0); print_operand (file, x, 0);
break; break;
case CONST_INT: case CONST_INT:
fprintf (file, "#%d", INTVAL (x) & 0xffff); fprintf (file, "#%d", INTVAL (x) & 0xffff);
break; break;
default:
abort ();
}
break;
case 'C':
fprintf (file, "#%d", INTVAL (x) - 2);
break;
case 'E':
switch (GET_CODE (x))
{
case REG:
fprintf (file, "%sl", names_big[REGNO (x)]);
break;
case CONST_INT:
fprintf (file, "#%d", (-INTVAL (x)) & 0xff);
break;
default: default:
abort (); abort ();
} }
break; break;
case 'g':
case 'F':
switch (GET_CODE (x)) switch (GET_CODE (x))
{ {
case REG: case NE:
fprintf (file, "%sh", names_big[REGNO (x)]); fprintf (file, "bcc");
break; break;
case EQ:
case CONST_INT: fprintf (file, "bcs");
fprintf (file, "#%d", ((-INTVAL (x)) & 0xff00) >> 8);
break; break;
default: default:
abort (); abort ();
} }
break; break;
case 'j': case 'j':
asm_fprintf (file, cond_string (GET_CODE (x))); asm_fprintf (file, cond_string (GET_CODE (x)));
break; break;
case 'k': case 'k':
asm_fprintf (file, cond_string (reverse_condition (GET_CODE (x)))); asm_fprintf (file, cond_string (reverse_condition (GET_CODE (x))));
break; break;
def: ; case 's':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%d", (INTVAL (x)) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 0));
break;
case 't':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%d", (INTVAL (x) >> 8) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 1));
break;
case 'u':
if (GET_CODE (x) != CONST_INT)
abort ();
fprintf (file, "%d", INTVAL (x));
break;
case 'w':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%d", INTVAL (x) & 0xff);
else
fprintf (file, "%s", byte_reg (x, TARGET_H8300 ? 2 : 0));
break;
case 'x':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%d", (INTVAL (x) >> 8) & 0xff);
else
fprintf (file, "%s", byte_reg (x, TARGET_H8300 ? 3 : 1));
break;
case 'y':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%d", (INTVAL (x) >> 16) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 0));
break;
case 'z':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%d", (INTVAL (x) >> 24) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 1));
break;
default: default:
def:
switch (GET_CODE (x)) switch (GET_CODE (x))
{ {
case REG: case REG:
switch (GET_MODE (x))
{
case QImode:
#if 0 /* Is it asm ("mov.b %0,r2l", ...) */
fprintf (file, "%s", byte_reg (x, 0));
#else /* ... or is it asm ("mov.b %0l,r2l", ...) */
fprintf (file, "%s", names_big[REGNO (x)]);
#endif
break;
case HImode:
fprintf (file, "%s", names_big[REGNO (x)]); fprintf (file, "%s", names_big[REGNO (x)]);
break; break;
case SImode:
fprintf (file, "%s", names_extended[REGNO (x)]);
break;
default:
abort ();
}
break;
case MEM: case MEM:
fprintf (file, "@"); fprintf (file, "@");
...@@ -720,15 +1139,15 @@ print_operand_address (file, addr) ...@@ -720,15 +1139,15 @@ print_operand_address (file, addr)
switch (GET_CODE (addr)) switch (GET_CODE (addr))
{ {
case REG: case REG:
fprintf (file, "%s", names_big[REGNO (addr)]); fprintf (file, "%s", h8_reg_names[REGNO (addr)]);
break; break;
case PRE_DEC: case PRE_DEC:
fprintf (file, "-%s", names_big[REGNO (XEXP (addr, 0))]); fprintf (file, "-%s", h8_reg_names[REGNO (XEXP (addr, 0))]);
break; break;
case POST_INC: case POST_INC:
fprintf (file, "%s+", names_big[REGNO (XEXP (addr, 0))]); fprintf (file, "%s+", h8_reg_names[REGNO (XEXP (addr, 0))]);
break; break;
case PLUS: case PLUS:
...@@ -751,15 +1170,22 @@ print_operand_address (file, addr) ...@@ -751,15 +1170,22 @@ print_operand_address (file, addr)
break; break;
case CONST_INT: case CONST_INT:
if (INTVAL (addr) < 0)
{ {
int v = -INTVAL (addr); /* Since the h8/300 only has 16 bit pointers, negative values are also
those >= 32768. This happens for example with pointer minus a
fprintf (file, "-%d", v); constant. We don't want to turn (char *p - 2) into
} (char *p + 65534) because loop unrolling can build upon this
(IE: char *p + 131068). */
int n = INTVAL (addr);
if (TARGET_H8300)
n = (int) (short) n;
if (n < 0)
/* ??? Why the special case for -ve values? */
fprintf (file, "-%d", -n);
else else
fprintf (file, "%d", INTVAL (addr)); fprintf (file, "%d", n);
break; break;
}
default: default:
output_addr_const (file, addr); output_addr_const (file, addr);
...@@ -782,139 +1208,833 @@ final_prescan_insn (insn, operand, num_operands) ...@@ -782,139 +1208,833 @@ final_prescan_insn (insn, operand, num_operands)
int uid = INSN_UID (insn); int uid = INSN_UID (insn);
if (TARGET_RTL_DUMP)
{
fprintf (asm_out_file, "\n****************");
print_rtl (asm_out_file, PATTERN (insn));
fprintf (asm_out_file, "\n");
}
if (TARGET_ADDRESSES) if (TARGET_ADDRESSES)
{ {
fprintf (asm_out_file, "; %d %d\n", insn_addresses[uid], fprintf (asm_out_file, "; 0x%x %d\n", insn_addresses[uid],
insn_addresses[uid] - last_insn_address); insn_addresses[uid] - last_insn_address);
last_insn_address = insn_addresses[uid]; last_insn_address = insn_addresses[uid];
} }
} }
static void /* Prepare for an SI sized move. */
shift_n_bits (lval, operand, f, notzero)
rtx lval; int
rtx operand; do_movsi (operands)
rtx (*f) (); rtx operands[];
int notzero;
{ {
rtx label = gen_label_rtx (); rtx src = operands[1];
rtx bot = gen_label_rtx (); rtx dst = operands[0];
if (!reload_in_progress && !reload_completed)
{
if (!register_operand (dst, GET_MODE (dst)))
{
rtx tmp = gen_reg_rtx (GET_MODE (dst));
emit_move_insn (tmp, src);
operands[1] = tmp;
}
}
return 0;
}
/* Function for INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET).
Define the offset between two registers, one to be eliminated, and the other
its replacement, at the start of a routine. */
int
initial_offset (from, to)
{
int offset = 0;
if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
offset = UNITS_PER_WORD + frame_pointer_needed * UNITS_PER_WORD;
else
{
int regno;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if ((regs_ever_live[regno]
&& (!call_used_regs[regno] || regno == FRAME_POINTER_REGNUM)))
offset += UNITS_PER_WORD;
/* See the comments for get_frame_size. We need to round it up to
STACK_BOUNDARY. */
offset += ((get_frame_size () + STACK_BOUNDARY / BITS_PER_UNIT - 1)
& ~(STACK_BOUNDARY / BITS_PER_UNIT - 1));
if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
offset += UNITS_PER_WORD; /* Skip saved PC */
}
return offset;
}
/* Update the condition code from the insn. */
if (! notzero) int
notice_update_cc (body, insn)
rtx body;
rtx insn;
{
switch (get_attr_cc (insn))
{ {
/* Have to put a zero test at the top. */ case CC_NONE:
emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, lval)); /* Insn does not affect the CC at all */
emit_jump_insn (gen_beq (bot)); break;
case CC_NONE_0HIT:
/* Insn does not change the CC, but the 0't operand has been changed. */
if (cc_status.value1 != 0
&& reg_overlap_mentioned_p (recog_operand[0], cc_status.value1))
cc_status.value1 = 0;
if (cc_status.value2 != 0
&& reg_overlap_mentioned_p (recog_operand[0], cc_status.value2))
cc_status.value2 = 0;
break;
case CC_SET:
/* Insn sets CC to recog_operand[0], but overflow is impossible. */
CC_STATUS_INIT;
cc_status.flags |= CC_NO_OVERFLOW;
cc_status.value1 = recog_operand[0];
break;
case CC_COMPARE:
/* The insn is a compare instruction */
CC_STATUS_INIT;
cc_status.value1 = SET_SRC (body);
break;
case CC_CBIT:
CC_STATUS_INIT;
cc_status.flags |= CC_DONE_CBIT;
cc_status.value1 = 0;
break;
case CC_WHOOPS:
case CC_CLOBBER:
/* Insn clobbers CC. */
CC_STATUS_INIT;
break;
} }
emit_label (label); }
f (operand);
emit_insn (gen_rtx (SET, QImode, lval, /* Recognize valid operators for bit instructions */
gen_rtx (MINUS, QImode, lval, const1_rtx)));
emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, lval)); int
emit_jump_insn (gen_bne (label)); bit_operator (x, mode)
rtx x;
enum machine_mode mode;
{
enum rtx_code code = GET_CODE (x);
return (code == XOR
|| code == AND
|| code == IOR);
}
/* Shifts.
We devote a fair bit of code to getting efficient shifts since we can only
shift one bit at a time. See the .md file for more comments.
Here are some thoughts on what the absolutely positively best code is.
"Best" here means some rational trade-off between code size and speed,
where speed is more preferred but not at the expense of generating 20 insns.
H8/300 QImode shifts
1-4 - do them inline
5-6 - ASHIFT | LSHIFTRT: rotate, mask off other bits
ASHIFTRT: loop
7 - ASHIFT | LSHIFTRT: rotate, mask off other bits
ASHIFTRT: shll, subx (propagate carry bit to all bits)
H8/300 HImode shifts
1-4 - do them inline
5-6 - loop
7 - shift other way once, move byte into place, move carry bit into place
8 - move byte, zero (ASHIFT | LSHIFTRT) or sign extend other (ASHIFTRT)
9 - inline shift 1-4, move byte, set other byte
13-14 - ASHIFT | LSHIFTRT: rotate 3/2, mask, move byte, set other byte to 0
- ASHIFTRT: loop
15 - ASHIFT | LSHIFTRT: rotate 1, mask, move byte, set other byte to 0
- ASHIFTRT: shll, subx, set other byte
H8/300 SImode shifts
1-2 - do them inline
3-6 - loop
7 - shift other way once, move bytes into place,
move carry into place (possibly with sign extension)
8 - move bytes into place, zero or sign extend other
9-14 - loop
15 - shift other way once, move word into place, move carry into place
16 - move word, zero or sign extend other
17-23 - loop
24 - move bytes into place, zero or sign extend other
25-27 - loop
28-30 - ASHIFT | LSHIFTRT: rotate top byte, mask, move byte into place,
zero others
ASHIFTRT: loop
31 - ASHIFT | LSHIFTRT: rotate top byte, mask, byte byte into place,
zero others
ASHIFTRT: shll top byte, subx, copy to other bytes
H8/300H QImode shifts
- same as H8/300
H8/300H HImode shifts
- same as H8/300
H8/300H SImode shifts
(These are complicated by the fact that we don't have byte level access to
the top word.)
A word is: bytes 3,2,1,0 (msb -> lsb), word 1,0 (msw -> lsw)
1-4 - do them inline
5-14 - loop
15 - shift other way once, move word into place, move carry into place
(with sign extension for ASHIFTRT)
16 - move word into place, zero or sign extend other
17-23 - loop
24 - ASHIFT: move byte 0(msb) to byte 1, zero byte 0,
move word 0 to word 1, zero word 0
LSHIFTRT: move word 1 to word 0, move byte 1 to byte 0,
zero word 1, zero byte 1
ASHIFTRT: move word 1 to word 0, move byte 1 to byte 0,
sign extend byte 0, sign extend word 0
25-27 - either loop, or
do 24 bit shift, inline rest
28-30 - ASHIFT: rotate 4/3/2, mask
LSHIFTRT: rotate 4/3/2, mask
ASHIFTRT: loop
31 - shll, subx byte 0, sign extend byte 0, sign extend word 0
Don't Panic!!!
All of these haven't been implemented. I've just documented them and
provided hooks so they can be.
*/
int
nshift_operator (x, mode)
rtx x;
enum machine_mode mode;
{
switch (GET_CODE (x))
{
case ASHIFTRT:
case LSHIFTRT:
case ASHIFT:
return 1;
emit_label (bot); default:
/* We can't end an expand with a label. */ return 0;
emit_move_insn (operand, operand); }
} }
/* Called from the .md file to emit code to do shifts.
Returns a boolean indicating success
(currently this is always TRUE). */
int int
can_shift (code, operands, f, limit, fby_eight) expand_a_shift (mode, code, operands)
enum machine_mode mode;
int code; int code;
rtx operands[]; rtx operands[];
rtx (*f) ();
int limit;
rtx (*fby_eight) ();
{ {
extern int rtx_equal_function_value_matters; extern int rtx_equal_function_value_matters;
emit_move_insn (operands[0], operands[1]); emit_move_insn (operands[0], operands[1]);
if (GET_CODE (operands[2]) != CONST_INT) /* need a loop to get all the bits we want - we generate the
code at emit time, but need to allocate a scratch reg now */
emit_insn (gen_rtx
(PARALLEL, VOIDmode,
gen_rtvec (2,
gen_rtx (SET, VOIDmode, operands[0],
gen_rtx (code, mode, operands[0], operands[2])),
gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, QImode, 0)))));
return 1;
}
/* Shift algorithm determination.
There are various ways of doing a shift:
SHIFT_INLINE: If the amount is small enough, just generate as many one-bit
shifts as we need.
SHIFT_ROT_AND: If the amount is large but close to either end, rotate the
necessary bits into position and then set the rest to zero.
SHIFT_SPECIAL: Hand crafted assembler.
SHIFT_LOOP: If the above methods fail, just loop. */
enum shift_alg
{
SHIFT_INLINE,
SHIFT_ROT_AND,
SHIFT_SPECIAL,
SHIFT_LOOP,
SHIFT_MAX
};
/* Symbols of the various shifts which can be used as indices. */
enum shift_type
{ {
rtx lval; SHIFT_ASHIFT, SHIFT_LSHIFTRT, SHIFT_ASHIFTRT
};
/* Can't define expand a loop after rtl generation. */ /* Symbols of the various modes which can be used as indices. */
if (! rtx_equal_function_value_matters)
return 0; enum shift_mode
{
QIshift, HIshift, SIshift
};
/* For single bit shift insns, record assembler and whether the condition code
is valid afterwards. */
struct shift_insn
{
char *assembler;
int cc_valid;
};
/* Assembler instruction shift table.
lval = gen_reg_rtx (QImode); These tables are used to look up the basic shifts.
They are indexed by cpu, shift_type, and mode.
*/
convert_move (lval, operands[2], 1); static const struct shift_insn shift_one[2][3][3] =
shift_n_bits (lval, operands[0], f, 0); {
/* H8/300 */
{
/* SHIFT_ASHIFT */
{
{ "shal %X0", 1 },
{ "add.w %T0,%T0\t; shal.w", 1 },
{ "add.w %f0,%f0\t; shal.l\n\taddx %y0,%y0\n\taddx %z0,%z0\t; end shal.l", 0 }
},
/* SHIFT_LSHIFTRT */
{
{ "shlr %X0", 1 },
{ "shlr %t0\t; shlr.w\n\trotxr %s0\t; end shlr.w", 0 },
{ "shlr %z0\t; shlr.l\n\trotxr %y0\n\trotxr %x0\n\trotxr %w0\t; end shlr.l", 0 }
},
/* SHIFT_ASHIFTRT */
{
{ "shar %X0", 1 },
{ "shar %t0\t; shar.w\n\trotxr %s0\t; end shar.w", 0 },
{ "shar %z0\t; shar.l\n\trotxr %y0\n\trotxr %x0\n\trotxr %w0\t; end shar.l", 0 }
} }
else },
/* H8/300H */
{
/* SHIFT_ASHIFT */
{
{ "shal.b %X0", 1 },
{ "shal.w %T0", 1 },
{ "shal.l %S0", 1 }
},
/* SHIFT_LSHIFTRT */
{
{ "shlr.b %X0", 1 },
{ "shlr.w %T0", 1 },
{ "shlr.l %S0", 1 }
},
/* SHIFT_ASHIFTRT */
{ {
int i; { "shar.b %X0", 1 },
{ "shar.w %T0", 1 },
{ "shar.l %S0", 1 }
}
}
};
i = INTVAL (operands[2]); /* Rotates are organized by which shift they'll be used in implementing.
if (i >= 8 && fby_eight) There's no need to record whether the cc is valid afterwards because
it is the AND insn that will decide this. */
static const char *const rotate_one[2][3][3] =
{
/* H8/300 */
{ {
fby_eight (operands[0]); /* SHIFT_ASHIFT */
i -= 8; {
"rotr %X0",
"shlr %t0\t; rotr.w\n\trotxr %s0\n\tbst #7,%t0\t; end rotr.w",
0
},
/* SHIFT_LSHIFTRT */
{
"rotl %X0",
"shll %s0\t; rotl.w\n\trotxl %t0\n\tbst #0,%s0\t; end rotl.w",
0
},
/* SHIFT_ASHIFTRT */
{
"rotl %X0",
"shll %s0\t; rotl.w\n\trotxl %t0\n\tbst #0,%s0\t; end rotl.w",
0
} }
if (i > limit) },
/* H8/300H */
{
/* SHIFT_ASHIFT */
{ {
rtx lval; "rotr.b %X0",
"rotr.w %T0",
"rotr.l %S0"
},
/* SHIFT_LSHIFTRT */
{
"rotl.b %X0",
"rotl.w %T0",
"rotl.l %S0"
},
/* SHIFT_ASHIFTRT */
{
"rotl.b %X0",
"rotl.w %T0",
"rotl.l %S0"
}
}
};
/* Can't define expand a loop after rtl generation. */ /* Given CPU, MODE, SHIFT_TYPE, and shift count COUNT, determine the best
if (! rtx_equal_function_value_matters) algorithm for doing the shift. The assembler code is stored in ASSEMBLER.
return 0; We don't achieve maximum efficiency in all cases, but the hooks are here
lval = gen_reg_rtx (QImode); to do so.
emit_move_insn (lval, gen_rtx (CONST_INT, VOIDmode, i));
shift_n_bits (lval, operands[0], f, 1); For now we just use lots of switch statements. Since we don't even come
close to supporting all the cases, this is simplest. If this function ever
gets too big, perhaps resort to a more table based lookup. Of course,
at this point you may just wish to do it all in rtl.
WARNING: The constraints on insns shiftbyn_QI/HI/SI assume shifts of
1,2,3,4 will be inlined (1,2 for SI). */
static enum shift_alg
get_shift_alg (cpu, shift_type, mode, count, assembler_p, cc_valid_p)
enum attr_cpu cpu;
enum shift_type shift_type;
enum machine_mode mode;
int count;
const char **assembler_p;
int *cc_valid_p;
{
/* The default is to loop. */
enum shift_alg alg = SHIFT_LOOP;
enum shift_mode shift_mode;
/* We don't handle negative shifts or shifts greater than the word size,
they should have been handled already. */
if (count < 0 || count > GET_MODE_BITSIZE (mode))
abort ();
switch (mode)
{
case QImode:
shift_mode = QIshift;
break;
case HImode:
shift_mode = HIshift;
break;
case SImode:
shift_mode = SIshift;
break;
default:
abort ();
}
/* Assume either SHIFT_LOOP or SHIFT_INLINE.
It is up to the caller to know that looping clobbers cc. */
*assembler_p = shift_one[cpu][shift_type][shift_mode].assembler;
*cc_valid_p = shift_one[cpu][shift_type][shift_mode].cc_valid;
/* Now look for cases we want to optimize. */
switch (shift_mode)
{
case QIshift:
if (count <= 4)
return SHIFT_INLINE;
else if (count <= 6)
{
if (shift_type == SHIFT_ASHIFTRT)
{
return SHIFT_LOOP;
} }
else else
{ {
while (i--) *assembler_p = rotate_one[cpu][shift_type][shift_mode];
f (operands[0]); *cc_valid_p = 0;
return SHIFT_ROT_AND;
} }
} }
return 1; else if (count == 7)
{
if (shift_type == SHIFT_ASHIFTRT)
{
*assembler_p = "shll %X0\t; shar.b(7)\n\tsubx %X0,%X0\t; end shar.b(7)";
*cc_valid_p = 0;
return SHIFT_SPECIAL;
}
else
{
*assembler_p = rotate_one[cpu][shift_type][shift_mode];
*cc_valid_p = 0;
return SHIFT_ROT_AND;
}
}
break;
case HIshift:
if (count <= 4)
return SHIFT_INLINE;
else if (count == 8)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
*assembler_p = "mov.b %s0,%t0\t; shal.w(8)\n\tsub.b %s0,%s0\t; end shal.w(8)";
*cc_valid_p = 0;
return SHIFT_SPECIAL;
case SHIFT_LSHIFTRT:
*assembler_p = "mov.b %t0,%s0\t; shlr.w(8)\n\tsub.b %t0,%t0\t; end shlr.w(8)";
*cc_valid_p = 0;
return SHIFT_SPECIAL;
case SHIFT_ASHIFTRT:
if (cpu == CPU_H8300)
*assembler_p = "mov.b %t0,%s0\t; shar.w(8)\n\tshll %t0\n\tsubx %t0,%t0\t; end shar.w(8)";
else
*assembler_p = "mov.b %t0,%s0\t; shar.w(8)\n\texts.w %T0\t; end shar.w(8)";
*cc_valid_p = 0;
return SHIFT_SPECIAL;
}
abort ();
}
else if (count == 15)
{
if (shift_type == SHIFT_ASHIFTRT)
{
*assembler_p = "shll %t0,%t0\t; shar.w(15)\n\tsubx %t0,%t0\n\tmov.b %t0,%s0\t; end shar.w(15)";
*cc_valid_p = 0;
return SHIFT_SPECIAL;
}
else
{
*assembler_p = rotate_one[cpu][shift_type][shift_mode];
*cc_valid_p = 0;
return SHIFT_ROT_AND;
}
}
break;
case SIshift:
if (count <= (cpu == CPU_H8300 ? 2 : 4))
return SHIFT_INLINE;
else if (count == 8)
{
if (cpu == CPU_H8300)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
*assembler_p = "mov.b %y0,%z0\t; shal.l(8)\n\tmov.b %x0,%y0\n\tmov.b %w0,%x0\n\tsub.b %w0,%w0\t; end shal.l(8)";
*cc_valid_p = 0;
return SHIFT_SPECIAL;
case SHIFT_LSHIFTRT:
*assembler_p = "mov.b %x0,%w0\t; shlr.l(8)\n\tmov.b %y0,%x0\n\tmov.b %z0,%y0\n\tsub.b %z0,%z0\t; end shlr.l(8)";
*cc_valid_p = 0;
return SHIFT_SPECIAL;
case SHIFT_ASHIFTRT:
*assembler_p = "mov.b %x0,%w0\t; shar.l(8)\n\tmov.b %y0,%x0\n\tmov.b %z0,%y0\n\tshll %z0\n\tsubx %z0,%z0; end shar.l(8)";
*cc_valid_p = 0;
return SHIFT_SPECIAL;
}
}
else /* CPU_H8300H */
/* We don't have byte level access to the high word so this isn't
easy to do. For now, just loop. */
;
}
else if (count == 16)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
*assembler_p = "mov.w %f0,%e0\t; shal.l(16)\n\tsub.w %f0,%f0\t; end shal.l(16)";
*cc_valid_p = 0;
return SHIFT_SPECIAL;
case SHIFT_LSHIFTRT:
*assembler_p = "mov.w %e0,%f0\t; shlr.l(16)\n\tsub.w %e0,%e0\t; end shlr.l(16)";
*cc_valid_p = 0;
return SHIFT_SPECIAL;
case SHIFT_ASHIFTRT:
if (cpu == CPU_H8300)
*assembler_p = "mov.w %e0,%f0\t; shar.l(16)\n\tshll %z0\n\tsubx %z0,%z0\n\tmov.b %z0,%y0\t; end shar.l(16)";
else
*assembler_p = "mov.w %e0,%f0\t; shar.l(16)\n\texts.l %S0\t; end shar.l(16)";
*cc_valid_p = 0;
return SHIFT_SPECIAL;
}
}
else if (count >= 28 && count <= 30)
{
if (shift_type == SHIFT_ASHIFTRT)
{
return SHIFT_LOOP;
}
else
{
if (cpu == CPU_H8300)
return SHIFT_LOOP;
else
{
*assembler_p = rotate_one[cpu][shift_type][shift_mode];
*cc_valid_p = 0;
return SHIFT_ROT_AND;
}
}
}
else if (count == 31)
{
if (shift_type == SHIFT_ASHIFTRT)
{
if (cpu == CPU_H8300)
*assembler_p = "shll %z0\t; shar.l(31)\n\tsubx %w0,%w0\n\tmov.b %w0,%x0\n\tmov.w %f0,%e0\t; end shar.l(31)";
else
*assembler_p = "shll %e0\t; shar.l(31)\n\tsubx %w0,%w0\n\tmov.b %w0,%x0\n\tmov.w %f0,%e0\t; end shar.l(31)";
*cc_valid_p = 0;
return SHIFT_SPECIAL;
}
else
{
if (cpu == CPU_H8300)
{
if (shift_type == SHIFT_ASHIFT)
*assembler_p = "sub.w %e0,%e0\t; shal.l(31)\n\tshlr %w0\n\tmov.w %e0,%f0\n\trotxr %z0\t; end shal.l(31)";
else
*assembler_p = "sub.w %f0,%f0\t; shlr.l(31)\n\tshll %z0\n\tmov.w %f0,%e0\n\trotxl %w0\t; end shlr.l(31)";
*cc_valid_p = 0;
return SHIFT_SPECIAL;
}
else
{
*assembler_p = rotate_one[cpu][shift_type][shift_mode];
*cc_valid_p = 0;
return SHIFT_ROT_AND;
}
}
}
break;
default:
abort ();
}
return alg;
} }
int /* Emit the assembler code for doing shifts. */
domovsi (operands)
rtx operands[]; char *
emit_a_shift (insn, operands)
rtx insn;
rtx *operands;
{ {
rtx src = operands[1]; static int loopend_lab;
rtx dst = operands[0]; char *assembler;
int cc_valid;
rtx inside = PATTERN (insn);
rtx shift = operands[3];
enum machine_mode mode = GET_MODE (shift);
enum rtx_code code = GET_CODE (shift);
enum shift_type shift_type;
enum shift_mode shift_mode;
loopend_lab++;
switch (mode)
{
case QImode:
shift_mode = QIshift;
break;
case HImode:
shift_mode = HIshift;
break;
case SImode:
shift_mode = SIshift;
break;
default:
abort ();
}
if (push_operand (dst, GET_MODE (dst))) switch (code)
{ {
/* Source must be a reg. */ case ASHIFTRT:
if (! REG_P (src)) shift_type = SHIFT_ASHIFTRT;
break;
case LSHIFTRT:
shift_type = SHIFT_LSHIFTRT;
break;
case ASHIFT:
shift_type = SHIFT_ASHIFT;
break;
default:
abort ();
}
if (GET_CODE (operands[2]) != CONST_INT)
{ {
rtx tmp = gen_reg_rtx (GET_MODE (dst)); /* Indexing by reg, so have to loop and test at top */
output_asm_insn ("mov.b %X2,%X4", operands);
fprintf (asm_out_file, "\tble .Lle%d\n", loopend_lab);
emit_move_insn (tmp, src); /* Get the assembler code to do one shift. */
operands[1] = tmp; get_shift_alg (cpu_type, shift_type, mode, 1, &assembler, &cc_valid);
} }
else
{
int n = INTVAL (operands[2]);
enum shift_alg alg;
/* If the count is negative, make it 0. */
if (n < 0)
n = 0;
/* If the count is too big, truncate it.
ANSI says shifts of GET_MODE_BITSIZE are undefined - we choose to
do the intuitive thing. */
else if (n > GET_MODE_BITSIZE (mode))
n = GET_MODE_BITSIZE (mode);
alg = get_shift_alg (cpu_type, shift_type, mode, n, &assembler, &cc_valid);
switch (alg)
{
case SHIFT_INLINE:
while (--n >= 0)
output_asm_insn (assembler, operands);
if (cc_valid)
cc_status.value1 = operands[0];
return "";
case SHIFT_ROT_AND:
{
int m = GET_MODE_BITSIZE (mode) - n;
int mask = (shift_type == SHIFT_ASHIFT
? ((1 << GET_MODE_BITSIZE (mode) - n) - 1) << n
: (1 << GET_MODE_BITSIZE (mode) - n) - 1);
char insn_buf[200];
/* Not all possibilities of rotate are supported. They shouldn't
be generated, but let's watch for 'em. */
if (assembler == 0)
abort ();
while (--m >= 0)
output_asm_insn (assembler, operands);
if (TARGET_H8300)
{
switch (mode)
{
case QImode:
sprintf (insn_buf, "and #%d,%%X0\t; end shift %d via rotate+and",
mask, n);
cc_status.value1 = operands[0];
break;
case HImode:
sprintf (insn_buf, "and #%d,%%s0\n\tand #%d,%%t0\t; end shift %d via rotate+and",
mask & 255, mask >> 8, n);
break;
case SImode:
abort ();
} }
return 0; }
else
{
sprintf (insn_buf, "and.%c #%d,%%%c0",
"bwl"[shift_mode], mask,
mode == QImode ? 'X' : mode == HImode ? 'T' : 'S');
cc_status.value1 = operands[0];
}
output_asm_insn (insn_buf, operands);
return "";
}
case SHIFT_SPECIAL:
output_asm_insn (assembler, operands);
return "";
}
/* Need a loop, move limit to tmp reg */
fprintf (asm_out_file, "\tmov.b #%d,%sl\n", n, names_big[REGNO (operands[4])]);
}
fprintf (asm_out_file, ".Llt%d:\n", loopend_lab);
output_asm_insn (assembler, operands);
output_asm_insn ("add #0xff,%X4", operands);
fprintf (asm_out_file, "\tbne .Llt%d\n", loopend_lab);
fprintf (asm_out_file, ".Lle%d:\n", loopend_lab);
return "";
} }
/* Fix the operands of a gen_xxx so that it could become a bit
operating insn. */
int int
io (FROM, TO) fix_bit_operand (operands, what, type)
rtx *operands;
char what;
enum rtx_code type;
{ {
int OFFSET = 0; /* The bit_operand predicate accepts any memory durint RTL generation, but
only 'U' memory afterwards, so if this is a MEM operand, we must force
it to be valid for 'U' by reloading the address. */
if ((FROM) == ARG_POINTER_REGNUM && (TO) == FRAME_POINTER_REGNUM) if (GET_CODE (operands[2]) == CONST_INT)
(OFFSET) = 2 + frame_pointer_needed * 2;
else
{ {
int regno; if (CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), what))
int offset = 0; {
/* Ok to have a memory dest. */
if (GET_CODE (operands[0]) == MEM && !EXTRA_CONSTRAINT (operands[0], 'U'))
{
rtx mem;
mem = gen_rtx (MEM, GET_MODE (operands[0]),
copy_to_mode_reg (Pmode, XEXP (operands[0], 0)));
RTX_UNCHANGING_P (mem) = RTX_UNCHANGING_P (operands[0]);
MEM_IN_STRUCT_P (mem) = MEM_IN_STRUCT_P (operands[0]);
MEM_VOLATILE_P (mem) = MEM_VOLATILE_P (operands[0]);
operands[0] = mem;
}
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if (GET_CODE (operands[1]) == MEM && !EXTRA_CONSTRAINT (operands[1], 'U'))
if ((regs_ever_live[regno] {
&& (! call_used_regs[regno] || regno == FRAME_POINTER_REGNUM))) rtx mem;
offset += 2; mem = gen_rtx (MEM, GET_MODE (operands[1]),
copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
RTX_UNCHANGING_P (mem) = RTX_UNCHANGING_P (operands[1]);
MEM_IN_STRUCT_P (mem) = MEM_IN_STRUCT_P (operands[1]);
MEM_VOLATILE_P (mem) = MEM_VOLATILE_P (operands[1]);
operands[1] = mem;
}
return 0;
}
}
(OFFSET) = offset + get_frame_size (); /* Dest and src op must be register. */
if ((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM) operands[1] = force_reg (QImode, operands[1]);
(OFFSET) += 2; /* Skip saved PC. */ {
rtx res = gen_reg_rtx (QImode);
emit_insn (gen_rtx (SET, VOIDmode, res, gen_rtx (type, QImode, operands[1], operands[2])));
emit_insn (gen_rtx (SET, VOIDmode, operands[0], res));
} }
return OFFSET; return 1;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment