blob: 4122f0e1eeb20a63f556c35ef03b2967a47f0b89 [file] [log] [blame]
/* Subroutines used for code generation on Ubicom IP2022
Communications Controller.
Copyright (C) 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
Contributed by Red Hat, Inc and Ubicom, Inc.
This file is part of GCC.
GCC 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.
GCC 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.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.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 "output.h"
#include "insn-attr.h"
#include "insn-addr.h"
#include "flags.h"
#include "reload.h"
#include "tree.h"
#include "expr.h"
#include "optabs.h"
#include "toplev.h"
#include "obstack.h"
#include "function.h"
#include "recog.h"
#include "tm_p.h"
#include "target.h"
#include "target-def.h"
#include "basic-block.h"
/* There are problems with 'frame_pointer_needed'. If we force it
on, we either end up not eliminating uses of FP, which results in
SPILL register failures or we may end up with calculation errors in
the stack offsets. Isolate the decision process into a simple macro. */
#define CHAIN_FRAMES (frame_pointer_needed || FRAME_POINTER_REQUIRED)
static int ip2k_naked_function_p (tree);
#ifdef IP2K_MD_REORG_PASS
static void mdr_resequence_xy_yx (rtx);
static void mdr_pres_replace_and_recurse (rtx, rtx, rtx);
static void mdr_propagate_reg_equivs_sequence (rtx, rtx, rtx);
static void mdr_propagate_reg_equivs (rtx);
static int track_dp_reload (rtx , rtx *, int , int);
static void mdr_try_dp_reload_elim (rtx);
static void mdr_try_move_dp_reload (rtx);
static void mdr_try_move_pushes (rtx);
static void mdr_try_propagate_clr_sequence (rtx, unsigned int);
static void mdr_try_propagate_clr (rtx);
static void mdr_try_propagate_move_sequence (rtx, rtx, rtx);
static void mdr_try_propagate_move (rtx);
static void mdr_try_remove_redundant_insns (rtx);
static int track_w_reload (rtx, rtx *, int , int);
static void mdr_try_wreg_elim (rtx);
#endif /* IP2K_MD_REORG_PASS */
static void ip2k_reorg (void);
static int ip2k_check_can_adjust_stack_ref (rtx, int);
static void ip2k_adjust_stack_ref (rtx *, int);
static int ip2k_xexp_not_uses_reg_for_mem (rtx, unsigned int);
static tree ip2k_handle_progmem_attribute (tree *, tree, tree, int, bool *);
static tree ip2k_handle_fndecl_attribute (tree *, tree, tree, int, bool *);
static bool ip2k_rtx_costs (rtx, int, int, int *);
static int ip2k_address_cost (rtx);
static void ip2k_init_libfuncs (void);
const struct attribute_spec ip2k_attribute_table[];
/* Initialize the GCC target structure. */
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE function_epilogue
#undef TARGET_ASM_UNIQUE_SECTION
#define TARGET_ASM_UNIQUE_SECTION unique_section
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE ip2k_attribute_table
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS ip2k_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST ip2k_address_cost
#undef TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG ip2k_reorg
#undef TARGET_INIT_LIBFUNCS
#define TARGET_INIT_LIBFUNCS ip2k_init_libfuncs
struct gcc_target targetm = TARGET_INITIALIZER;
/* Prologue/Epilogue size in words. */
static int prologue_size;
static int epilogue_size;
/* compare and test instructions for the IP2K are materialized by
the conditional branch that uses them. This is because conditional
branches are skips over unconditional branches. */
rtx ip2k_compare_operands[3]; /* Additional operands for condition code. */
int ip2k_test_flag; /* Indicates Z, WREG contain condition code
information. */
/* Some ip2k patterns push a byte onto the stack and then access
SP-relative addresses. Since reload doesn't know about these
pushes, we must track them internally with a %< (push) or %> (pop)
indicator. */
static int ip2k_stack_delta;
/* Track if or how far our ip2k reorganization pass has run. */
int ip2k_reorg_in_progress = 0;
int ip2k_reorg_completed = 0;
int ip2k_reorg_split_dimode = 0;
int ip2k_reorg_split_simode = 0;
int ip2k_reorg_split_himode = 0;
int ip2k_reorg_split_qimode = 0;
int ip2k_reorg_merge_qimode = 0;
/* Set up local allocation order. */
void
ip2k_init_local_alloc (int *rao)
{
static const int alloc_order[] = REG_ALLOC_ORDER;
memcpy (rao, alloc_order, sizeof (alloc_order));
}
/* Returns the number of bytes of arguments automatically
popped when returning from a subroutine call.
FUNDECL is the declaration node of the function (as a tree),
FUNTYPE is the data type of the function (as a tree),
or for a library call it is an identifier node for the subroutine name.
SIZE is the number of bytes of arguments passed on the stack. */
int
ip2k_return_pops_args (tree fundecl ATTRIBUTE_UNUSED, tree funtype, int size)
{
if (TREE_CODE (funtype) == IDENTIFIER_NODE)
return size;
if (TYPE_ARG_TYPES (funtype) == NULL_TREE
|| (TREE_VALUE (tree_last (TYPE_ARG_TYPES (funtype))) == void_type_node))
return size;
return 0;
}
/* Return nonzero if FUNC is a naked function. */
static int
ip2k_naked_function_p (tree func)
{
tree a;
if (TREE_CODE (func) != FUNCTION_DECL)
abort ();
a = lookup_attribute ("naked", DECL_ATTRIBUTES (func));
return a != NULL_TREE;
}
/* Output function prologue. */
void
function_prologue (FILE *file, HOST_WIDE_INT size)
{
int leaf_func_p;
int main_p;
int reg;
rtx operands[2];
prologue_size = epilogue_size = 0;
if (ip2k_naked_function_p (current_function_decl))
{
fprintf (file, "/* prologue: naked */\n");
return;
}
leaf_func_p = leaf_function_p ();
main_p = MAIN_NAME_P (DECL_NAME (current_function_decl));
/* For now, we compute all these facts about the function, but don't
take any action based on the information. */
prologue_size = 0;
fprintf (file, "/* prologue: frame size=" HOST_WIDE_INT_PRINT_DEC " */\n",
size);
/* Unless we're a leaf we need to save the return PC. */
if (! leaf_func_p)
{
OUT_AS1 (push, calll);
OUT_AS1 (push, callh);
prologue_size += 4;
}
/* We need to save the old FP and set the new FP pointing at the
stack location where the old one is saved. Note that because of
post-decrement addressing, the SP is off-by-one after the
push, so we harvest the SP address BEFORE we push the MSBs of
the FP. */
if (CHAIN_FRAMES)
{
OUT_AS1 (push, REG_FP+1); /* Save old LSBs. */
OUT_AS2 (mov, w, spl);
OUT_AS2 (mov, REG_FP+1, w); /* SPL -> FPL */
OUT_AS2 (mov, w, sph); /* Freeze SP MSBs */
OUT_AS1 (push, REG_FP); /* Save old MSBs */
OUT_AS2 (mov, REG_FP, w); /* SPH -> FPH */
prologue_size += 12;
}
for (reg = (CHAIN_FRAMES) ? (REG_FP - 1) : (REG_FP + 1);
reg > 0; --reg)
{
if (regs_ever_live[reg] && ! call_used_regs[reg])
{
fprintf (file, "\t" AS1 (push,%s) "\n", reg_names[reg]);
prologue_size += 2;
}
}
if (size)
{
operands[0] = GEN_INT (size);
switch (size & 0xff)
{
case 0:
break;
case 1:
OUT_AS1 (dec, spl);
prologue_size += 2;
break;
default:
OUT_AS2 (mov, w, %L0);
OUT_AS2 (sub, spl, w);
prologue_size += 4;
}
switch (size & 0xff00)
{
case 0:
break;
case 0x100:
OUT_AS1 (dec, sph);
prologue_size += 2;
break;
default:
if ((size & 0xff) != ((size >> 8) & 0xff))
OUT_AS2 (mov, w, %H0); /* Otherwise W has value we want. */
OUT_AS2 (sub, sph, w);
prologue_size += 4;
}
}
/* XXX - change this to use the carry-propagating subtract trick. */
if (flag_stack_check)
{
OUT_AS2 (mov, w, sph);
OUT_AS2 (cmp, w, #%%hi8data(_end));
OUT_AS1 (sc, ); /* C == 0 -> hi8(edata) < sph */
OUT_AS1 (page, 1f);
OUT_AS1 (jmp, 1f);
OUT_AS1 (sz, ); /* Z == 1 -> look at low byte */
OUT_AS1 (page,0f);
OUT_AS1 (jmp,0f); /* sp < edata, so raise stack fault */
OUT_AS2 (mov, w, spl);
OUT_AS2 (cmp, w, #%%lo8data(_end));
OUT_AS1 (sc,); /* C==1 -> lo8(edata) >= spl */
OUT_AS1 (page,1f);
OUT_AS1 (jmp,1f);
OUT_AS1 (0:,);
output_asm_insn ("push\t$ff", operands);
OUT_AS1 (system,);
OUT_AS1 (1:, );
prologue_size += 30;
}
}
/* Output function epilogue. */
void
function_epilogue (FILE *file, HOST_WIDE_INT size)
{
int leaf_func_p;
int reg,savelimit;
rtx operands[2]; /* Dummy used by OUT_ASn */
int args_locals_size = current_function_args_size;
int saved_regs_p = 0;
int need_ret = 1;
/* Use this opportunity to reset the reorg flags! */
ip2k_reorg_in_progress = 0;
ip2k_reorg_completed = 0;
ip2k_reorg_split_dimode = 0;
ip2k_reorg_split_simode = 0;
ip2k_reorg_split_himode = 0;
ip2k_reorg_split_qimode = 0;
ip2k_reorg_merge_qimode = 0;
if (ip2k_naked_function_p (current_function_decl))
{
fprintf (file, "/* epilogue: naked */\n");
return;
}
leaf_func_p = leaf_function_p ();
epilogue_size = 0;
fprintf (file, "/* epilogue: frame size=" HOST_WIDE_INT_PRINT_DEC " */\n",
size);
savelimit = (CHAIN_FRAMES) ? REG_FP : (REG_FP + 2);
for (reg = 0; reg < savelimit; reg++)
if (regs_ever_live[reg] && ! call_used_regs[reg])
{
saved_regs_p = 1;
break;
}
if (size)
{
if (leaf_func_p && !CHAIN_FRAMES && !saved_regs_p
&& current_function_pops_args)
args_locals_size = current_function_args_size + size;
else
{
operands[0] = GEN_INT (size);
switch (size & 0xff)
{
default:
OUT_AS2 (mov, w, %L0);
OUT_AS2 (add, spl, w);
epilogue_size += 4;
/* fall-through */
case 0:
break;
case 1:
OUT_AS1 (inc, spl);
epilogue_size += 2;
}
switch (size & 0xff00)
{
default:
if ((size & 0xff) != ((size >> 8) & 0xff))
OUT_AS2 (mov, w, %H0);
OUT_AS2 (add, sph, w);
epilogue_size += 4;
/* fall-through */
case 0:
break;
case 0x100:
OUT_AS1 (inc, sph);
epilogue_size += 2;
}
}
}
for (reg = 0; reg < savelimit; reg++)
{
if (regs_ever_live[reg] && ! call_used_regs[reg])
{
fprintf (file, "\t" AS1 (pop,%s) "\n", reg_names[reg]);
prologue_size += 2;
}
}
if (CHAIN_FRAMES
&& ! (current_function_pops_args
&& current_function_args_size >= 2
&& current_function_args_size < 0x100))
{
OUT_AS1 (pop, REG_FP);
OUT_AS1 (pop, REG_FP+1);
epilogue_size += 4;
}
if (! leaf_func_p)
{
if (current_function_pops_args
&& current_function_args_size >= 2
&& current_function_args_size < 0x100)
{
if (current_function_args_size == 2)
{
if (CHAIN_FRAMES)
{
OUT_AS1 (page, __fp_pop2_args_ret);
OUT_AS1 (jmp, __fp_pop2_args_ret);
}
else
{
OUT_AS1 (page, __pop2_args_ret);
OUT_AS1 (jmp, __pop2_args_ret);
}
epilogue_size += 4;
}
else
{
operands[0] = GEN_INT (current_function_args_size);
OUT_AS2 (mov, w, %L0);
if (CHAIN_FRAMES)
{
OUT_AS1 (page, __fp_pop_args_ret);
OUT_AS1 (jmp, __fp_pop_args_ret);
}
else
{
OUT_AS1 (page, __pop_args_ret);
OUT_AS1 (jmp, __pop_args_ret);
}
epilogue_size += 6;
}
need_ret = 0;
}
else
{
OUT_AS1 (pop, callh);
OUT_AS1 (pop, calll);
epilogue_size += 4;
}
}
else
{
if (current_function_pops_args
&& args_locals_size >= 2
&& args_locals_size < 0x100)
{
if (args_locals_size == 2)
{
if (CHAIN_FRAMES)
{
OUT_AS1 (page, __leaf_fp_pop2_args_ret);
OUT_AS1 (jmp, __leaf_fp_pop2_args_ret);
epilogue_size += 4;
need_ret = 0;
}
}
else
{
operands[0] = GEN_INT (args_locals_size);
if (CHAIN_FRAMES)
{
OUT_AS2 (mov, w, %L0);
OUT_AS1 (page, __leaf_fp_pop_args_ret);
OUT_AS1 (jmp, __leaf_fp_pop_args_ret);
epilogue_size += 6;
need_ret = 0;
}
}
}
}
if (current_function_pops_args && args_locals_size && need_ret)
{
operands[0] = GEN_INT (args_locals_size);
switch (args_locals_size & 0xff)
{
default:
OUT_AS2 (mov, w, %L0);
OUT_AS2 (add, spl, w);
epilogue_size += 4;
/* fall-through */
case 0:
break;
case 1:
OUT_AS1 (inc, spl);
epilogue_size += 2;
}
switch (args_locals_size & 0xff00)
{
default:
if ((args_locals_size & 0xff) != ((args_locals_size >> 8) & 0xff))
OUT_AS2 (mov, w, %H0);
OUT_AS2 (add, sph, w);
epilogue_size += 4;
/* fall-through */
case 0:
break;
case 0x100:
OUT_AS1 (inc, sph);
epilogue_size += 2;
}
}
if (need_ret)
{
OUT_AS1 (ret,);
epilogue_size += 2;
}
fprintf (file, "/* epilogue end (size=%d) */\n", epilogue_size);
}
/* Return the difference between the registers after the function
prologue.
Stack Frame grows down:
ARGUMENTS
<------ AP ($102:$103)
RETURN PC (unless leaf function)
SAVEDFP (if needed)
<------ FP [HARD_FRAME_POINTER] ($FD:$FE)
SAVED REGS
<------ VFP [$100:$101]
STACK ALLOCATION
<------ SP ($6:$7) */
int
ip2k_init_elim_offset (int from, int to)
{
int leaf_func_p = leaf_function_p ();
int no_saved_pc = leaf_func_p
|| ip2k_naked_function_p (current_function_decl);
int offset;
int reg;
int reglimit;
if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
return get_frame_size () + 1;
if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
return (CHAIN_FRAMES ? 2 : 0) + (no_saved_pc ? 0 : 2);
/* Count all the registers we had to preserve. */
reglimit = CHAIN_FRAMES ? REG_FP : (REG_FP + 2);
for (offset = 0,reg = 0; reg < reglimit; ++reg)
{
if ((regs_ever_live[reg] && ! call_used_regs[reg]))
{
++offset;
}
}
if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
return -offset;
if (from == HARD_FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
/* Add in the stack-local variables. */
return offset + get_frame_size () + 1;
if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
/* Add stack-locals plus saved FP and PC. */
return offset + get_frame_size () + 1
+ (CHAIN_FRAMES ? 2 : 0) + (no_saved_pc ? 0 : 2);
abort (); /* Unanticipated elimination. */
}
/* Return nonzero if X (an RTX) is a legitimate memory address on the target
machine for a memory operand of mode MODE. */
int
legitimate_address_p (enum machine_mode mode, rtx x, int strict)
{
int off;
if (GET_CODE (x) == SUBREG)
x = SUBREG_REG (x);
switch (GET_CODE (x))
{
case REG:
/* IP allows indirection without offset - only okay if
we don't require access to multiple bytes. */
if (REGNO (x) == REG_IP)
return (GET_MODE_SIZE (mode) == 1) ? 'R' : 0;
/* We can indirect through DP or SP register. */
if (strict ? REG_OK_FOR_BASE_STRICT_P (x)
: REG_OK_FOR_BASE_NOSTRICT_P (x))
return 'S';
break;
case PLUS:
/* Offsets from DP or SP are legal in the range 0..127 */
{
rtx op1, op2;
op1 = XEXP (x, 0);
op2 = XEXP (x, 1);
if (REG_P (op2) && ! REG_P (op1))
{
rtx tmp = op1;
op1 = op2;
op2 = tmp;
}
/* Don't let anything but R+I through.. */
if (! REG_P (op1)
|| REG_P (op2)
|| GET_CODE (op2) != CONST_INT)
return 0;
switch (REGNO (op1))
{
case REG_DP: /* only 0..127 displacement */
case REG_SP:
off = 2 * GET_MODE_SIZE (mode);
if (! off)
off = 1;
if (INTVAL (op2) < 0 || INTVAL (op2) > (128 - off))
return 0; /* Positive must be small enough that after
splitting all pieces are addressed. */
return 'S'; /* Safe displacement. */
case REG_IP:
if (GET_MODE_SIZE (mode) <= 1 && INTVAL (op2) == 0)
return (GET_MODE_SIZE (mode) == 1) ? 'R' : 0;
return 0;
case REG_AP:
case REG_FP:
case REG_VFP:
default:
if (strict || ! REG_OK_FOR_BASE_NOSTRICT_P (op1))
return 0; /* Allow until reload. */
return 'S';
}
}
break;
case CONST:
case SYMBOL_REF:
/* We always allow references to things in code space. */
return is_regfile_address (x) ? 0 : 'C';
case LABEL_REF:
return 'L';
default:
return 0;
}
return 0;
}
/* Is ADDR mode dependent? */
int
ip2k_mode_dependent_address (rtx addr)
{
switch (GET_CODE (addr))
{
case POST_INC:
case POST_DEC:
case PRE_INC:
case PRE_DEC:
return 1;
case REG:
return (REGNO (addr) == REG_IP); /* Can't do IP displaced addresses. */
default:
return 0; /* Assume no dependency. */
}
}
/* Attempts to replace X with a valid
memory address for an operand of mode MODE. */
rtx
legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED, rtx scratch)
{
rtx reg;
/* You might think that we could split up a symbolic address by
adding the HIGH 8 bits and doing a displacement off the dp. But
because we only have 7 bits of offset, that doesn't actually
help. So only constant displacements are likely to obtain an
advantage. */
if (GET_CODE (x) == PLUS && REG_P (XEXP (x, 0))
&& GET_CODE (XEXP (x, 1)) == CONST_INT
&& ! CONST_OK_FOR_LETTER_P (INTVAL (XEXP (x, 1)), 'K'))
{
int offset = INTVAL (XEXP (x, 1));
reg = scratch ? scratch : gen_reg_rtx (Pmode);
emit_insn (gen_rtx_SET (VOIDmode, reg,
gen_rtx_PLUS (Pmode, XEXP (x, 0),
GEN_INT (offset & 0xffc0))));
x = gen_rtx_PLUS (Pmode, reg, GEN_INT (offset & 0x3f));
}
return x; /* We don't have any other tricks. */
}
/* Determine if X is a 'data' address or a code address. All static
data and stack variables reside in data memory. Only code is believed
to be in PRAM or FLASH. */
int
is_regfile_address (rtx x)
{
while (1)
switch (GET_CODE (x))
{
case SYMBOL_REF:
return ! SYMBOL_REF_FUNCTION_P (x); /* Declared as function. */
case CONST:
case PLUS:
x = XEXP (x, 0);
break;
case CONST_INT:
case REG:
case SUBREG:
return 1;
case LABEL_REF:
return 0;
default:
return 0;
}
return 0;
}
/* Output ADDR to FILE as address. */
void
print_operand_address (FILE *file, rtx addr)
{
switch (GET_CODE (addr))
{
case SUBREG:
addr = alter_subreg (&addr);
/* fall-through */
case REG:
fprintf (file, "(%s)",
REGNO (addr) == REG_DP ? "DP"
: REGNO (addr) == REG_SP ? "SP"
: REGNO (addr) == REG_IP ? "IP"
: REGNO (addr) == REG_VFP ? "VFP" /* Should never see this */
: REGNO (addr) == REG_AP ? "AP" /* or this, either. */
: reg_names[REGNO (addr)]);
break;
case PRE_DEC:
case POST_INC:
abort ();
break;
case CONST:
addr = XEXP (addr, 0);
print_operand_address (file, XEXP (addr, 0));
fprintf (file, "+");
print_operand_address (file, XEXP (addr, 1));
return;
case LO_SUM:
if (is_regfile_address (XEXP (addr, 1)))
fprintf (file, "%%lo8data(");
else
fprintf (file, "%%lo8insn(");
print_operand_address (file, XEXP (addr, 1));
fprintf (file, ")");
print_operand_address (file, XEXP (addr, 0));
break;
case PLUS: /* Ought to be stack or dp references. */
if (XEXP (addr, 1) == const0_rtx
&& GET_CODE (XEXP (addr, 0)) == PLUS)
{
print_operand_address (file, XEXP (addr, 0));
return;
}
if (! REG_P (XEXP (addr, 0)) || REGNO (XEXP (addr, 0)) != REG_IP)
print_operand_address (file, XEXP (addr, 1)); /* const */
print_operand_address (file, XEXP (addr, 0)); /* (reg) */
break;
case HIGH:
if (is_regfile_address (XEXP (addr, 0)))
fprintf (file, "%%hi8data(");
else
fprintf (file, "%%hi8insn(");
output_addr_const (file, XEXP (addr, 0));
fprintf (file, ")");
break;
default:
output_addr_const (file, addr);
}
}
/* Output X as assembler operand to file FILE. */
void
print_operand (FILE *file, rtx x, int code)
{
int abcd = 0;
unsigned long value;
switch (code)
{
case '<': /* Push */
ip2k_stack_delta++;
return;
case '>': /* Pop */
ip2k_stack_delta--;
return;
case 'A':
case 'B':
case 'C':
case 'D':
abcd = code - 'A';
break;
case 'H':
abcd = 0;
break;
case 'L':
abcd = 1;
break;
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z':
abcd = code - 'S';
default:
break;
}
if (ip2k_short_operand (x, GET_MODE (x))
&& ip2k_address_uses_reg_p (x, REG_SP))
/* An SP-relative address needs to account for interior stack
pushes that reload didn't know about when it calculated the
stack offset. */
abcd += ip2k_stack_delta;
switch (GET_CODE (x))
{
case SUBREG:
x = alter_subreg (&x);
/* fall-through */
case REG:
fprintf (file, reg_names[true_regnum (x) + abcd]);
break;
case CONST_INT:
switch (code)
{
case 'x':
fprintf (file, "$%x", (int)(INTVAL (x) & 0xffff));
break;
case 'b':
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x)); /* bit selector */
break;
case 'e': /* "1 << n" - e.g. "exp" */
fprintf (file, "#%d", 1 << INTVAL (x));
break;
case 'A':
case 'B':
case 'C':
case 'D':
value = INTVAL (x);
value >>= 8 * (3 - abcd);
value &= 0xff;
fprintf (file, "#%ld", value);
break;
case 'H':
fprintf (file, "#%d", (int)((INTVAL (x) >> 8) & 0xff));
break;
case 'L':
fprintf (file, "#%d", (int)(INTVAL (x) & 0xff));
break;
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z':
value = ((unsigned long long)INTVAL (x)) >> (8 * (7 - abcd)) & 0xff;
fprintf (file, "#%ld", value);
break;
default:
fprintf (file, "#" HOST_WIDE_INT_PRINT_DEC, INTVAL (x));
}
break;
case SYMBOL_REF:
case LABEL_REF:
case CODE_LABEL:
case CONST:
switch (code)
{
case 'A':
case 'B':
case 'C':
case 'D':
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z':
abort (); /* Probably an error. */
break;
case 'H':
fprintf (file, "#%s(",
is_regfile_address (x) ? "%hi8data"
: "%hi8insn");
print_operand_address (file, x);
fputc (')', file);
break;
case 'L':
fprintf (file, "#%s(",
is_regfile_address (x) ? "%lo8data"
: "%lo8insn");
print_operand_address (file, x);
fputc (')', file);
break;
default:
print_operand_address (file, x);
}
break;
case MEM:
{
rtx addr = XEXP (x, 0);
if (GET_CODE (addr) == SUBREG)
addr = alter_subreg (&x);
if (CONSTANT_P (addr) && abcd)
{
fputc ('(', file);
print_operand_address (file, addr);
fprintf (file, ")+%d", abcd);
}
else if (abcd)
{
switch (GET_CODE (addr))
{
case PLUS:
abcd += INTVAL (XEXP (addr, 1));
/* Worry about (plus (plus (reg DP) (const_int 10))
(const_int 0)) */
if (GET_CODE (XEXP (addr, 0)) == PLUS)
{
addr = XEXP (addr, 0);
abcd += INTVAL (XEXP (addr, 1));
}
fprintf (file, "%d", abcd);
print_operand_address (file, XEXP (addr, 0));
break;
case REG:
default:
fprintf (file, "%d", abcd);
print_operand_address (file, addr);
}
}
else if (GET_CODE (addr) == REG
&& (REGNO (addr) == REG_DP || REGNO (addr) == REG_SP))
{
fprintf (file, "0");
print_operand_address (file, addr);
}
else
print_operand_address (file, addr);
}
break;
case CONST_DOUBLE:
/* Is this an integer or a floating point value? */
if (GET_MODE (x) == VOIDmode)
{
switch (code)
{
case 'S':
case 'T':
case 'U':
case 'V':
value = CONST_DOUBLE_HIGH (x);
value >>= 8 * (3 - abcd);
value &= 0xff;
fprintf (file, "#%ld", value);
break;
case 'W':
case 'X':
case 'Y':
case 'Z':
value = CONST_DOUBLE_LOW (x);
value >>= 8 * (7 - abcd);
value &= 0xff;
fprintf (file, "#%ld", value);
break;
}
}
else
{
REAL_VALUE_TYPE rv;
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_SINGLE (rv, value);
fprintf (file, "0x%lx", value);
}
break;
default:
fatal_insn ("bad operand", x);
}
}
/* Remember the operands for the compare. */
const char *
ip2k_set_compare (rtx x, rtx y)
{
ip2k_compare_operands[0] = x;
ip2k_compare_operands[1] = y;
return "";
}
/* Emit the code for sCOND instructions. */
const char *
ip2k_gen_sCOND (rtx insn ATTRIBUTE_UNUSED, enum rtx_code code, rtx dest)
{
#define operands ip2k_compare_operands
enum machine_mode mode;
operands[2] = dest;
mode = GET_MODE (operands[0]);
if ((mode != QImode) && (mode != HImode)
&& (mode != SImode) && (mode != DImode))
mode = GET_MODE (operands[1]);
/* We have a fast path for a specific type of QImode compare. We ought
to extend this for larger cases too but that wins less frequently and
introduces a lot of complexity. */
if (mode == QImode
&& !rtx_equal_p (operands[0], operands[2])
&& !rtx_equal_p (operands[1], operands[2])
&& (! REG_P (operands[2])
|| (ip2k_xexp_not_uses_reg_p (operands[0], REGNO (operands[2]), 1)
&& ip2k_xexp_not_uses_reg_p (operands[1],
REGNO (operands[2]), 1))))
{
OUT_AS1 (clr, %2);
if (immediate_operand (operands[1], QImode)
&& ((INTVAL (operands[1]) & 0xff) == 0xff))
{
if (code == EQ)
OUT_AS2 (incsnz, w, %0);
else
OUT_AS2 (incsz, w, %0);
}
else if (immediate_operand (operands[1], QImode)
&& ((INTVAL (operands[1]) & 0xff) == 0x01))
{
if (code == EQ)
OUT_AS2 (decsnz, w, %0);
else
OUT_AS2 (decsz, w, %0);
}
else if (ip2k_compare_operands[1] == const0_rtx)
{
OUT_AS2 (mov, w, %0);
if (code == EQ)
OUT_AS1 (snz,);
else
OUT_AS1 (sz,);
}
else
{
OUT_AS2 (mov, w, %0);
if (code == EQ)
OUT_AS2 (csne, w, %1);
else
OUT_AS2 (cse, w, %1);
}
OUT_AS1 (inc, %2);
}
else
{
if (ip2k_compare_operands[1] == const0_rtx)
{
switch (mode)
{
case QImode:
OUT_AS2 (mov, w, %0);
break;
case HImode:
OUT_AS2 (mov, w, %H0);
OUT_AS2 (or, w, %L0);
break;
case SImode:
OUT_AS2 (mov, w, %A0);
OUT_AS2 (or, w, %B0);
OUT_AS2 (or, w, %C0);
OUT_AS2 (or, w, %D0);
break;
case DImode:
OUT_AS2 (mov, w, %S0);
OUT_AS2 (or, w, %T0);
OUT_AS2 (or, w, %U0);
OUT_AS2 (or, w, %V0);
OUT_AS2 (or, w, %W0);
OUT_AS2 (or, w, %X0);
OUT_AS2 (or, w, %Y0);
OUT_AS2 (or, w, %Z0);
break;
default:
abort ();
}
}
else
{
switch (mode)
{
case QImode:
OUT_AS2 (mov, w, %1);
OUT_AS2 (cmp, w, %0);
break;
case HImode:
OUT_AS2 (mov, w, %H1);
OUT_AS2 (cmp, w, %H0);
OUT_AS1 (sz,);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %L1);
OUT_AS2 (cmp, w, %L0);
OUT_AS1 (2:,);
break;
case SImode:
if (code == EQ)
{
OUT_AS2 (mov, w, #1);
OUT_AS2 (mov, mulh, w);
}
else
OUT_AS1 (clr, mulh);
OUT_AS2 (mov, w, %A1);
OUT_AS2 (cse, w, %A0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %B1);
OUT_AS2 (cse, w, %B0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %C1);
OUT_AS2 (cse, w, %C0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %D1);
OUT_AS2 (cse, w, %D0);
OUT_AS1 (2:,);
if (code == EQ)
OUT_AS1 (dec, mulh);
else
OUT_AS1 (inc, mulh);
OUT_AS2 (mov, w, mulh);
OUT_AS2 (mov, %2, w);
return "";
case DImode:
if (code == EQ)
{
OUT_AS2 (mov, w, #1);
OUT_AS2 (mov, mulh, w);
}
else
OUT_AS1 (clr, mulh);
OUT_AS2 (mov, w, %S1);
OUT_AS2 (cse, w, %S0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %T1);
OUT_AS2 (cse, w, %T0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %U1);
OUT_AS2 (cse, w, %U0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %V1);
OUT_AS2 (cse, w, %V0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %W1);
OUT_AS2 (cse, w, %W0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %X1);
OUT_AS2 (cse, w, %X0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %Y1);
OUT_AS2 (cse, w, %Y0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %Z1);
OUT_AS2 (cse, w, %Z0);
OUT_AS1 (2:,);
if (code == EQ)
OUT_AS1 (dec, mulh);
else
OUT_AS1 (inc, mulh);
OUT_AS2 (mov, w, mulh);
OUT_AS2 (mov, %2, w);
return "";
default:
abort ();
}
}
OUT_AS2 (mov, w, #0);
if (code == EQ)
OUT_AS1 (snz,);
else
OUT_AS1 (sz,);
OUT_AS1 (inc, wreg);
OUT_AS2 (mov, %2, w);
}
return "";
#undef operands
}
const char *
ip2k_gen_signed_comp_branch (rtx insn, enum rtx_code code, rtx label)
{
#define operands ip2k_compare_operands
enum machine_mode mode;
int can_use_skip = 0;
rtx ninsn;
operands[2] = label;
mode = GET_MODE (operands[0]);
if ((mode != QImode) && (mode != HImode)
&& (mode != SImode) && (mode != DImode))
mode = GET_MODE (operands[1]);
/* Look for situations where we can just skip the next instruction instead
of skipping and then branching! */
ninsn = next_real_insn (insn);
if (ninsn
&& (recog_memoized (ninsn) >= 0)
&& get_attr_skip (ninsn) == SKIP_YES)
{
rtx skip_tgt = next_nonnote_insn (next_real_insn (insn));
/* The first situation is where the target of the jump is one insn
after the jump insn and the insn being jumped is only one machine
opcode long. */
if (label == skip_tgt)
can_use_skip = 1;
else
{
/* If our skip target is in fact a code label then we ignore the
label and move onto the next useful instruction. Nothing we do
here has any effect on the use of skipping instructions. */
if (GET_CODE (skip_tgt) == CODE_LABEL)
skip_tgt = next_nonnote_insn (skip_tgt);
/* The second situation is where we have something of the form:
test_condition
skip_conditional
page/jump label
optional_label (this may or may not exist):
skippable_insn
page/jump label
In this case we can eliminate the first "page/jump label". */
if (GET_CODE (skip_tgt) == JUMP_INSN)
{
rtx set = single_set (skip_tgt);
if (GET_CODE (XEXP (set, 0)) == PC
&& GET_CODE (XEXP (set, 1)) == LABEL_REF
&& label == JUMP_LABEL (skip_tgt))
can_use_skip = 2;
}
}
}
/* gcc is a little braindead and does some rather stateful things while
inspecting attributes - we have to put this state back to what it's
supposed to be. */
extract_constrain_insn_cached (insn);
if (ip2k_compare_operands[1] == const0_rtx) /* These are easier. */
{
switch (code)
{
case LT:
if (can_use_skip)
{
OUT_AS2 (sb, %0, 7);
}
else
{
OUT_AS2 (snb, %0, 7);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
case GT:
switch (mode)
{
case DImode:
OUT_AS2 (rl, w, %S0);
OUT_AS2 (mov, w, %S0);
OUT_AS2 (or, w, %T0);
OUT_AS2 (or, w, %U0);
OUT_AS2 (or, w, %V0);
OUT_AS2 (or, w, %W0);
OUT_AS2 (or, w, %X0);
OUT_AS2 (or, w, %Y0);
OUT_AS2 (or, w, %Z0);
OUT_AS1 (snz, );
OUT_AS2 (setb, status, 0);
OUT_AS2 (sb, status, 0);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
case SImode:
OUT_AS2 (rl, w, %A0);
OUT_AS2 (mov, w, %A0);
OUT_AS2 (or, w, %B0);
OUT_AS2 (or, w, %C0);
OUT_AS2 (or, w, %D0);
OUT_AS1 (snz, );
OUT_AS2 (setb, status, 0);
OUT_AS2 (sb, status, 0);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
case HImode:
OUT_AS2 (rl, w, %H0);
OUT_AS2 (mov, w, %H0);
OUT_AS2 (or, w, %L0);
OUT_AS1 (snz, );
OUT_AS2 (setb, status, 0);
OUT_AS2 (sb, status, 0);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
case QImode:
OUT_AS2 (mov, w, %0); /* Will just do "sb w, 7". */
OUT_AS1 (snz, );
OUT_AS2 (setb, wreg, 7);
OUT_AS2 (sb, wreg, 7);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
default:
abort ();
}
break;
case LE:
switch (mode)
{
case DImode:
OUT_AS2 (mov, w, %S0);
OUT_AS2 (or, w, %T0);
OUT_AS2 (or, w, %U0);
OUT_AS2 (or, w, %V0);
OUT_AS2 (or, w, %W0);
OUT_AS2 (or, w, %X0);
OUT_AS2 (or, w, %Y0);
OUT_AS2 (or, w, %Z0); /* Z is correct. */
OUT_AS1 (sz, );
OUT_AS2 (snb, %S0, 7);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
case SImode:
OUT_AS2 (mov, w, %A0);
OUT_AS2 (or, w, %B0);
OUT_AS2 (or, w, %C0);
OUT_AS2 (or, w, %D0); /* Z is correct. */
OUT_AS1 (sz, );
OUT_AS2 (snb, %A0, 7);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
case HImode:
OUT_AS2 (mov, w, %H0);
OUT_AS2 (or, w, %L0);
OUT_AS1 (sz, );
OUT_AS2 (snb, %H0, 7);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
case QImode:
OUT_AS2 (mov, w, %0); /* Will just do "sb w, 7". */
OUT_AS1 (sz, );
OUT_AS2 (snb, wreg, 7);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
default:
abort ();
}
break;
case GE:
if (can_use_skip)
{
OUT_AS2 (snb, %0, 7);
}
else
{
OUT_AS2 (sb, %0, 7);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
default:
abort ();
}
return "";
}
/* signed compares are out of line because we can't get
the hardware to compute the overflow for us. */
switch (mode)
{
case QImode:
OUT_AS1 (push, %1%<);
OUT_AS1 (push, %0%>);
OUT_AS1 (page, __cmpqi2);
OUT_AS1 (call, __cmpqi2);
break;
case HImode:
OUT_AS1 (push, %L1%<);
OUT_AS1 (push, %H1%<);
OUT_AS1 (push, %L0%<);
OUT_AS1 (push, %H0%>%>%>);
OUT_AS1 (page, __cmphi2);
OUT_AS1 (call, __cmphi2);
break;
case SImode:
OUT_AS1 (push, %D1%<);
OUT_AS1 (push, %C1%<);
OUT_AS1 (push, %B1%<);
OUT_AS1 (push, %A1%<);
OUT_AS1 (push, %D0%<);
OUT_AS1 (push, %C0%<);
OUT_AS1 (push, %B0%<);
OUT_AS1 (push, %A0%>%>%>%>%>%>%>);
OUT_AS1 (page, __cmpsi2);
OUT_AS1 (call, __cmpsi2);
break;
case DImode:
if (GET_CODE (operands[0]) == MEM
&& true_regnum (XEXP (operands[0], 0)) == REG_DP)
{
OUT_AS1 (push, %Z1%<);
OUT_AS1 (push, %Y1%<);
OUT_AS1 (push, %X1%<);
OUT_AS1 (push, %W1%<);
OUT_AS1 (push, %V1%<);
OUT_AS1 (push, %U1%<);
OUT_AS1 (push, %T1%<);
OUT_AS1 (push, %S1%>%>%>%>%>%>%>);
OUT_AS1 (page, __cmpdi2_dp);
OUT_AS1 (call, __cmpdi2_dp);
}
else
{
OUT_AS1 (push, %Z1%<);
OUT_AS1 (push, %Y1%<);
OUT_AS1 (push, %X1%<);
OUT_AS1 (push, %W1%<);
OUT_AS1 (push, %V1%<);
OUT_AS1 (push, %U1%<);
OUT_AS1 (push, %T1%<);
OUT_AS1 (push, %S1%<);
OUT_AS1 (push, %Z0%<);
OUT_AS1 (push, %Y0%<);
OUT_AS1 (push, %X0%<);
OUT_AS1 (push, %W0%<);
OUT_AS1 (push, %V0%<);
OUT_AS1 (push, %U0%<);
OUT_AS1 (push, %T0%<);
OUT_AS1 (push, %S0%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>);
OUT_AS1 (page, __cmpdi2);
OUT_AS1 (call, __cmpdi2);
}
break;
default:
abort ();
}
switch (code)
{
case LT:
if (can_use_skip)
{
OUT_AS2 (cse, w, #0);
}
else
{
OUT_AS2 (csne, w, #0);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
case GT:
if (can_use_skip)
{
OUT_AS2 (cse, w, #2);
}
else
{
OUT_AS2 (csne, w, #2);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
case LE:
if (can_use_skip)
{
OUT_AS2 (snb, wreg, 1);
}
else
{
OUT_AS2 (sb, wreg, 1);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
case GE:
if (can_use_skip)
{
OUT_AS2 (csne, w, #0);
}
else
{
OUT_AS2 (cse, w, #0);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
default:
abort ();
}
return "";
#undef operands
}
const char *
ip2k_gen_unsigned_comp_branch (rtx insn, enum rtx_code code, rtx label)
{
#define operands ip2k_compare_operands
enum machine_mode mode;
int imm_sub = 0;
int imm_cmp = 0;
int can_use_skip = 0;
rtx ninsn;
HOST_WIDE_INT const_low;
HOST_WIDE_INT const_high;
operands[2] = label;
mode = GET_MODE (operands[0]);
if ((mode != QImode) && (mode != HImode) && (mode != SImode)
&& (mode != DImode))
{
mode = GET_MODE (operands[1]);
}
/* Look for situations where we can just skip the next instruction instead
of skipping and then branching! */
ninsn = next_real_insn (insn);
if (ninsn
&& (recog_memoized (ninsn) >= 0)
&& get_attr_skip (ninsn) == SKIP_YES)
{
rtx skip_tgt = next_nonnote_insn (next_real_insn (insn));
/* The first situation is where the target of the jump is one insn
after the jump insn and the insn being jumped is only one machine
opcode long. */
if (label == skip_tgt)
can_use_skip = 1;
else
{
/* If our skip target is in fact a code label then we ignore the
label and move onto the next useful instruction. Nothing we do
here has any effect on the use of skipping instructions. */
if (GET_CODE (skip_tgt) == CODE_LABEL)
skip_tgt = next_nonnote_insn (skip_tgt);
/* The second situation is where we have something of the form:
test_condition
skip_conditional
page/jump label
optional_label (this may or may not exist):
skippable_insn
page/jump label
In this case we can eliminate the first "page/jump label". */
if (GET_CODE (skip_tgt) == JUMP_INSN)
{
rtx set = single_set (skip_tgt);
if (GET_CODE (XEXP (set, 0)) == PC
&& GET_CODE (XEXP (set, 1)) == LABEL_REF
&& label == JUMP_LABEL (skip_tgt))
can_use_skip = 2;
}
}
}
/* gcc is a little braindead and does some rather stateful things while
inspecting attributes - we have to put this state back to what it's
supposed to be. */
extract_constrain_insn_cached (insn);
if (ip2k_compare_operands[1] == const0_rtx)
{
switch (code)
{
case LEU:
code = EQ; /* Nothing is LTU 0. */
goto zero;
case GTU:
code = NE; /* Anything nonzero is GTU. */
/* fall-through */
case EQ:
case NE: /* Test all the bits, result in
Z AND WREG. */
zero:
switch (mode)
{
case DImode:
OUT_AS2 (mov, w, %S0);
OUT_AS2 (or, w, %T0);
OUT_AS2 (or, w, %U0);
OUT_AS2 (or, w, %V0);
OUT_AS2 (or, w, %W0);
OUT_AS2 (or, w, %X0);
OUT_AS2 (or, w, %Y0);
OUT_AS2 (or, w, %Z0);
break;
case SImode:
OUT_AS2 (mov, w, %A0);
OUT_AS2 (or, w, %B0);
OUT_AS2 (or, w, %C0);
OUT_AS2 (or, w, %D0);
break;
case HImode:
OUT_AS2 (mov, w, %H0);
OUT_AS2 (or, w, %L0);
break;
case QImode:
OUT_AS2 (mov, w, %0);
break;
default:
abort ();
}
if (can_use_skip)
{
if (code == EQ)
OUT_AS1 (sz, );
else
OUT_AS1 (snz, );
}
else
{
if (code == EQ)
OUT_AS1 (snz,);
else
OUT_AS1 (sz,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
case GEU:
/* Always succeed. */
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
case LTU:
/* Always fail. */
break;
default:
abort ();
}
return "";
}
/* Look at whether we have a constant as one of our operands. If we do
and it's in the position that we use to subtract from during our
normal optimized comparison concept then we have to shuffle things
around! */
if (mode != QImode)
{
if ((immediate_operand (operands[1], GET_MODE (operands[1]))
&& ((code == LEU) || (code == GTU)))
|| (immediate_operand (operands[0], GET_MODE (operands[0]))
&& ((code == LTU) || (code == GEU))))
{
imm_sub = 1;
}
}
/* Same as above - look if we have a constant that we can compare
for equality or non-equality. If we know this then we can look
for common value eliminations. Note that we want to ensure that
any immediate value is operand 1 to simplify the code later! */
if ((code == EQ) || (code == NE))
{
imm_cmp = immediate_operand (operands[1], GET_MODE (operands[1]));
if (! imm_cmp)
{
imm_cmp = immediate_operand (operands[0], GET_MODE (operands[0]));
if (imm_cmp)
{
rtx tmp = operands[1];
operands[1] = operands[0];
operands[0] = tmp;
}
}
}
switch (mode)
{
case QImode:
switch (code)
{
case EQ:
if (imm_cmp && ((INTVAL (operands[1]) & 0xff) == 0xff))
OUT_AS2 (incsnz, w, %0);
else if (imm_cmp && ((INTVAL (operands[1]) & 0xff) == 0x01))
OUT_AS2 (decsnz, w, %0);
else
{
OUT_AS2 (mov, w, %1);
OUT_AS2 (csne, w, %0);
}
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
case NE:
if (imm_cmp && ((INTVAL (operands[1]) & 0xff) == 0xff))
OUT_AS2 (incsz, w, %0);
else if (imm_cmp && ((INTVAL (operands[1]) & 0xff) == 0x01))
OUT_AS2 (decsz, w, %0);
else
{
OUT_AS2 (mov, w, %1);
OUT_AS2 (cse, w, %0);
}
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
case GTU:
OUT_AS2 (mov, w, %0);
OUT_AS2 (cmp, w, %1);
OUT_AS1 (sc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
case GEU:
OUT_AS2 (mov, w, %1);
OUT_AS2 (cmp, w, %0);
OUT_AS1 (snc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
case LTU:
OUT_AS2 (mov, w, %1);
OUT_AS2 (cmp, w, %0);
OUT_AS1 (sc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
case LEU:
OUT_AS2 (mov, w, %0);
OUT_AS2 (cmp, w, %1);
OUT_AS1 (snc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
default:
abort ();
}
break;
case HImode:
switch (code)
{
case EQ:
{
unsigned char h = 0, l = 1;
if (imm_cmp)
{
h = (INTVAL (operands[1]) >> 8) & 0xff;
l = INTVAL (operands[1]) & 0xff;
if ((h == 0xff) && (l == 0xff))
{
/* We should be able to do the following, but the
IP2k simulator doesn't like it and we get a load
of failures in gcc-c-torture. */
OUT_AS2 (incsnz, w, %L0);
OUT_AS2 (incsz, w, %H0);
/* OUT_AS1 (skip,); Should have this */
OUT_AS1 (page, 1f);/* Shouldn't need this! */
OUT_AS1 (jmp, 1f); /* Shouldn't need this either. */
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
OUT_AS1 (1:,);
break;
}
else if (h == 0)
{
if (l == 1)
OUT_AS2 (dec, w, %L0);
else
{
OUT_AS2 (mov, w, %L0);
OUT_AS2 (sub, w, %L1);
}
OUT_AS2 (or, w, %H0);
OUT_AS1 (snz,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
}
else if (l == 0)
{
if (h == 1)
OUT_AS2 (dec, w, %H0);
else
{
OUT_AS2 (mov, w, %H0);
OUT_AS2 (sub, w, %H1);
}
OUT_AS2 (or, w, %L0);
OUT_AS1 (snz,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
}
}
OUT_AS2 (mov, w, %H1);
OUT_AS2 (cse, w, %H0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
if (! imm_cmp || (h != l))
OUT_AS2 (mov, w, %L1);
OUT_AS2 (csne, w, %L0);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
OUT_AS1 (2:,);
}
break;
case NE:
{
unsigned char h = 0, l = 1;
if (imm_cmp)
{
h = (INTVAL (operands[1]) >> 8) & 0xff;
l = INTVAL (operands[1]) & 0xff;
if ((h == 0xff) && (l == 0xff))
{
OUT_AS2 (incsnz, w, %L0);
OUT_AS2 (incsz, w, %H0);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
}
else if (h == 0)
{
if (l == 1)
OUT_AS2 (dec, w, %L0);
else
{
OUT_AS2 (mov, w, %L0);
OUT_AS2 (sub, w, %L1);
}
OUT_AS2 (or, w, %H0);
OUT_AS1 (sz,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
}
else if (l == 0)
{
if (h == 1)
OUT_AS2 (dec, w, %H0);
else
{
OUT_AS2 (mov, w, %H0);
OUT_AS2 (sub, w, %H1);
}
OUT_AS2 (or, w, %L0);
OUT_AS1 (sz,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
break;
}
}
OUT_AS2 (mov, w, %H1);
if (imm_cmp && (h == l))
{
OUT_AS2 (csne, w, %H0);
OUT_AS2 (cse, w, %L0);
}
else
{
OUT_AS2 (cse, w, %H0);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
OUT_AS2 (mov, w, %L1);
OUT_AS2 (cse, w, %L0);
}
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
case GTU:
if (imm_sub)
{
/* > 0xffff never succeeds! */
if ((INTVAL (operands[1]) & 0xffff) != 0xffff)
{
operands[3] = GEN_INT (INTVAL (operands[1]) + 1);
OUT_AS2 (mov, w, %L3);
OUT_AS2 (sub, w, %L0);
OUT_AS2 (mov, w, %H3);
OUT_AS2 (subc, w, %H0);
OUT_AS1 (snc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
}
else
{
OUT_AS2 (mov, w, %L0);
OUT_AS2 (sub, w, %L1);
OUT_AS2 (mov, w, %H0);
OUT_AS2 (subc, w, %H1);
OUT_AS1 (sc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
case GEU:
if (imm_sub)
{
if (INTVAL (operands[0]) == 0)
{
OUT_AS2 (mov, w, %H1);
OUT_AS2 (or, w, %L1);
OUT_AS1 (snz,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
else
{
operands[3] = GEN_INT (INTVAL (operands[0]) - 1);
OUT_AS2 (mov, w, %L3);
OUT_AS2 (sub, w, %L1);
OUT_AS2 (mov, w, %H3);
OUT_AS2 (subc, w, %H1);
OUT_AS1 (sc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
}
else
{
OUT_AS2 (mov, w, %L1);
OUT_AS2 (sub, w, %L0);
OUT_AS2 (mov, w, %H1);
OUT_AS2 (subc, w, %H0);
OUT_AS1 (snc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
case LTU:
if (imm_sub)
{
if (INTVAL (operands[0]) == 0)
{
OUT_AS2 (mov, w, %H1);
OUT_AS2 (or, w, %L1);
OUT_AS1 (sz,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
else
{
operands[3] = GEN_INT (INTVAL (operands[0]) - 1);
OUT_AS2 (mov, w, %L3);
OUT_AS2 (sub, w, %L1);
OUT_AS2 (mov, w, %H3);
OUT_AS2 (subc, w, %H1);
OUT_AS1 (snc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
}
else
{
OUT_AS2 (mov, w, %L1);
OUT_AS2 (sub, w, %L0);
OUT_AS2 (mov, w, %H1);
OUT_AS2 (subc, w, %H0);
OUT_AS1 (sc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
case LEU:
if (imm_sub)
{
if ((INTVAL (operands[1]) & 0xffff) == 0xffff)
{
/* <= 0xffff always succeeds. */
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
else
{
operands[3] = GEN_INT (INTVAL (operands[1]) + 1);
OUT_AS2 (mov, w, %L3);
OUT_AS2 (sub, w, %L0);
OUT_AS2 (mov, w, %H3);
OUT_AS2 (subc, w, %H0);
OUT_AS1 (sc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
}
else
{
OUT_AS2 (mov, w, %L0);
OUT_AS2 (sub, w, %L1);
OUT_AS2 (mov, w, %H0);
OUT_AS2 (subc, w, %H1);
OUT_AS1 (snc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
default:
abort ();
}
break;
case SImode:
switch (code)
{
case EQ:
{
unsigned char a = 0, b = 1, c = 2, d = 3;
if (imm_cmp)
{
a = (INTVAL (operands[1]) >> 24) & 0xff;
b = (INTVAL (operands[1]) >> 16) & 0xff;
c = (INTVAL (operands[1]) >> 8) & 0xff;
d = INTVAL (operands[1]) & 0xff;
}
OUT_AS2 (mov, w, %A1);
if (imm_cmp && (b == a))
{
OUT_AS2 (csne, w, %A0);
OUT_AS2 (cse, w, %B0);
}
else
{
OUT_AS2 (cse, w, %A0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %B1);
OUT_AS2 (cse, w, %B0);
}
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
if (! imm_cmp || (c != b))
OUT_AS2 (mov, w, %C1);
OUT_AS2 (cse, w, %C0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
if (! imm_cmp || (d != c))
OUT_AS2 (mov, w, %D1);
OUT_AS2 (csne, w, %D0);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
OUT_AS1 (2:,);
}
break;
case NE:
{
unsigned char a = 0, b = 1, c = 2, d = 3;
if (imm_cmp)
{
a = (INTVAL (operands[1]) >> 24) & 0xff;
b = (INTVAL (operands[1]) >> 16) & 0xff;
c = (INTVAL (operands[1]) >> 8) & 0xff;
d = INTVAL (operands[1]) & 0xff;
}
OUT_AS2 (mov, w, %A1);
if (imm_cmp && (b == a))
{
OUT_AS2 (csne, w, %A0);
OUT_AS2 (cse, w, %B0);
}
else
{
OUT_AS2 (cse, w, %A0);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
OUT_AS2 (mov, w, %B1);
OUT_AS2 (cse, w, %B0);
}
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
if (! imm_cmp || (c != b))
OUT_AS2 (mov, w, %C1);
if (imm_cmp && (d == c))
{
OUT_AS2 (csne, w, %C0);
OUT_AS2 (cse, w, %D0);
}
else
{
OUT_AS2 (cse, w, %C0);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
OUT_AS2 (mov, w, %D1);
OUT_AS2 (cse, w, %D0);
}
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
case GTU:
if (imm_sub)
{
/* > 0xffffffff never succeeds! */
if ((unsigned HOST_WIDE_INT)(INTVAL (operands[1]) & 0xffffffff)
!= 0xffffffff)
{
operands[3] = GEN_INT (INTVAL (operands[1]) + 1);
OUT_AS2 (mov, w, %D3);
OUT_AS2 (sub, w, %D0);
OUT_AS2 (mov, w, %C3);
OUT_AS2 (subc, w, %C0);
OUT_AS2 (mov, w, %B3);
OUT_AS2 (subc, w, %B0);
OUT_AS2 (mov, w, %A3);
OUT_AS2 (subc, w, %A0);
OUT_AS1 (snc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
}
else
{
OUT_AS2 (mov, w, %D0);
OUT_AS2 (sub, w, %D1);
OUT_AS2 (mov, w, %C0);
OUT_AS2 (subc, w, %C1);
OUT_AS2 (mov, w, %B0);
OUT_AS2 (subc, w, %B1);
OUT_AS2 (mov, w, %A0);
OUT_AS2 (subc, w, %A1);
OUT_AS1 (sc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
case GEU:
if (imm_sub)
{
if (INTVAL (operands[0]) == 0)
{
OUT_AS2 (mov, w, %A1);
OUT_AS2 (or, w, %B1);
OUT_AS2 (or, w, %C1);
OUT_AS2 (or, w, %D1);
OUT_AS1 (snz,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
else
{
operands[3] = GEN_INT (INTVAL (operands[0]) - 1);
OUT_AS2 (mov, w, %D3);
OUT_AS2 (sub, w, %D1);
OUT_AS2 (mov, w, %C3);
OUT_AS2 (subc, w, %C1);
OUT_AS2 (mov, w, %B3);
OUT_AS2 (subc, w, %B1);
OUT_AS2 (mov, w, %A3);
OUT_AS2 (subc, w, %A1);
OUT_AS1 (sc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
}
else
{
OUT_AS2 (mov, w, %D1);
OUT_AS2 (sub, w, %D0);
OUT_AS2 (mov, w, %C1);
OUT_AS2 (subc, w, %C0);
OUT_AS2 (mov, w, %B1);
OUT_AS2 (subc, w, %B0);
OUT_AS2 (mov, w, %A1);
OUT_AS2 (subc, w, %A0);
OUT_AS1 (snc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
case LTU:
if (imm_sub)
{
if (INTVAL (operands[0]) == 0)
{
OUT_AS2 (mov, w, %A1);
OUT_AS2 (or, w, %B1);
OUT_AS2 (or, w, %C1);
OUT_AS2 (or, w, %D1);
OUT_AS1 (sz,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
else
{
operands[3] = GEN_INT (INTVAL (operands[0]) - 1);
OUT_AS2 (mov, w, %D3);
OUT_AS2 (sub, w, %D1);
OUT_AS2 (mov, w, %C3);
OUT_AS2 (subc, w, %C1);
OUT_AS2 (mov, w, %B3);
OUT_AS2 (subc, w, %B1);
OUT_AS2 (mov, w, %A3);
OUT_AS2 (subc, w, %A1);
OUT_AS1 (snc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
}
else
{
OUT_AS2 (mov, w, %D1);
OUT_AS2 (sub, w, %D0);
OUT_AS2 (mov, w, %C1);
OUT_AS2 (subc, w, %C0);
OUT_AS2 (mov, w, %B1);
OUT_AS2 (subc, w, %B0);
OUT_AS2 (mov, w, %A1);
OUT_AS2 (subc, w, %A0);
OUT_AS1 (sc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
case LEU:
if (imm_sub)
{
if ((unsigned HOST_WIDE_INT)(INTVAL (operands[1]) & 0xffffffff)
== 0xffffffff)
{
/* <= 0xffffffff always succeeds. */
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
else
{
operands[3] = GEN_INT (INTVAL (operands[1]) + 1);
OUT_AS2 (mov, w, %D3);
OUT_AS2 (sub, w, %D0);
OUT_AS2 (mov, w, %C3);
OUT_AS2 (subc, w, %C0);
OUT_AS2 (mov, w, %B3);
OUT_AS2 (subc, w, %B0);
OUT_AS2 (mov, w, %A3);
OUT_AS2 (subc, w, %A0);
OUT_AS1 (sc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
}
else
{
OUT_AS2 (mov, w, %D0);
OUT_AS2 (sub, w, %D1);
OUT_AS2 (mov, w, %C0);
OUT_AS2 (subc, w, %C1);
OUT_AS2 (mov, w, %B0);
OUT_AS2 (subc, w, %B1);
OUT_AS2 (mov, w, %A0);
OUT_AS2 (subc, w, %A1);
OUT_AS1 (snc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
default:
abort ();
}
break;
case DImode:
if (GET_CODE (operands[1]) == CONST_INT)
{
const_low = INTVAL (operands[1]);
const_high = (const_low >= 0) - 1;
}
else if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
const_low = CONST_DOUBLE_LOW (operands[1]);
const_high = CONST_DOUBLE_HIGH (operands[1]);
}
switch (code)
{
case EQ:
{
unsigned char s = 0, t = 1, u = 2, v = 3;
unsigned char w = 4, x = 5, y = 6, z = 7;
if (optimize_size)
{
if (GET_CODE (operands[0]) == MEM
&& true_regnum (XEXP (operands[0], 0)) == REG_DP)
{
OUT_AS1 (push, %Z1%<);
OUT_AS1 (push, %Y1%<);
OUT_AS1 (push, %X1%<);
OUT_AS1 (push, %W1%<);
OUT_AS1 (push, %V1%<);
OUT_AS1 (push, %U1%<);
OUT_AS1 (push, %T1%<);
OUT_AS1 (push, %S1%>%>%>%>%>%>%>);
OUT_AS1 (page, __cmpdi2_dp);
OUT_AS1 (call, __cmpdi2_dp);
OUT_AS2 (csne, w, #1);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
else
{
OUT_AS1 (push, %Z1%<);
OUT_AS1 (push, %Y1%<);
OUT_AS1 (push, %X1%<);
OUT_AS1 (push, %W1%<);
OUT_AS1 (push, %V1%<);
OUT_AS1 (push, %U1%<);
OUT_AS1 (push, %T1%<);
OUT_AS1 (push, %S1%<);
OUT_AS1 (push, %Z0%<);
OUT_AS1 (push, %Y0%<);
OUT_AS1 (push, %X0%<);
OUT_AS1 (push, %W0%<);
OUT_AS1 (push, %V0%<);
OUT_AS1 (push, %U0%<);
OUT_AS1 (push, %T0%<);
OUT_AS1 (push, %S0%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>);
OUT_AS1 (page, __cmpdi2);
OUT_AS1 (call, __cmpdi2);
OUT_AS2 (csne, w, #1);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
}
else
{
if (imm_cmp)
{
s = (const_high >> 24) & 0xff;
t = (const_high >> 16) & 0xff;
u = (const_high >> 8) & 0xff;
v = const_high & 0xff;
w = (const_low >> 24) & 0xff;
x = (const_low >> 16) & 0xff;
y = (const_low >> 8) & 0xff;
z = const_low & 0xff;
}
OUT_AS2 (mov, w, %S1);
if (imm_cmp && (s == t))
{
OUT_AS2 (csne, w, %S0);
OUT_AS2 (cse, w, %T0);
}
else
{
OUT_AS2 (cse, w, %S0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %T1);
OUT_AS2 (cse, w, %T0);
}
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %U1);
if (imm_cmp && (u == v))
{
OUT_AS2 (csne, w, %U0);
OUT_AS2 (cse, w, %V0);
}
else
{
OUT_AS2 (cse, w, %U0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %V1);
OUT_AS2 (cse, w, %V0);
}
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %W1);
if (imm_cmp && (w == x))
{
OUT_AS2 (csne, w, %W0);
OUT_AS2 (cse, w, %X0);
}
else
{
OUT_AS2 (cse, w, %W0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
OUT_AS2 (mov, w, %X1);
OUT_AS2 (cse, w, %X0);
}
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
if (! imm_cmp || (x != y))
OUT_AS2 (mov, w, %Y1);
OUT_AS2 (cse, w, %Y0);
OUT_AS1 (page, 2f);
OUT_AS1 (jmp, 2f);
if (! imm_cmp || (z != y))
OUT_AS2 (mov, w, %Z1);
OUT_AS2 (csne, w, %Z0);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
OUT_AS1 (2:,);
}
}
break;
case NE:
{
unsigned char s = 0, t = 1, u = 2, v = 3;
unsigned char w = 4, x = 5, y = 6, z = 7;
if (optimize_size)
{
if (GET_CODE (operands[0]) == MEM
&& true_regnum (XEXP (operands[0], 0)) == REG_DP)
{
OUT_AS1 (push, %Z1%<);
OUT_AS1 (push, %Y1%<);
OUT_AS1 (push, %X1%<);
OUT_AS1 (push, %W1%<);
OUT_AS1 (push, %V1%<);
OUT_AS1 (push, %U1%<);
OUT_AS1 (push, %T1%<);
OUT_AS1 (push, %S1%>%>%>%>%>%>%>);
OUT_AS1 (page, __cmpdi2_dp);
OUT_AS1 (call, __cmpdi2_dp);
OUT_AS2 (cse, w, #1);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
else
{
OUT_AS1 (push, %Z1%<);
OUT_AS1 (push, %Y1%<);
OUT_AS1 (push, %X1%<);
OUT_AS1 (push, %W1%<);
OUT_AS1 (push, %V1%<);
OUT_AS1 (push, %U1%<);
OUT_AS1 (push, %T1%<);
OUT_AS1 (push, %S1%<);
OUT_AS1 (push, %Z0%<);
OUT_AS1 (push, %Y0%<);
OUT_AS1 (push, %X0%<);
OUT_AS1 (push, %W0%<);
OUT_AS1 (push, %V0%<);
OUT_AS1 (push, %U0%<);
OUT_AS1 (push, %T0%<);
OUT_AS1 (push, %S0%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>);
OUT_AS1 (page, __cmpdi2);
OUT_AS1 (call, __cmpdi2);
OUT_AS2 (cse, w, #1);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
}
else
{
if (imm_cmp)
{
s = (const_high >> 24) & 0xff;
t = (const_high >> 16) & 0xff;
u = (const_high >> 8) & 0xff;
v = const_high & 0xff;
w = (const_low >> 24) & 0xff;
x = (const_low >> 16) & 0xff;
y = (const_low >> 8) & 0xff;
z = const_low & 0xff;
}
OUT_AS2 (mov, w, %S1);
if (imm_cmp && (s == t))
{
OUT_AS2 (csne, w, %S0);
OUT_AS2 (cse, w, %T0);
}
else
{
OUT_AS2 (cse, w, %S0);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
OUT_AS2 (mov, w, %T1);
OUT_AS2 (cse, w, %T0);
}
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
OUT_AS2 (mov, w, %U1);
if (imm_cmp && (u == v))
{
OUT_AS2 (csne, w, %U0);
OUT_AS2 (cse, w, %V0);
}
else
{
OUT_AS2 (cse, w, %U0);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
OUT_AS2 (mov, w, %V1);
OUT_AS2 (cse, w, %V0);
}
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
OUT_AS2 (mov, w, %W1);
if (imm_cmp && (w == x))
{
OUT_AS2 (csne, w, %W0);
OUT_AS2 (cse, w, %X0);
}
else
{
OUT_AS2 (cse, w, %W0);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
OUT_AS2 (mov, w, %X1);
OUT_AS2 (cse, w, %X0);
}
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
if (! imm_cmp || (y != x))
OUT_AS2 (mov, w, %Y1);
if (imm_cmp && (z == y))
{
OUT_AS2 (csne, w, %Y0);
OUT_AS2 (cse, w, %Z0);
}
else
{
OUT_AS2 (cse, w, %Y0);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
OUT_AS2 (mov, w, %Z1);
OUT_AS2 (cse, w, %Z0);
}
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
}
break;
case GTU:
if (imm_sub)
{
/* > 0xffffffffffffffff never suceeds! */
if (((const_high & 0xffffffff) != 0xffffffff)
|| ((const_low & 0xffffffff) != 0xffffffff))
{
operands[3] = GEN_INT (const_low + 1);
operands[4] = GEN_INT (const_high
+ (INTVAL (operands[3]) ? 0 : 1));
OUT_AS2 (mov, w, %D3);
OUT_AS2 (sub, w, %Z0);
OUT_AS2 (mov, w, %C3);
OUT_AS2 (subc, w, %Y0);
OUT_AS2 (mov, w, %B3);
OUT_AS2 (subc, w, %X0);
OUT_AS2 (mov, w, %A3);
OUT_AS2 (subc, w, %W0);
OUT_AS2 (mov, w, %D4);
OUT_AS2 (subc, w, %V0);
OUT_AS2 (mov, w, %C4);
OUT_AS2 (subc, w, %U0);
OUT_AS2 (mov, w, %B4);
OUT_AS2 (subc, w, %T0);
OUT_AS2 (mov, w, %A4);
OUT_AS2 (subc, w, %S0);
OUT_AS1 (snc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
}
else
{
OUT_AS2 (mov, w, %Z0);
OUT_AS2 (sub, w, %Z1);
OUT_AS2 (mov, w, %Y0);
OUT_AS2 (subc, w, %Y1);
OUT_AS2 (mov, w, %X0);
OUT_AS2 (subc, w, %X1);
OUT_AS2 (mov, w, %W0);
OUT_AS2 (subc, w, %W1);
OUT_AS2 (mov, w, %V0);
OUT_AS2 (subc, w, %V1);
OUT_AS2 (mov, w, %U0);
OUT_AS2 (subc, w, %U1);
OUT_AS2 (mov, w, %T0);
OUT_AS2 (subc, w, %T1);
OUT_AS2 (mov, w, %S0);
OUT_AS2 (subc, w, %S1);
OUT_AS1 (sc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
case GEU:
if (imm_sub)
{
HOST_WIDE_INT const_low0;
HOST_WIDE_INT const_high0;
if (GET_CODE (operands[0]) == CONST_INT)
{
const_low0 = INTVAL (operands[0]);
const_high0 = (const_low >= 0) - 1;
}
else if (GET_CODE (operands[0]) == CONST_DOUBLE)
{
const_low0 = CONST_DOUBLE_LOW (operands[0]);
const_high0 = CONST_DOUBLE_HIGH (operands[0]);
}
if (const_high0 == 0 && const_low0 == 0)
{
OUT_AS2 (mov, w, %S1);
OUT_AS2 (or, w, %T1);
OUT_AS2 (or, w, %U1);
OUT_AS2 (or, w, %V1);
OUT_AS2 (or, w, %W1);
OUT_AS2 (or, w, %X1);
OUT_AS2 (or, w, %Y1);
OUT_AS2 (or, w, %Z1);
OUT_AS1 (snz,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
else
{
operands[3] = GEN_INT (const_low0 - 1);
operands[4] = GEN_INT (const_high0 - (const_low0 ? 1 : 0));
OUT_AS2 (mov, w, %D3);
OUT_AS2 (sub, w, %Z1);
OUT_AS2 (mov, w, %C3);
OUT_AS2 (subc, w, %Y1);
OUT_AS2 (mov, w, %B3);
OUT_AS2 (subc, w, %X1);
OUT_AS2 (mov, w, %A3);
OUT_AS2 (subc, w, %W1);
OUT_AS2 (mov, w, %D4);
OUT_AS2 (subc, w, %V1);
OUT_AS2 (mov, w, %C4);
OUT_AS2 (subc, w, %U1);
OUT_AS2 (mov, w, %B4);
OUT_AS2 (subc, w, %T1);
OUT_AS2 (mov, w, %A4);
OUT_AS2 (subc, w, %S1);
OUT_AS1 (sc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
}
else
{
OUT_AS2 (mov, w, %Z1);
OUT_AS2 (sub, w, %Z0);
OUT_AS2 (mov, w, %Y1);
OUT_AS2 (subc, w, %Y0);
OUT_AS2 (mov, w, %X1);
OUT_AS2 (subc, w, %X0);
OUT_AS2 (mov, w, %W1);
OUT_AS2 (subc, w, %W0);
OUT_AS2 (mov, w, %V1);
OUT_AS2 (subc, w, %V0);
OUT_AS2 (mov, w, %U1);
OUT_AS2 (subc, w, %U0);
OUT_AS2 (mov, w, %T1);
OUT_AS2 (subc, w, %T0);
OUT_AS2 (mov, w, %S1);
OUT_AS2 (subc, w, %S0);
OUT_AS1 (snc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
case LTU:
if (imm_sub)
{
HOST_WIDE_INT const_low0;
HOST_WIDE_INT const_high0;
if (GET_CODE (operands[0]) == CONST_INT)
{
const_low0 = INTVAL (operands[0]);
const_high0 = (const_low >= 0) - 1;
}
else if (GET_CODE (operands[0]) == CONST_DOUBLE)
{
const_low0 = CONST_DOUBLE_LOW (operands[0]);
const_high0 = CONST_DOUBLE_HIGH (operands[0]);
}
if (const_high0 == 0 && const_low0 == 0)
{
OUT_AS2 (mov, w, %S1);
OUT_AS2 (or, w, %T1);
OUT_AS2 (or, w, %U1);
OUT_AS2 (or, w, %V1);
OUT_AS2 (or, w, %W1);
OUT_AS2 (or, w, %X1);
OUT_AS2 (or, w, %Y1);
OUT_AS2 (or, w, %Z1);
OUT_AS1 (sz,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
else
{
operands[3] = GEN_INT (const_low0 - 1);
operands[4] = GEN_INT (const_high0 - (const_low0 ? 1 : 0));
OUT_AS2 (mov, w, %D3);
OUT_AS2 (sub, w, %Z1);
OUT_AS2 (mov, w, %C3);
OUT_AS2 (subc, w, %Y1);
OUT_AS2 (mov, w, %B3);
OUT_AS2 (subc, w, %X1);
OUT_AS2 (mov, w, %A3);
OUT_AS2 (subc, w, %W1);
OUT_AS2 (mov, w, %D4);
OUT_AS2 (subc, w, %V1);
OUT_AS2 (mov, w, %C4);
OUT_AS2 (subc, w, %U1);
OUT_AS2 (mov, w, %B4);
OUT_AS2 (subc, w, %T1);
OUT_AS2 (mov, w, %A4);
OUT_AS2 (subc, w, %S1);
OUT_AS1 (snc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
}
else
{
OUT_AS2 (mov, w, %Z1);
OUT_AS2 (sub, w, %Z0);
OUT_AS2 (mov, w, %Y1);
OUT_AS2 (subc, w, %Y0);
OUT_AS2 (mov, w, %X1);
OUT_AS2 (subc, w, %X0);
OUT_AS2 (mov, w, %W1);
OUT_AS2 (subc, w, %W0);
OUT_AS2 (mov, w, %V1);
OUT_AS2 (subc, w, %V0);
OUT_AS2 (mov, w, %U1);
OUT_AS2 (subc, w, %U0);
OUT_AS2 (mov, w, %T1);
OUT_AS2 (subc, w, %T0);
OUT_AS2 (mov, w, %S1);
OUT_AS2 (subc, w, %S0);
OUT_AS1 (sc,);
OUT_AS1 (page, %2);
OUT_AS1 (jmp, %2);
}
break;
case LEU:
if (imm_sub)
{
if (((const_high & 0xffffffff) == 0xffffffff)
&& ((const_low & 0xffffffff) == 0xffffffff))
{
/* <= 0xffffffffffffffff always suceeds. */