-/* Subroutines for insn-output.c for Intel X86.
+/* Subroutines used for code generation on IA-32.
Copyright (C) 1988, 92, 94-98, 1999 Free Software Foundation, Inc.
This file is part of GNU CC.
#include "recog.h"
#include "expr.h"
#include "toplev.h"
+#include "basic-block.h"
#ifdef EXTRA_CONSTRAINT
/* If EXTRA_CONSTRAINT is defined, then the 'S'
#define CHECK_STACK_LIMIT -1
#endif
-/* Type of an operand for ix86_{binary,unary}_operator_ok */
-enum reg_mem
-{
- reg_p,
- mem_p,
- imm_p
-};
-
/* Processor costs (relative to an add) */
struct processor_costs i386_cost = { /* 386 specific costs */
1, /* cost of an add instruction */
2, /* constant shift costs */
6, /* cost of starting a multiply */
1, /* cost of multiply per each bit set */
- 23 /* cost of a divide/mod */
+ 23, /* cost of a divide/mod */
+ 15 /* "large" insn */
};
struct processor_costs i486_cost = { /* 486 specific costs */
2, /* constant shift costs */
12, /* cost of starting a multiply */
1, /* cost of multiply per each bit set */
- 40 /* cost of a divide/mod */
+ 40, /* cost of a divide/mod */
+ 15 /* "large" insn */
};
struct processor_costs pentium_cost = {
1, /* constant shift costs */
11, /* cost of starting a multiply */
0, /* cost of multiply per each bit set */
- 25 /* cost of a divide/mod */
+ 25, /* cost of a divide/mod */
+ 8 /* "large" insn */
};
struct processor_costs pentiumpro_cost = {
1, /* cost of an add instruction */
1, /* cost of a lea instruction */
- 3, /* variable shift costs */
+ 1, /* variable shift costs */
1, /* constant shift costs */
- 4, /* cost of starting a multiply */
+ 1, /* cost of starting a multiply */
0, /* cost of multiply per each bit set */
- 17 /* cost of a divide/mod */
+ 17, /* cost of a divide/mod */
+ 8 /* "large" insn */
};
-/* We use decoding time together with execution time.
- To get correct vale add 1 for short decodable, 2 for long decodable
- and 4 for vector decodable instruction to execution time and divide
- by two (because CPU is able to do two insns at a time). */
-
struct processor_costs k6_cost = {
1, /* cost of an add instruction */
- 1, /* cost of a lea instruction */
+ 2, /* cost of a lea instruction */
1, /* variable shift costs */
1, /* constant shift costs */
3, /* cost of starting a multiply */
0, /* cost of multiply per each bit set */
- 20 /* cost of a divide/mod */
+ 18, /* cost of a divide/mod */
+ 8 /* "large" insn */
};
struct processor_costs *ix86_cost = &pentium_cost;
const int x86_use_leave = m_386 | m_K6;
const int x86_push_memory = m_386 | m_K6;
const int x86_zero_extend_with_and = m_486 | m_PENT;
-const int x86_movx = m_386 | m_PPRO | m_K6;
-const int x86_double_with_add = ~(m_386 | m_PENT | m_PPRO);
+const int x86_movx = 0 /* m_386 | m_PPRO | m_K6 */;
+const int x86_double_with_add = ~m_386;
const int x86_use_bit_test = m_386;
-const int x86_unroll_strlen = m_486 | m_PENT | m_PPRO;
+const int x86_unroll_strlen = m_486 | m_PENT;
const int x86_use_q_reg = m_PENT | m_PPRO | m_K6;
const int x86_use_any_reg = m_486;
const int x86_cmove = m_PPRO;
-const int x86_deep_branch = m_PPRO| m_K6;
+const int x86_deep_branch = m_PPRO | m_K6;
+const int x86_use_sahf = m_PPRO | m_K6;
+const int x86_partial_reg_stall = m_PPRO;
+const int x86_use_loop = m_K6;
+const int x86_use_fiop = ~m_PPRO;
+const int x86_use_mov0 = m_K6;
+const int x86_use_cltd = ~(m_PENT | m_K6);
+const int x86_read_modify_write = ~m_PENT;
+const int x86_read_modify = ~(m_PENT | m_PPRO);
+const int x86_split_long_moves = m_PPRO;
#define AT_BP(mode) (gen_rtx_MEM ((mode), frame_pointer_rtx))
-extern FILE *asm_out_file;
-extern char *strcat ();
-
-static void ix86_epilogue PROTO((int));
-static void ix86_prologue PROTO((int));
-
-char *singlemove_string ();
-char *output_move_const_single ();
-char *output_fp_cc0_set ();
-
-char *hi_reg_name[] = HI_REGISTER_NAMES;
-char *qi_reg_name[] = QI_REGISTER_NAMES;
-char *qi_high_reg_name[] = QI_HIGH_REGISTER_NAMES;
+const char * const hi_reg_name[] = HI_REGISTER_NAMES;
+const char * const qi_reg_name[] = QI_REGISTER_NAMES;
+const char * const qi_high_reg_name[] = QI_HIGH_REGISTER_NAMES;
/* Array of the smallest class containing reg number REGNO, indexed by
REGNO. Used by REGNO_REG_CLASS in i386.h. */
-enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] =
+enum reg_class const regclass_map[FIRST_PSEUDO_REGISTER] =
{
/* ax, dx, cx, bx */
AREG, DREG, CREG, BREG,
/* si, di, bp, sp */
- SIREG, DIREG, INDEX_REGS, GENERAL_REGS,
+ SIREG, DIREG, NON_Q_REGS, NON_Q_REGS,
/* FP registers */
FP_TOP_REG, FP_SECOND_REG, FLOAT_REGS, FLOAT_REGS,
FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS,
/* arg pointer */
- INDEX_REGS
+ INDEX_REGS,
+ /* flags, fpsr */
+ NO_REGS, NO_REGS
};
/* Test and compare insns in i386.md store the information needed to
generate branch and scc insns here. */
-struct rtx_def *i386_compare_op0 = NULL_RTX;
-struct rtx_def *i386_compare_op1 = NULL_RTX;
-struct rtx_def *(*i386_compare_gen)(), *(*i386_compare_gen_eq)();
+struct rtx_def *ix86_compare_op0 = NULL_RTX;
+struct rtx_def *ix86_compare_op1 = NULL_RTX;
/* which cpu are we scheduling for */
enum processor_type ix86_cpu;
const char *ix86_arch_string; /* for -march=<xxx> */
/* Register allocation order */
-const char *i386_reg_alloc_order;
+const char *ix86_reg_alloc_order;
static char regs_allocated[FIRST_PSEUDO_REGISTER];
/* # of registers to use to pass arguments. */
-const char *i386_regparm_string;
+const char *ix86_regparm_string;
-/* i386_regparm_string as a number */
-int i386_regparm;
+/* ix86_regparm_string as a number */
+int ix86_regparm;
/* Alignment to use for loops and jumps: */
/* Power of two alignment for loops. */
-const char *i386_align_loops_string;
+const char *ix86_align_loops_string;
/* Power of two alignment for non-loop jumps. */
-const char *i386_align_jumps_string;
+const char *ix86_align_jumps_string;
/* Power of two alignment for stack boundary in bytes. */
-const char *i386_preferred_stack_boundary_string;
+const char *ix86_preferred_stack_boundary_string;
/* Preferred alignment for stack boundary in bits. */
-int i386_preferred_stack_boundary;
+int ix86_preferred_stack_boundary;
/* Values 1-5: see jump.c */
-int i386_branch_cost;
-const char *i386_branch_cost_string;
+int ix86_branch_cost;
+const char *ix86_branch_cost_string;
/* Power of two alignment for functions. */
-int i386_align_funcs;
-const char *i386_align_funcs_string;
+int ix86_align_funcs;
+const char *ix86_align_funcs_string;
/* Power of two alignment for loops. */
-int i386_align_loops;
+int ix86_align_loops;
/* Power of two alignment for non-loop jumps. */
-int i386_align_jumps;
+int ix86_align_jumps;
+\f
+static void output_pic_addr_const PROTO ((FILE *, rtx, int));
+static void put_condition_code PROTO ((enum rtx_code, enum machine_mode,
+ int, int, FILE *));
+static enum rtx_code unsigned_comparison PROTO ((enum rtx_code code));
+static rtx ix86_expand_int_compare PROTO ((enum rtx_code, rtx, rtx));
+static rtx ix86_expand_fp_compare PROTO ((enum rtx_code, rtx, rtx, int));
+static rtx ix86_expand_compare PROTO ((enum rtx_code, int));
+static rtx gen_push PROTO ((rtx));
+static int memory_address_length PROTO ((rtx addr));
+static int ix86_flags_dependant PROTO ((rtx, rtx, enum attr_type));
+static int ix86_agi_dependant PROTO ((rtx, rtx, enum attr_type));
+static int ix86_safe_length PROTO ((rtx));
+static enum attr_memory ix86_safe_memory PROTO ((rtx));
+static enum attr_pent_pair ix86_safe_pent_pair PROTO ((rtx));
+static enum attr_ppro_uops ix86_safe_ppro_uops PROTO ((rtx));
+static void ix86_dump_ppro_packet PROTO ((FILE *));
+static void ix86_reorder_insn PROTO ((rtx *, rtx *));
+static rtx * ix86_pent_find_pair PROTO ((rtx *, rtx *, enum attr_pent_pair,
+ rtx));
+
+struct ix86_address
+{
+ rtx base, index, disp;
+ HOST_WIDE_INT scale;
+};
+static int ix86_decompose_address PARAMS ((rtx, struct ix86_address *));
+\f
/* Sometimes certain combinations of command options do not make
sense on a particular target machine. You can define a macro
`OVERRIDE_OPTIONS' to take account of this. This macro, if
void
override_options ()
{
- int ch, i, j;
- int def_align;
+ /* Comes from final.c -- no real reason to change it. */
+#define MAX_CODE_ALIGN 16
static struct ptt
{
- char *name; /* Canonical processor name. */
- enum processor_type processor; /* Processor type enum value. */
- struct processor_costs *cost; /* Processor costs */
- int target_enable; /* Target flags to enable. */
- int target_disable; /* Target flags to disable. */
- } processor_target_table[] = {
- {PROCESSOR_I386_STRING, PROCESSOR_I386, &i386_cost, 0, 0},
- {PROCESSOR_I486_STRING, PROCESSOR_I486, &i486_cost, 0, 0},
- {PROCESSOR_I586_STRING, PROCESSOR_PENTIUM, &pentium_cost, 0, 0},
- {PROCESSOR_PENTIUM_STRING, PROCESSOR_PENTIUM, &pentium_cost, 0, 0},
- {PROCESSOR_I686_STRING, PROCESSOR_PENTIUMPRO, &pentiumpro_cost, 0, 0},
- {PROCESSOR_PENTIUMPRO_STRING, PROCESSOR_PENTIUMPRO,
- &pentiumpro_cost, 0, 0},
- {PROCESSOR_K6_STRING, PROCESSOR_K6, &k6_cost, 0, 0}
+ struct processor_costs *cost; /* Processor costs */
+ int target_enable; /* Target flags to enable. */
+ int target_disable; /* Target flags to disable. */
+ int align_loop; /* Default alignments. */
+ int align_jump;
+ int align_func;
+ int branch_cost;
+ }
+ const processor_target_table[PROCESSOR_max] =
+ {
+ {&i386_cost, 0, 0, 2, 2, 2, 1},
+ {&i486_cost, 0, 0, 4, 4, 4, 1},
+ {&pentium_cost, 0, 0, -4, -4, -4, 1},
+ {&pentiumpro_cost, 0, 0, 4, -4, 4, 1},
+ {&k6_cost, 0, 0, -5, -5, 4, 1}
+ };
+
+ static struct pta
+ {
+ char *name; /* processor name or nickname. */
+ enum processor_type processor;
+ }
+ const processor_alias_table[] =
+ {
+ {"i386", PROCESSOR_I386},
+ {"i486", PROCESSOR_I486},
+ {"i586", PROCESSOR_PENTIUM},
+ {"pentium", PROCESSOR_PENTIUM},
+ {"i686", PROCESSOR_PENTIUMPRO},
+ {"pentiumpro", PROCESSOR_PENTIUMPRO},
+ {"ppro", PROCESSOR_PENTIUMPRO},
+ {"pentium2", PROCESSOR_PENTIUMPRO},
+ {"p2", PROCESSOR_PENTIUMPRO},
+ {"k6", PROCESSOR_K6},
};
- int ptt_size = sizeof (processor_target_table) / sizeof (struct ptt);
+ int const pta_size = sizeof(processor_alias_table)/sizeof(struct pta);
#ifdef SUBTARGET_OVERRIDE_OPTIONS
SUBTARGET_OVERRIDE_OPTIONS;
#endif
+ ix86_arch = PROCESSOR_PENTIUM;
+ ix86_cpu = (enum processor_type) TARGET_CPU_DEFAULT;
+
+ if (ix86_arch_string != 0)
+ {
+ int i;
+ for (i = 0; i < pta_size; i++)
+ if (! strcmp (ix86_arch_string, processor_alias_table[i].name))
+ {
+ ix86_arch = processor_alias_table[i].processor;
+ /* Default cpu tuning to the architecture. */
+ ix86_cpu = ix86_arch;
+ break;
+ }
+ if (i == pta_size)
+ error ("bad value (%s) for -march= switch", ix86_arch_string);
+ }
+
+ if (ix86_cpu_string != 0)
+ {
+ int i;
+ for (i = 0; i < pta_size; i++)
+ if (! strcmp (ix86_cpu_string, processor_alias_table[i].name))
+ {
+ ix86_cpu = processor_alias_table[i].processor;
+ break;
+ }
+ if (i == pta_size)
+ error ("bad value (%s) for -mcpu= switch", ix86_cpu_string);
+ }
+
+ ix86_cost = processor_target_table[ix86_cpu].cost;
+ target_flags |= processor_target_table[ix86_cpu].target_enable;
+ target_flags &= ~processor_target_table[ix86_cpu].target_disable;
+
/* Validate registers in register allocation order. */
- if (i386_reg_alloc_order)
+ if (ix86_reg_alloc_order)
{
- for (i = 0; (ch = i386_reg_alloc_order[i]) != '\0'; i++)
+ int i, ch;
+ for (i = 0; (ch = ix86_reg_alloc_order[i]) != '\0'; i++)
{
int regno = 0;
}
}
- if (ix86_arch_string == 0)
- {
- ix86_arch_string = PROCESSOR_PENTIUM_STRING;
- if (ix86_cpu_string == 0)
- ix86_cpu_string = PROCESSOR_DEFAULT_STRING;
- }
-
- for (i = 0; i < ptt_size; i++)
- if (! strcmp (ix86_arch_string, processor_target_table[i].name))
- {
- ix86_arch = processor_target_table[i].processor;
- if (ix86_cpu_string == 0)
- ix86_cpu_string = processor_target_table[i].name;
- break;
- }
-
- if (i == ptt_size)
- {
- error ("bad value (%s) for -march= switch", ix86_arch_string);
- ix86_arch_string = PROCESSOR_PENTIUM_STRING;
- ix86_arch = PROCESSOR_DEFAULT;
- }
-
- if (ix86_cpu_string == 0)
- ix86_cpu_string = PROCESSOR_DEFAULT_STRING;
-
- for (j = 0; j < ptt_size; j++)
- if (! strcmp (ix86_cpu_string, processor_target_table[j].name))
- {
- ix86_cpu = processor_target_table[j].processor;
- ix86_cost = processor_target_table[j].cost;
- if (i > j && (int) ix86_arch >= (int) PROCESSOR_K6)
- error ("-mcpu=%s does not support -march=%s",
- ix86_cpu_string, ix86_arch_string);
-
- target_flags |= processor_target_table[j].target_enable;
- target_flags &= ~processor_target_table[j].target_disable;
- break;
- }
-
- if (j == ptt_size)
- {
- error ("bad value (%s) for -mcpu= switch", ix86_cpu_string);
- ix86_cpu_string = PROCESSOR_DEFAULT_STRING;
- ix86_cpu = PROCESSOR_DEFAULT;
- }
-
/* Validate -mregparm= value. */
- if (i386_regparm_string)
+ if (ix86_regparm_string)
{
- i386_regparm = atoi (i386_regparm_string);
- if (i386_regparm < 0 || i386_regparm > REGPARM_MAX)
+ ix86_regparm = atoi (ix86_regparm_string);
+ if (ix86_regparm < 0 || ix86_regparm > REGPARM_MAX)
fatal ("-mregparm=%d is not between 0 and %d",
- i386_regparm, REGPARM_MAX);
+ ix86_regparm, REGPARM_MAX);
}
- /* The 486 suffers more from non-aligned cache line fills, and the
- larger code size results in a larger cache foot-print and more misses.
- The 486 has a 16 byte cache line, pentium and pentiumpro have a 32 byte
- cache line. */
- def_align = (TARGET_486) ? 4 : 2;
-
/* Validate -malign-loops= value, or provide default. */
-#ifdef ASM_OUTPUT_MAX_SKIP_ALIGN
- i386_align_loops = 4;
-#else
- i386_align_loops = 2;
-#endif
- if (i386_align_loops_string)
+ ix86_align_loops = processor_target_table[ix86_cpu].align_loop;
+ if (ix86_align_loops_string)
{
- i386_align_loops = atoi (i386_align_loops_string);
- if (i386_align_loops < 0 || i386_align_loops > MAX_CODE_ALIGN)
+ ix86_align_loops = atoi (ix86_align_loops_string);
+ if (ix86_align_loops < 0 || ix86_align_loops > MAX_CODE_ALIGN)
fatal ("-malign-loops=%d is not between 0 and %d",
- i386_align_loops, MAX_CODE_ALIGN);
+ ix86_align_loops, MAX_CODE_ALIGN);
}
/* Validate -malign-jumps= value, or provide default. */
-#ifdef ASM_OUTPUT_MAX_SKIP_ALIGN
- i386_align_jumps = 4;
-#else
- i386_align_jumps = def_align;
-#endif
- if (i386_align_jumps_string)
+ ix86_align_jumps = processor_target_table[ix86_cpu].align_jump;
+ if (ix86_align_jumps_string)
{
- i386_align_jumps = atoi (i386_align_jumps_string);
- if (i386_align_jumps < 0 || i386_align_jumps > MAX_CODE_ALIGN)
+ ix86_align_jumps = atoi (ix86_align_jumps_string);
+ if (ix86_align_jumps < 0 || ix86_align_jumps > MAX_CODE_ALIGN)
fatal ("-malign-jumps=%d is not between 0 and %d",
- i386_align_jumps, MAX_CODE_ALIGN);
+ ix86_align_jumps, MAX_CODE_ALIGN);
}
/* Validate -malign-functions= value, or provide default. */
- i386_align_funcs = def_align;
- if (i386_align_funcs_string)
+ ix86_align_funcs = processor_target_table[ix86_cpu].align_func;
+ if (ix86_align_funcs_string)
{
- i386_align_funcs = atoi (i386_align_funcs_string);
- if (i386_align_funcs < 0 || i386_align_funcs > MAX_CODE_ALIGN)
+ ix86_align_funcs = atoi (ix86_align_funcs_string);
+ if (ix86_align_funcs < 0 || ix86_align_funcs > MAX_CODE_ALIGN)
fatal ("-malign-functions=%d is not between 0 and %d",
- i386_align_funcs, MAX_CODE_ALIGN);
+ ix86_align_funcs, MAX_CODE_ALIGN);
}
/* Validate -mpreferred_stack_boundary= value, or provide default.
The default of 128 bits is for Pentium III's SSE __m128. */
- i386_preferred_stack_boundary = 128;
- if (i386_preferred_stack_boundary_string)
+ ix86_preferred_stack_boundary = 128;
+ if (ix86_preferred_stack_boundary_string)
{
- i = atoi (i386_preferred_stack_boundary_string);
+ int i = atoi (ix86_preferred_stack_boundary_string);
if (i < 2 || i > 31)
fatal ("-mpreferred_stack_boundary=%d is not between 2 and 31", i);
- i386_preferred_stack_boundary = (1 << i) * BITS_PER_UNIT;
+ ix86_preferred_stack_boundary = (1 << i) * BITS_PER_UNIT;
}
/* Validate -mbranch-cost= value, or provide default. */
- i386_branch_cost = 1;
- if (i386_branch_cost_string)
+ ix86_branch_cost = processor_target_table[ix86_cpu].branch_cost;
+ if (ix86_branch_cost_string)
{
- i386_branch_cost = atoi (i386_branch_cost_string);
- if (i386_branch_cost < 0 || i386_branch_cost > 5)
- fatal ("-mbranch-cost=%d is not between 0 and 5", i386_branch_cost);
+ ix86_branch_cost = atoi (ix86_branch_cost_string);
+ if (ix86_branch_cost < 0 || ix86_branch_cost > 5)
+ fatal ("-mbranch-cost=%d is not between 0 and 5",
+ ix86_branch_cost);
}
/* Keep nonleaf frame pointers. */
if (TARGET_OMIT_LEAF_FRAME_POINTER)
flag_omit_frame_pointer = 1;
+
+ /* If we're doing fast math, we don't care about comparison order
+ wrt NaNs. This lets us use a shorter comparison sequence. */
+ if (flag_fast_math)
+ target_flags &= ~MASK_IEEE_FP;
+
+ /* If we're planning on using `loop', use it. */
+ if (TARGET_USE_LOOP && optimize)
+ flag_branch_on_count_reg = 1;
}
\f
/* A C statement (sans semicolon) to choose the order in which to
/* User specified the register allocation order. */
- if (i386_reg_alloc_order)
+ if (ix86_reg_alloc_order)
{
- for (i = order = 0; (ch = i386_reg_alloc_order[i]) != '\0'; i++)
+ for (i = order = 0; (ch = ix86_reg_alloc_order[i]) != '\0'; i++)
{
int regno = 0;
#endif
}
\f
-/* Sign-extend a 16-bit constant */
-
-struct rtx_def *
-i386_sext16_if_const (op)
- struct rtx_def *op;
-{
- if (GET_CODE (op) == CONST_INT)
- {
- HOST_WIDE_INT val = INTVAL (op);
- HOST_WIDE_INT sext_val;
- if (val & 0x8000)
- sext_val = val | ~0xffff;
- else
- sext_val = val & 0xffff;
- if (sext_val != val)
- op = GEN_INT (sext_val);
- }
- return op;
-}
-\f
-/* Return nonzero if the rtx is aligned */
-
-static int
-i386_aligned_reg_p (regno)
- int regno;
-{
- return (regno == STACK_POINTER_REGNUM
- || (! flag_omit_frame_pointer && regno == FRAME_POINTER_REGNUM));
-}
+/* Return nonzero if the rtx is known aligned. */
+/* ??? Unused. */
int
-i386_aligned_p (op)
+ix86_aligned_p (op)
rtx op;
{
+ struct ix86_address parts;
+
/* Registers and immediate operands are always "aligned". */
if (GET_CODE (op) != MEM)
return 1;
if (MEM_VOLATILE_P (op))
return 0;
- /* Get address of memory operand. */
op = XEXP (op, 0);
- switch (GET_CODE (op))
- {
- case CONST_INT:
- if (INTVAL (op) & 3)
- break;
- return 1;
-
- /* Match "reg + offset" */
- case PLUS:
- if (GET_CODE (XEXP (op, 1)) != CONST_INT)
- break;
- if (INTVAL (XEXP (op, 1)) & 3)
- break;
-
- op = XEXP (op, 0);
- if (GET_CODE (op) != REG)
- break;
-
- /* ... fall through ... */
+ /* Pushes and pops are only valid on the stack pointer. */
+ if (GET_CODE (op) == PRE_DEC
+ || GET_CODE (op) == POST_INC)
+ return 1;
- case REG:
- return i386_aligned_reg_p (REGNO (op));
+ /* Decode the address. */
+ if (! ix86_decompose_address (op, &parts))
+ abort ();
- default:
- break;
+ /* Look for some component that isn't known to be aligned. */
+ if (parts.index)
+ {
+ if (parts.scale < 4
+ && REGNO_POINTER_ALIGN (REGNO (parts.index)) < 4)
+ return 0;
+ }
+ if (parts.base)
+ {
+ if (REGNO_POINTER_ALIGN (REGNO (parts.index)) < 4)
+ return 0;
+ }
+ if (parts.disp)
+ {
+ if (GET_CODE (parts.disp) != CONST_INT
+ || (INTVAL (parts.disp) & 3) != 0)
+ return 0;
}
- return 0;
-}
-\f
-/* Return nonzero if INSN looks like it won't compute useful cc bits
- as a side effect. This information is only a hint. */
-
-int
-i386_cc_probably_useless_p (insn)
- rtx insn;
-{
- return ! next_cc0_user (insn);
+ /* Didn't find one -- this must be an aligned address. */
+ return 1;
}
\f
/* Return nonzero if IDENTIFIER with arguments ARGS is a valid machine specific
assigned to DECL. */
int
-i386_valid_decl_attribute_p (decl, attributes, identifier, args)
+ix86_valid_decl_attribute_p (decl, attributes, identifier, args)
tree decl ATTRIBUTE_UNUSED;
tree attributes ATTRIBUTE_UNUSED;
tree identifier ATTRIBUTE_UNUSED;
assigned to TYPE. */
int
-i386_valid_type_attribute_p (type, attributes, identifier, args)
+ix86_valid_type_attribute_p (type, attributes, identifier, args)
tree type;
tree attributes ATTRIBUTE_UNUSED;
tree identifier;
warning to be generated). */
int
-i386_comp_type_attributes (type1, type2)
+ix86_comp_type_attributes (type1, type2)
tree type1;
tree type2;
{
return 0;
return 1;
}
-
\f
/* Value is the number of bytes of arguments automatically
popped when returning from a subroutine call.
The attribute stdcall is equivalent to RTD on a per module basis. */
int
-i386_return_pops_args (fundecl, funtype, size)
+ix86_return_pops_args (fundecl, funtype, size)
tree fundecl;
tree funtype;
int size;
return 0;
}
-
\f
/* Argument support functions. */
*cum = zero_cum;
/* Set up the number of registers to use for passing arguments. */
- cum->nregs = i386_regparm;
+ cum->nregs = ix86_regparm;
if (fntype)
{
tree attr = lookup_attribute ("regparm", TYPE_ATTRIBUTES (fntype));
return ret;
}
-
-/* For an arg passed partly in registers and partly in memory,
- this is the number of registers used.
- For args passed entirely in registers or entirely in memory, zero. */
+\f
+/* Returns 1 if OP is either a symbol reference or a sum of a symbol
+ reference and a constant. */
int
-function_arg_partial_nregs (cum, mode, type, named)
- CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED; /* current arg information */
- enum machine_mode mode ATTRIBUTE_UNUSED; /* current arg mode */
- tree type ATTRIBUTE_UNUSED; /* type of the argument or 0 if lib support */
- int named ATTRIBUTE_UNUSED; /* != 0 for normal args, == 0 for ... args */
-{
- return 0;
-}
-\f
-char *
-singlemove_string (operands)
- rtx *operands;
+symbolic_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
{
- rtx x;
- if (GET_CODE (operands[0]) == MEM
- && GET_CODE (x = XEXP (operands[0], 0)) == PRE_DEC)
- {
- if (XEXP (x, 0) != stack_pointer_rtx)
- abort ();
- return "push%L1 %1";
- }
- else if (GET_CODE (operands[1]) == CONST_DOUBLE)
- return output_move_const_single (operands);
- else if (GET_CODE (operands[0]) == REG || GET_CODE (operands[1]) == REG)
- return AS2 (mov%L0,%1,%0);
- else if (CONSTANT_P (operands[1]))
- return AS2 (mov%L0,%1,%0);
- else
+ switch (GET_CODE (op))
{
- output_asm_insn ("push%L1 %1", operands);
- return "pop%L0 %0";
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return 1;
+
+ case CONST:
+ op = XEXP (op, 0);
+ if (GET_CODE (op) == SYMBOL_REF
+ || GET_CODE (op) == LABEL_REF
+ || (GET_CODE (op) == UNSPEC
+ && XINT (op, 1) >= 6
+ && XINT (op, 1) <= 7))
+ return 1;
+ if (GET_CODE (op) != PLUS
+ || GET_CODE (XEXP (op, 1)) != CONST_INT)
+ return 0;
+
+ op = XEXP (op, 0);
+ if (GET_CODE (op) == SYMBOL_REF
+ || GET_CODE (op) == LABEL_REF)
+ return 1;
+ /* Only @GOTOFF gets offsets. */
+ if (GET_CODE (op) != UNSPEC
+ || XINT (op, 1) != 7)
+ return 0;
+
+ op = XVECEXP (op, 0, 0);
+ if (GET_CODE (op) == SYMBOL_REF
+ || GET_CODE (op) == LABEL_REF)
+ return 1;
+ return 0;
+
+ default:
+ return 0;
}
}
-\f
-/* Output an insn to add the constant N to the register X. */
-static void
-asm_add (n, x)
- int n;
- rtx x;
-{
- rtx xops[2];
- xops[0] = x;
+/* Return true if the operand contains a @GOT or @GOTOFF reference. */
- if (n == -1)
- output_asm_insn (AS1 (dec%L0,%0), xops);
- else if (n == 1)
- output_asm_insn (AS1 (inc%L0,%0), xops);
- else if (n < 0 || n == 128)
- {
- xops[1] = GEN_INT (-n);
- output_asm_insn (AS2 (sub%L0,%1,%0), xops);
- }
- else if (n > 0)
+int
+pic_symbolic_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ if (GET_CODE (op) == CONST)
{
- xops[1] = GEN_INT (n);
- output_asm_insn (AS2 (add%L0,%1,%0), xops);
+ op = XEXP (op, 0);
+ if (GET_CODE (op) == UNSPEC)
+ return 1;
+ if (GET_CODE (op) != PLUS
+ || GET_CODE (XEXP (op, 1)) != CONST_INT)
+ return 0;
+ op = XEXP (op, 0);
+ if (GET_CODE (op) == UNSPEC)
+ return 1;
}
+ return 0;
}
-\f
-/* Output assembler code to perform a doubleword move insn
- with operands OPERANDS. */
-char *
-output_move_double (operands)
- rtx *operands;
-{
- enum {REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
- rtx latehalf[2];
- rtx middlehalf[2];
- rtx xops[2];
- int dest_overlapped_low = 0;
- int size = GET_MODE_SIZE (GET_MODE (operands[0]));
-
- middlehalf[0] = 0;
- middlehalf[1] = 0;
-
- /* First classify both operands. */
-
- if (REG_P (operands[0]))
- optype0 = REGOP;
- else if (offsettable_memref_p (operands[0]))
- optype0 = OFFSOP;
- else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
- optype0 = POPOP;
- else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
- optype0 = PUSHOP;
- else if (GET_CODE (operands[0]) == MEM)
- optype0 = MEMOP;
- else
- optype0 = RNDOP;
-
- if (REG_P (operands[1]))
- optype1 = REGOP;
- else if (CONSTANT_P (operands[1]))
- optype1 = CNSTOP;
- else if (offsettable_memref_p (operands[1]))
- optype1 = OFFSOP;
- else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
- optype1 = POPOP;
- else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
- optype1 = PUSHOP;
- else if (GET_CODE (operands[1]) == MEM)
- optype1 = MEMOP;
- else
- optype1 = RNDOP;
+/* Test for a valid operand for a call instruction.
+ Don't allow the arg pointer register or virtual regs
+ since they may change into reg + const, which the patterns
+ can't handle yet. */
- /* Check for the cases that are not supposed to happen
- either due to the operand constraints or the fact
- that all memory operands on the x86 are offsettable.
- Abort if we get one, because generating code for these
- cases is painful. */
+int
+call_insn_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ if (GET_CODE (op) != MEM)
+ return 0;
+ op = XEXP (op, 0);
- if (optype0 == RNDOP || optype1 == RNDOP
- || optype0 == MEMOP || optype1 == MEMOP)
- abort ();
+ /* Disallow indirect through a virtual register. This leads to
+ compiler aborts when trying to eliminate them. */
+ if (GET_CODE (op) == REG
+ && (op == arg_pointer_rtx
+ || (REGNO (op) >= FIRST_PSEUDO_REGISTER
+ && REGNO (op) <= LAST_VIRTUAL_REGISTER)))
+ return 0;
- /* If one operand is decrementing and one is incrementing
- decrement the former register explicitly
- and change that operand into ordinary indexing. */
+ /* Otherwise we can allow any general_operand in the address. */
+ return general_operand (op, Pmode);
+}
- if (optype0 == PUSHOP && optype1 == POPOP)
- {
- /* ??? Can this ever happen on i386? */
- operands[0] = XEXP (XEXP (operands[0], 0), 0);
- asm_add (-size, operands[0]);
- if (GET_MODE (operands[1]) == XFmode)
- operands[0] = gen_rtx_MEM (XFmode, operands[0]);
- else if (GET_MODE (operands[0]) == DFmode)
- operands[0] = gen_rtx_MEM (DFmode, operands[0]);
- else
- operands[0] = gen_rtx_MEM (DImode, operands[0]);
- optype0 = OFFSOP;
- }
+/* Like call_insn_operand but allow (mem (symbol_ref ...))
+ even if pic. */
- if (optype0 == POPOP && optype1 == PUSHOP)
- {
- /* ??? Can this ever happen on i386? */
- operands[1] = XEXP (XEXP (operands[1], 0), 0);
- asm_add (-size, operands[1]);
- if (GET_MODE (operands[1]) == XFmode)
- operands[1] = gen_rtx_MEM (XFmode, operands[1]);
- else if (GET_MODE (operands[1]) == DFmode)
- operands[1] = gen_rtx_MEM (DFmode, operands[1]);
- else
- operands[1] = gen_rtx_MEM (DImode, operands[1]);
- optype1 = OFFSOP;
- }
+int
+expander_call_insn_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ if (GET_CODE (op) != MEM)
+ return 0;
+ op = XEXP (op, 0);
- /* Ok, we can do one word at a time.
- Normally we do the low-numbered word first,
- but if either operand is autodecrementing then we
- do the high-numbered word first.
+ /* Direct symbol references. */
+ if (CONSTANT_ADDRESS_P (op))
+ return 1;
- In either case, set up in LATEHALF the operands to use
- for the high-numbered word and in some cases alter the
- operands in OPERANDS to be suitable for the low-numbered word. */
+ /* Disallow indirect through a virtual register. This leads to
+ compiler aborts when trying to eliminate them. */
+ if (GET_CODE (op) == REG
+ && (op == arg_pointer_rtx
+ || (REGNO (op) >= FIRST_PSEUDO_REGISTER
+ && REGNO (op) <= LAST_VIRTUAL_REGISTER)))
+ return 0;
- if (size == 12)
- {
- if (optype0 == REGOP)
- {
- middlehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
- latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 2);
- }
- else if (optype0 == OFFSOP)
- {
- middlehalf[0] = adj_offsettable_operand (operands[0], 4);
- latehalf[0] = adj_offsettable_operand (operands[0], 8);
- }
- else
- {
- middlehalf[0] = operands[0];
- latehalf[0] = operands[0];
- }
+ /* Otherwise we can allow any general_operand in the address. */
+ return general_operand (op, mode);
+}
- if (optype1 == REGOP)
- {
- middlehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1);
- latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 2);
- }
- else if (optype1 == OFFSOP)
- {
- middlehalf[1] = adj_offsettable_operand (operands[1], 4);
- latehalf[1] = adj_offsettable_operand (operands[1], 8);
- }
- else if (optype1 == CNSTOP)
- {
- if (GET_CODE (operands[1]) == CONST_DOUBLE)
- {
- REAL_VALUE_TYPE r; long l[3];
+int
+constant_call_address_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return GET_CODE (op) == MEM && CONSTANT_ADDRESS_P (XEXP (op, 0));
+}
- REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
- REAL_VALUE_TO_TARGET_LONG_DOUBLE (r, l);
- operands[1] = GEN_INT (l[0]);
- middlehalf[1] = GEN_INT (l[1]);
- latehalf[1] = GEN_INT (l[2]);
- }
- else if (CONSTANT_P (operands[1]))
- /* No non-CONST_DOUBLE constant should ever appear here. */
- abort ();
- }
- else
- {
- middlehalf[1] = operands[1];
- latehalf[1] = operands[1];
- }
- }
+/* Match exactly zero and one. */
- else
- {
- /* Size is not 12. */
+int
+const0_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return op == CONST0_RTX (mode);
+}
- if (optype0 == REGOP)
- latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
- else if (optype0 == OFFSOP)
- latehalf[0] = adj_offsettable_operand (operands[0], 4);
- else
- latehalf[0] = operands[0];
-
- if (optype1 == REGOP)
- latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1);
- else if (optype1 == OFFSOP)
- latehalf[1] = adj_offsettable_operand (operands[1], 4);
- else if (optype1 == CNSTOP)
- split_double (operands[1], &operands[1], &latehalf[1]);
- else
- latehalf[1] = operands[1];
- }
+int
+const1_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return op == const1_rtx;
+}
- /* If insn is effectively movd N (sp),-(sp) then we will do the
- high word first. We should use the adjusted operand 1
- (which is N+4 (sp) or N+8 (sp))
- for the low word and middle word as well,
- to compensate for the first decrement of sp. */
- if (optype0 == PUSHOP
- && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
- && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
- middlehalf[1] = operands[1] = latehalf[1];
-
- /* For (set (reg:DI N) (mem:DI ... (reg:SI N) ...)),
- if the upper part of reg N does not appear in the MEM, arrange to
- emit the move late-half first. Otherwise, compute the MEM address
- into the upper part of N and use that as a pointer to the memory
- operand. */
- if (optype0 == REGOP && optype1 == OFFSOP)
- {
- if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))
- && reg_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
- {
- /* If both halves of dest are used in the src memory address,
- compute the address into latehalf of dest. */
- compadr:
- xops[0] = latehalf[0];
- xops[1] = XEXP (operands[1], 0);
- output_asm_insn (AS2 (lea%L0,%a1,%0), xops);
- if (GET_MODE (operands[1]) == XFmode)
- {
- operands[1] = gen_rtx_MEM (XFmode, latehalf[0]);
- middlehalf[1] = adj_offsettable_operand (operands[1], size-8);
- latehalf[1] = adj_offsettable_operand (operands[1], size-4);
- }
- else
- {
- operands[1] = gen_rtx_MEM (DImode, latehalf[0]);
- latehalf[1] = adj_offsettable_operand (operands[1], size-4);
- }
- }
+/* Match 2, 4, or 8. Used for leal multiplicands. */
- else if (size == 12
- && reg_mentioned_p (middlehalf[0], XEXP (operands[1], 0)))
- {
- /* Check for two regs used by both source and dest. */
- if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))
- || reg_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
- goto compadr;
-
- /* Only the middle reg conflicts; simply put it last. */
- output_asm_insn (singlemove_string (operands), operands);
- output_asm_insn (singlemove_string (latehalf), latehalf);
- output_asm_insn (singlemove_string (middlehalf), middlehalf);
- return "";
- }
+int
+const248_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return (GET_CODE (op) == CONST_INT
+ && (INTVAL (op) == 2 || INTVAL (op) == 4 || INTVAL (op) == 8));
+}
- else if (reg_mentioned_p (operands[0], XEXP (operands[1], 0)))
- /* If the low half of dest is mentioned in the source memory
- address, the arrange to emit the move late half first. */
- dest_overlapped_low = 1;
- }
+/* True if this is a constant appropriate for an increment or decremenmt. */
- /* If one or both operands autodecrementing,
- do the two words, high-numbered first. */
-
- /* Likewise, the first move would clobber the source of the second one,
- do them in the other order. This happens only for registers;
- such overlap can't happen in memory unless the user explicitly
- sets it up, and that is an undefined circumstance. */
-
-#if 0
- if (optype0 == PUSHOP || optype1 == PUSHOP
- || (optype0 == REGOP && optype1 == REGOP
- && REGNO (operands[0]) == REGNO (latehalf[1]))
- || dest_overlapped_low)
-#endif
-
- if (optype0 == PUSHOP || optype1 == PUSHOP
- || (optype0 == REGOP && optype1 == REGOP
- && ((middlehalf[1] && REGNO (operands[0]) == REGNO (middlehalf[1]))
- || REGNO (operands[0]) == REGNO (latehalf[1])))
- || dest_overlapped_low)
- {
- /* Do the high-numbered word. */
- output_asm_insn (singlemove_string (latehalf), latehalf);
-
- if (size == 12)
- output_asm_insn (singlemove_string (middlehalf), middlehalf);
-
- /* Do low-numbered word. */
- return singlemove_string (operands);
- }
-
- /* Normal case: do the two words, low-numbered first. */
+int
+incdec_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (op == const1_rtx || op == constm1_rtx)
+ return 1;
+ if (GET_CODE (op) != CONST_INT)
+ return 0;
+ if (mode == SImode && INTVAL (op) == (HOST_WIDE_INT) 0xffffffff)
+ return 1;
+ if (mode == HImode && INTVAL (op) == (HOST_WIDE_INT) 0xffff)
+ return 1;
+ if (mode == QImode && INTVAL (op) == (HOST_WIDE_INT) 0xff)
+ return 1;
+ return 0;
+}
- output_asm_insn (singlemove_string (operands), operands);
+/* Return false if this is the stack pointer, or any other fake
+ register eliminable to the stack pointer. Otherwise, this is
+ a register operand.
- /* Do the middle one of the three words for long double */
- if (size == 12)
- output_asm_insn (singlemove_string (middlehalf), middlehalf);
+ This is used to prevent esp from being used as an index reg.
+ Which would only happen in pathological cases. */
- /* Do the high-numbered word. */
- output_asm_insn (singlemove_string (latehalf), latehalf);
+int
+reg_no_sp_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ rtx t = op;
+ if (GET_CODE (t) == SUBREG)
+ t = SUBREG_REG (t);
+ if (t == stack_pointer_rtx || t == arg_pointer_rtx)
+ return 0;
- return "";
+ return register_operand (op, mode);
}
-\f
-#define MAX_TMPS 2 /* max temporary registers used */
-/* Output the appropriate code to move push memory on the stack */
+/* Return true if op is a Q_REGS class register. */
-char *
-output_move_pushmem (operands, insn, length, tmp_start, n_operands)
- rtx operands[];
- rtx insn;
- int length;
- int tmp_start;
- int n_operands;
+int
+q_regs_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
{
- struct
- {
- char *load;
- char *push;
- rtx xops[2];
- } tmp_info[MAX_TMPS];
-
- rtx src = operands[1];
- int max_tmps = 0;
- int offset = 0;
- int stack_p = reg_overlap_mentioned_p (stack_pointer_rtx, src);
- int stack_offset = 0;
- int i, num_tmps;
- rtx xops[1];
-
- if (! offsettable_memref_p (src))
- fatal_insn ("Source is not offsettable", insn);
-
- if ((length & 3) != 0)
- fatal_insn ("Pushing non-word aligned size", insn);
+ if (mode != VOIDmode && GET_MODE (op) != mode)
+ return 0;
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ return QI_REG_P (op);
+}
- /* Figure out which temporary registers we have available */
- for (i = tmp_start; i < n_operands; i++)
- {
- if (GET_CODE (operands[i]) == REG)
- {
- if (reg_overlap_mentioned_p (operands[i], src))
- continue;
+/* Return true if op is a NON_Q_REGS class register. */
- tmp_info[ max_tmps++ ].xops[1] = operands[i];
- if (max_tmps == MAX_TMPS)
- break;
- }
- }
+int
+non_q_regs_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (mode != VOIDmode && GET_MODE (op) != mode)
+ return 0;
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ return NON_QI_REG_P (op);
+}
- if (max_tmps == 0)
- for (offset = length - 4; offset >= 0; offset -= 4)
- {
- xops[0] = adj_offsettable_operand (src, offset + stack_offset);
- output_asm_insn (AS1(push%L0,%0), xops);
- if (stack_p)
- stack_offset += 4;
- }
+/* Return 1 if OP is a comparison operator that can use the condition code
+ generated by a logical operation, which characteristicly does not set
+ overflow or carry. To be used with CCNOmode. */
- else
- for (offset = length - 4; offset >= 0; )
- {
- for (num_tmps = 0; num_tmps < max_tmps && offset >= 0; num_tmps++)
- {
- tmp_info[num_tmps].load = AS2(mov%L0,%0,%1);
- tmp_info[num_tmps].push = AS1(push%L0,%1);
- tmp_info[num_tmps].xops[0]
- = adj_offsettable_operand (src, offset + stack_offset);
- offset -= 4;
- }
+int
+no_comparison_operator (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return ((mode == VOIDmode || GET_MODE (op) == mode)
+ && GET_RTX_CLASS (GET_CODE (op)) == '<'
+ && GET_CODE (op) != LE
+ && GET_CODE (op) != GT);
+}
- for (i = 0; i < num_tmps; i++)
- output_asm_insn (tmp_info[i].load, tmp_info[i].xops);
+/* Return 1 if OP is a comparison operator that can be issued by fcmov. */
- for (i = 0; i < num_tmps; i++)
- output_asm_insn (tmp_info[i].push, tmp_info[i].xops);
+int
+fcmov_comparison_operator (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return ((mode == VOIDmode || GET_MODE (op) == mode)
+ && GET_RTX_CLASS (GET_CODE (op)) == '<'
+ && GET_CODE (op) == unsigned_condition (GET_CODE (op)));
+}
- if (stack_p)
- stack_offset += 4*num_tmps;
- }
+/* Nearly general operand, but accept any const_double, since we wish
+ to be able to drop them into memory rather than have them get pulled
+ into registers. */
- return "";
-}
-\f
int
-standard_80387_constant_p (x)
- rtx x;
+cmp_fp_expander_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
{
-#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
- REAL_VALUE_TYPE d;
- jmp_buf handler;
- int is0, is1;
-
- if (setjmp (handler))
+ if (mode != VOIDmode && mode != GET_MODE (op))
return 0;
-
- set_float_handler (handler);
- REAL_VALUE_FROM_CONST_DOUBLE (d, x);
- is0 = REAL_VALUES_EQUAL (d, dconst0) && !REAL_VALUE_MINUS_ZERO (d);
- is1 = REAL_VALUES_EQUAL (d, dconst1);
- set_float_handler (NULL_PTR);
-
- if (is0)
+ if (GET_CODE (op) == CONST_DOUBLE)
return 1;
-
- if (is1)
- return 2;
-
- /* Note that on the 80387, other constants, such as pi,
- are much slower to load as standard constants
- than to load from doubles in memory! */
- /* ??? Not true on K6: all constants are equal cost. */
-#endif
-
- return 0;
+ return general_operand (op, mode);
}
-char *
-output_move_const_single (operands)
- rtx *operands;
-{
- if (FP_REG_P (operands[0]))
- {
- int conval = standard_80387_constant_p (operands[1]);
-
- if (conval == 1)
- return "fldz";
-
- if (conval == 2)
- return "fld1";
- }
-
- if (GET_CODE (operands[1]) == CONST_DOUBLE)
- {
- REAL_VALUE_TYPE r; long l;
-
- if (GET_MODE (operands[1]) == XFmode)
- abort ();
-
- REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
- REAL_VALUE_TO_TARGET_SINGLE (r, l);
- operands[1] = GEN_INT (l);
- }
-
- return singlemove_string (operands);
-}
-\f
-/* Returns 1 if OP is either a symbol reference or a sum of a symbol
- reference and a constant. */
+/* Match an SI or HImode register for a zero_extract. */
int
-symbolic_operand (op, mode)
+ext_register_operand (op, mode)
register rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
+ if (GET_MODE (op) != SImode && GET_MODE (op) != HImode)
+ return 0;
+ return register_operand (op, VOIDmode);
+}
+
+/* Return 1 if this is a valid binary floating-point operation.
+ OP is the expression matched, and MODE is its mode. */
+
+int
+binary_fp_operator (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+
switch (GET_CODE (op))
{
- case SYMBOL_REF:
- case LABEL_REF:
- return 1;
-
- case CONST:
- op = XEXP (op, 0);
- return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
- || GET_CODE (XEXP (op, 0)) == LABEL_REF)
- && GET_CODE (XEXP (op, 1)) == CONST_INT);
+ case PLUS:
+ case MINUS:
+ case MULT:
+ case DIV:
+ return GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT;
default:
return 0;
}
}
-/* Return nonzero if OP is a constant shift count small enough to
- encode into an lea instruction. */
+int
+mult_operator(op, mode)
+ register rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return GET_CODE (op) == MULT;
+}
+
+int
+div_operator(op, mode)
+ register rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return GET_CODE (op) == DIV;
+}
int
-small_shift_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+arith_or_logical_operator (op, mode)
+ rtx op;
+ enum machine_mode mode;
{
- return (GET_CODE (op) == CONST_INT && INTVAL (op) > 0 && INTVAL (op) < 4);
+ return ((mode == VOIDmode || GET_MODE (op) == mode)
+ && (GET_RTX_CLASS (GET_CODE (op)) == 'c'
+ || GET_RTX_CLASS (GET_CODE (op)) == '2'));
}
-/* Test for a valid operand for a call instruction.
- Don't allow the arg pointer register or virtual regs
- since they may change into reg + const, which the patterns
- can't handle yet. */
+/* Returns 1 if OP is memory operand with a displacement. */
int
-call_insn_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+memory_displacement_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
{
- if (GET_CODE (op) == MEM
- && ((CONSTANT_ADDRESS_P (XEXP (op, 0))
- /* This makes a difference for PIC. */
- && general_operand (XEXP (op, 0), Pmode))
- || (GET_CODE (XEXP (op, 0)) == REG
- && XEXP (op, 0) != arg_pointer_rtx
- && ! (REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER
- && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER))))
- return 1;
+ struct ix86_address parts;
- return 0;
+ if (! memory_operand (op, mode))
+ return 0;
+
+ if (! ix86_decompose_address (XEXP (op, 0), &parts))
+ abort ();
+
+ return parts.disp != NULL_RTX;
}
-/* Like call_insn_operand but allow (mem (symbol_ref ...))
- even if pic. */
+/* To avoid problems when jump re-emits comparisons like testqi_ext_0,
+ re-recognize the operand to avoid a copy_to_mode_reg that will fail.
+
+ ??? It seems likely that this will only work because cmpsi is an
+ expander, and no actual insns use this. */
int
-expander_call_insn_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+cmpsi_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
{
- if (GET_CODE (op) == MEM
- && (CONSTANT_ADDRESS_P (XEXP (op, 0))
- || (GET_CODE (XEXP (op, 0)) == REG
- && XEXP (op, 0) != arg_pointer_rtx
- && ! (REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER
- && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER))))
+ if (general_operand (op, mode))
+ return 1;
+
+ if (GET_CODE (op) == AND
+ && GET_MODE (op) == SImode
+ && GET_CODE (XEXP (op, 0)) == ZERO_EXTRACT
+ && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT
+ && GET_CODE (XEXP (XEXP (op, 0), 2)) == CONST_INT
+ && INTVAL (XEXP (XEXP (op, 0), 1)) == 8
+ && INTVAL (XEXP (XEXP (op, 0), 2)) == 8
+ && GET_CODE (XEXP (op, 1)) == CONST_INT)
return 1;
return 0;
}
-/* Return 1 if OP is a comparison operator that can use the condition code
- generated by an arithmetic operation. */
+/* Returns 1 if OP is memory operand that can not be represented by the
+ modRM array. */
int
-arithmetic_comparison_operator (op, mode)
+long_memory_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
- enum rtx_code code;
-
- if (mode != VOIDmode && mode != GET_MODE (op))
- return 0;
-
- code = GET_CODE (op);
- if (GET_RTX_CLASS (code) != '<')
+ if (! memory_operand (op, mode))
return 0;
- return (code != GT && code != LE);
+ return memory_address_length (op) != 0;
}
+\f
+/* Return true if the constant is something that can be loaded with
+ a special instruction. Only handle 0.0 and 1.0; others are less
+ worthwhile. */
int
-ix86_logical_operator (op, mode)
- register rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+standard_80387_constant_p (x)
+ rtx x;
{
- return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR;
+ if (GET_CODE (x) != CONST_DOUBLE)
+ return -1;
+
+#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+ {
+ REAL_VALUE_TYPE d;
+ jmp_buf handler;
+ int is0, is1;
+
+ if (setjmp (handler))
+ return 0;
+
+ set_float_handler (handler);
+ REAL_VALUE_FROM_CONST_DOUBLE (d, x);
+ is0 = REAL_VALUES_EQUAL (d, dconst0) && !REAL_VALUE_MINUS_ZERO (d);
+ is1 = REAL_VALUES_EQUAL (d, dconst1);
+ set_float_handler (NULL_PTR);
+
+ if (is0)
+ return 1;
+
+ if (is1)
+ return 2;
+
+ /* Note that on the 80387, other constants, such as pi,
+ are much slower to load as standard constants
+ than to load from doubles in memory! */
+ /* ??? Not true on K6: all constants are equal cost. */
+ }
+#endif
+
+ return 0;
}
-\f
/* Returns 1 if OP contains a symbol reference */
int
return 0;
}
-\f
-/* Attempt to expand a binary operator. Make the expansion closer to the
- actual machine, then just general_operand, which will allow 3 separate
- memory references (one output, two input) in a single insn. Return
- whether the insn fails, or succeeds. */
+
+/* Return 1 if it is appropriate to emit `ret' instructions in the
+ body of a function. Do this only if the epilogue is simple, needing a
+ couple of insns. Prior to reloading, we can't tell how many registers
+ must be saved, so return 0 then. Return 0 if there is no frame
+ marker to de-allocate.
+
+ If NON_SAVING_SETJMP is defined and true, then it is not possible
+ for the epilogue to be simple, so return 0. This is a special case
+ since NON_SAVING_SETJMP will not cause regs_ever_live to change
+ until final, but jump_optimize may need to know sooner if a
+ `return' is OK. */
int
-ix86_expand_binary_operator (code, mode, operands)
- enum rtx_code code;
- enum machine_mode mode;
- rtx operands[];
+ix86_can_use_return_insn_p ()
{
- int modified;
+ int regno;
+ int nregs = 0;
+ int reglimit = (frame_pointer_needed
+ ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
+ int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
+ || current_function_uses_const_pool);
- /* Recognize <var1> = <value> <op> <var1> for commutative operators */
- if (GET_RTX_CLASS (code) == 'c'
- && (rtx_equal_p (operands[0], operands[2])
- || immediate_operand (operands[1], mode)))
- {
- rtx temp = operands[1];
- operands[1] = operands[2];
- operands[2] = temp;
- }
+#ifdef NON_SAVING_SETJMP
+ if (NON_SAVING_SETJMP && current_function_calls_setjmp)
+ return 0;
+#endif
- /* If optimizing, copy to regs to improve CSE */
- if (TARGET_PSEUDO && optimize
- && ((reload_in_progress | reload_completed) == 0))
- {
- if (GET_CODE (operands[1]) == MEM
- && ! rtx_equal_p (operands[0], operands[1]))
- operands[1] = force_reg (GET_MODE (operands[1]), operands[1]);
+ if (! reload_completed)
+ return 0;
- if (GET_CODE (operands[2]) == MEM)
- operands[2] = force_reg (GET_MODE (operands[2]), operands[2]);
+ for (regno = reglimit - 1; regno >= 0; regno--)
+ if ((regs_ever_live[regno] && ! call_used_regs[regno])
+ || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
+ nregs++;
- if (GET_CODE (operands[1]) == CONST_INT && code == MINUS)
- {
- rtx temp = gen_reg_rtx (GET_MODE (operands[0]));
+ return nregs == 0 || ! frame_pointer_needed;
+}
+\f
+static char pic_label_name[32];
+static int pic_label_output;
- emit_move_insn (temp, operands[1]);
- operands[1] = temp;
- return TRUE;
- }
- }
+/* This function generates code for -fpic that loads %ebx with
+ the return address of the caller and then returns. */
+
+void
+asm_output_function_prefix (file, name)
+ FILE *file;
+ char *name ATTRIBUTE_UNUSED;
+{
+ rtx xops[2];
+ int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
+ || current_function_uses_const_pool);
+ xops[0] = pic_offset_table_rtx;
+ xops[1] = stack_pointer_rtx;
- if (!ix86_binary_operator_ok (code, mode, operands))
+ /* Deep branch prediction favors having a return for every call. */
+ if (pic_reg_used && TARGET_DEEP_BRANCH_PREDICTION)
{
- /* If not optimizing, try to make a valid insn (optimize code
- previously did this above to improve chances of CSE) */
+ if (!pic_label_output)
+ {
+ /* This used to call ASM_DECLARE_FUNCTION_NAME() but since it's an
+ internal (non-global) label that's being emitted, it didn't make
+ sense to have .type information for local labels. This caused
+ the SCO OpenServer 5.0.4 ELF assembler grief (why are you giving
+ me debug info for a label that you're declaring non-global?) this
+ was changed to call ASM_OUTPUT_LABEL() instead. */
- if ((! TARGET_PSEUDO || !optimize)
- && ((reload_in_progress | reload_completed) == 0)
- && (GET_CODE (operands[1]) == MEM || GET_CODE (operands[2]) == MEM))
- {
- modified = FALSE;
- if (GET_CODE (operands[1]) == MEM
- && ! rtx_equal_p (operands[0], operands[1]))
- {
- operands[1] = force_reg (GET_MODE (operands[1]), operands[1]);
- modified = TRUE;
- }
-
- if (GET_CODE (operands[2]) == MEM)
- {
- operands[2] = force_reg (GET_MODE (operands[2]), operands[2]);
- modified = TRUE;
- }
-
- if (GET_CODE (operands[1]) == CONST_INT && code == MINUS)
- {
- rtx temp = gen_reg_rtx (GET_MODE (operands[0]));
+ ASM_OUTPUT_LABEL (file, pic_label_name);
- emit_move_insn (temp, operands[1]);
- operands[1] = temp;
- return TRUE;
- }
+ xops[1] = gen_rtx_MEM (SImode, xops[1]);
+ output_asm_insn ("mov{l}\t{%1, %0|%0, %1}", xops);
+ output_asm_insn ("ret", xops);
- if (modified && ! ix86_binary_operator_ok (code, mode, operands))
- return FALSE;
+ pic_label_output = 1;
}
- else
- return FALSE;
}
-
- return TRUE;
}
-\f
-/* Return TRUE or FALSE depending on whether the binary operator meets the
- appropriate constraints. */
-int
-ix86_binary_operator_ok (code, mode, operands)
- enum rtx_code code;
- enum machine_mode mode ATTRIBUTE_UNUSED;
- rtx operands[3];
+void
+load_pic_register ()
{
- return (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)
- && (GET_CODE (operands[1]) != CONST_INT || GET_RTX_CLASS (code) == 'c');
-}
-\f
-/* Attempt to expand a unary operator. Make the expansion closer to the
- actual machine, then just general_operand, which will allow 2 separate
- memory references (one output, one input) in a single insn. Return
- whether the insn fails, or succeeds. */
+ rtx gotsym, pclab;
-int
-ix86_expand_unary_operator (code, mode, operands)
- enum rtx_code code;
- enum machine_mode mode;
- rtx operands[];
-{
- /* If optimizing, copy to regs to improve CSE */
- if (TARGET_PSEUDO
- && optimize
- && ((reload_in_progress | reload_completed) == 0)
- && GET_CODE (operands[1]) == MEM)
- operands[1] = force_reg (GET_MODE (operands[1]), operands[1]);
+ gotsym = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
- if (! ix86_unary_operator_ok (code, mode, operands))
+ if (TARGET_DEEP_BRANCH_PREDICTION)
{
- if ((! TARGET_PSEUDO || optimize == 0)
- && ((reload_in_progress | reload_completed) == 0)
- && GET_CODE (operands[1]) == MEM)
- {
- operands[1] = force_reg (GET_MODE (operands[1]), operands[1]);
- if (! ix86_unary_operator_ok (code, mode, operands))
- return FALSE;
- }
- else
- return FALSE;
+ if (pic_label_name[0] == '\0')
+ ASM_GENERATE_INTERNAL_LABEL (pic_label_name, "LPR", 0);
+ pclab = gen_rtx_MEM (QImode, gen_rtx_SYMBOL_REF (Pmode, pic_label_name));
}
-
- return TRUE;
-}
-\f
-/* Return TRUE or FALSE depending on whether the unary operator meets the
- appropriate constraints. */
-
-int
-ix86_unary_operator_ok (code, mode, operands)
- enum rtx_code code ATTRIBUTE_UNUSED;
- enum machine_mode mode ATTRIBUTE_UNUSED;
- rtx operands[2] ATTRIBUTE_UNUSED;
-{
- return TRUE;
-}
-\f
-static rtx pic_label_rtx;
-static char pic_label_name [256];
-static int pic_label_no = 0;
-
-/* This function generates code for -fpic that loads %ebx with
- the return address of the caller and then returns. */
-
-void
-asm_output_function_prefix (file, name)
- FILE *file;
- char *name ATTRIBUTE_UNUSED;
-{
- rtx xops[2];
- int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
- || current_function_uses_const_pool);
- xops[0] = pic_offset_table_rtx;
- xops[1] = stack_pointer_rtx;
-
- /* Deep branch prediction favors having a return for every call. */
- if (pic_reg_used && TARGET_DEEP_BRANCH_PREDICTION)
+ else
{
- tree prologue_node;
-
- if (pic_label_rtx == 0)
- {
- pic_label_rtx = gen_label_rtx ();
- ASM_GENERATE_INTERNAL_LABEL (pic_label_name, "LPR", pic_label_no++);
- LABEL_NAME (pic_label_rtx) = pic_label_name;
- }
-
- prologue_node = make_node (FUNCTION_DECL);
- DECL_RESULT (prologue_node) = 0;
-
- /* This used to call ASM_DECLARE_FUNCTION_NAME() but since it's an
- internal (non-global) label that's being emitted, it didn't make
- sense to have .type information for local labels. This caused
- the SCO OpenServer 5.0.4 ELF assembler grief (why are you giving
- me debug info for a label that you're declaring non-global?) this
- was changed to call ASM_OUTPUT_LABEL() instead. */
-
-
- ASM_OUTPUT_LABEL (file, pic_label_name);
- output_asm_insn ("movl (%1),%0", xops);
- output_asm_insn ("ret", xops);
+ pclab = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
}
-}
-/* Generate the assembly code for function entry.
- FILE is an stdio stream to output the code to.
- SIZE is an int: how many units of temporary storage to allocate. */
+ emit_insn (gen_prologue_get_pc (pic_offset_table_rtx, pclab));
-void
-function_prologue (file, size)
- FILE *file ATTRIBUTE_UNUSED;
- int size ATTRIBUTE_UNUSED;
-{
- if (TARGET_SCHEDULE_PROLOGUE)
- {
- pic_label_rtx = 0;
- return;
- }
+ if (! TARGET_DEEP_BRANCH_PREDICTION)
+ emit_insn (gen_popsi1 (pic_offset_table_rtx));
- ix86_prologue (0);
+ emit_insn (gen_prologue_set_got (pic_offset_table_rtx, gotsym, pclab));
}
-/* Expand the prologue into a bunch of separate insns. */
-
-void
-ix86_expand_prologue ()
-{
- if (! TARGET_SCHEDULE_PROLOGUE)
- return;
-
- ix86_prologue (1);
-}
+/* Generate an SImode "push" pattern for input ARG. */
-void
-load_pic_register (do_rtl)
- int do_rtl;
+static rtx
+gen_push (arg)
+ rtx arg;
{
- rtx xops[4];
-
- if (TARGET_DEEP_BRANCH_PREDICTION)
- {
- xops[0] = pic_offset_table_rtx;
- if (pic_label_rtx == 0)
- {
- pic_label_rtx = gen_label_rtx ();
- ASM_GENERATE_INTERNAL_LABEL (pic_label_name, "LPR", pic_label_no++);
- LABEL_NAME (pic_label_rtx) = pic_label_name;
- }
-
- xops[1] = gen_rtx_MEM (QImode,
- gen_rtx (SYMBOL_REF, Pmode,
- LABEL_NAME (pic_label_rtx)));
-
- if (do_rtl)
- {
- emit_insn (gen_prologue_get_pc (xops[0], xops[1]));
- emit_insn (gen_prologue_set_got (xops[0],
-#ifdef YES_UNDERSCORES
- gen_rtx_SYMBOL_REF (Pmode,
- "$__GLOBAL_OFFSET_TABLE_"),
-#else
- gen_rtx_SYMBOL_REF (Pmode,
- "$_GLOBAL_OFFSET_TABLE_"),
-#endif
- xops[1]));
- }
- else
- {
- output_asm_insn (AS1 (call,%X1), xops);
- output_asm_insn ("addl $%__GLOBAL_OFFSET_TABLE_,%0", xops);
- pic_label_rtx = 0;
- }
- }
-
- else
- {
- xops[0] = pic_offset_table_rtx;
- xops[1] = gen_label_rtx ();
-
- if (do_rtl)
- {
- /* We can't put a raw CODE_LABEL into the RTL, and we can't emit
- a new CODE_LABEL after reload, so we need a single pattern to
- emit the 3 necessary instructions. */
- emit_insn (gen_prologue_get_pc_and_set_got (xops[0]));
- }
- else
- {
- output_asm_insn (AS1 (call,%P1), xops);
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
- CODE_LABEL_NUMBER (xops[1]));
- output_asm_insn (AS1 (pop%L0,%0), xops);
- output_asm_insn ("addl $%__GLOBAL_OFFSET_TABLE_+[.-%P1],%0", xops);
- }
- }
-
- /* When -fpic, we must emit a scheduling barrier, so that the instruction
- that restores %ebx (which is PIC_OFFSET_TABLE_REGNUM), does not get
- moved before any instruction which implicitly uses the got. */
-
- if (do_rtl)
- emit_insn (gen_blockage ());
+ return gen_rtx (SET, VOIDmode,
+ gen_rtx_MEM (SImode,
+ gen_rtx (PRE_DEC, SImode,
+ stack_pointer_rtx)),
+ arg);
}
/* Compute the size of local storage taking into consideration the
return size + padding;
}
-static void
-ix86_prologue (do_rtl)
- int do_rtl;
+/* Expand the prologue into a bunch of separate insns. */
+
+void
+ix86_expand_prologue ()
{
register int regno;
int limit;
- rtx xops[4];
int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
|| current_function_uses_const_pool);
HOST_WIDE_INT tsize = ix86_compute_frame_size (get_frame_size (), (int *)0);
rtx insn;
- int cfa_offset = INCOMING_FRAME_SP_OFFSET, cfa_store_offset = cfa_offset;
- xops[0] = stack_pointer_rtx;
- xops[1] = frame_pointer_rtx;
- xops[2] = GEN_INT (tsize);
+ /* Note: AT&T enter does NOT have reversed args. Enter is probably
+ slower on all targets. Also sdb doesn't like it. */
if (frame_pointer_needed)
{
- if (do_rtl)
- {
- insn = emit_insn (gen_rtx (SET, VOIDmode,
- gen_rtx_MEM (SImode,
- gen_rtx (PRE_DEC, SImode,
- stack_pointer_rtx)),
- frame_pointer_rtx));
-
- RTX_FRAME_RELATED_P (insn) = 1;
- insn = emit_move_insn (xops[1], xops[0]);
- RTX_FRAME_RELATED_P (insn) = 1;
- }
-
- else
- {
- output_asm_insn ("push%L1 %1", xops);
-#ifdef INCOMING_RETURN_ADDR_RTX
- if (dwarf2out_do_frame ())
- {
- char *l = dwarf2out_cfi_label ();
-
- cfa_store_offset += 4;
- cfa_offset = cfa_store_offset;
- dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset);
- dwarf2out_reg_save (l, FRAME_POINTER_REGNUM, - cfa_store_offset);
- }
-#endif
+ insn = emit_insn (gen_push (frame_pointer_rtx));
+ RTX_FRAME_RELATED_P (insn) = 1;
- output_asm_insn (AS2 (mov%L0,%0,%1), xops);
-#ifdef INCOMING_RETURN_ADDR_RTX
- if (dwarf2out_do_frame ())
- dwarf2out_def_cfa ("", FRAME_POINTER_REGNUM, cfa_offset);
-#endif
- }
+ insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;
}
if (tsize == 0)
;
else if (! TARGET_STACK_PROBE || tsize < CHECK_STACK_LIMIT)
{
- if (do_rtl)
- {
- insn = emit_insn (gen_prologue_set_stack_ptr (xops[2]));
- RTX_FRAME_RELATED_P (insn) = 1;
- }
+ if (frame_pointer_needed)
+ insn = emit_insn (gen_prologue_allocate_stack (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (-tsize),
+ frame_pointer_rtx));
else
- {
- output_asm_insn (AS2 (sub%L0,%2,%0), xops);
-#ifdef INCOMING_RETURN_ADDR_RTX
- if (dwarf2out_do_frame ())
- {
- cfa_store_offset += tsize;
- if (! frame_pointer_needed)
- {
- cfa_offset = cfa_store_offset;
- dwarf2out_def_cfa ("", STACK_POINTER_REGNUM, cfa_offset);
- }
- }
-#endif
- }
+ insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (-tsize)));
+ RTX_FRAME_RELATED_P (insn) = 1;
}
else
{
- xops[3] = gen_rtx_REG (SImode, 0);
- if (do_rtl)
- emit_move_insn (xops[3], xops[2]);
- else
- output_asm_insn (AS2 (mov%L0,%2,%3), xops);
+ /* ??? Is this only valid for Win32? */
- xops[3] = gen_rtx_MEM (FUNCTION_MODE,
- gen_rtx (SYMBOL_REF, Pmode, "_alloca"));
+ rtx arg0, sym;
- if (do_rtl)
- emit_call_insn (gen_rtx (CALL, VOIDmode, xops[3], const0_rtx));
- else
- output_asm_insn (AS1 (call,%P3), xops);
- }
+ arg0 = gen_rtx_REG (SImode, 0);
+ emit_move_insn (arg0, GEN_INT (tsize));
- /* Note If use enter it is NOT reversed args.
- This one is not reversed from intel!!
- I think enter is slower. Also sdb doesn't like it.
- But if you want it the code is:
- {
- xops[3] = const0_rtx;
- output_asm_insn ("enter %2,%3", xops);
- }
- */
+ sym = gen_rtx_MEM (FUNCTION_MODE,
+ gen_rtx_SYMBOL_REF (Pmode, "_alloca"));
+ insn = emit_call_insn (gen_call (sym, const0_rtx));
+
+ CALL_INSN_FUNCTION_USAGE (insn)
+ = gen_rtx_EXPR_LIST (VOIDmode, arg0, CALL_INSN_FUNCTION_USAGE (insn));
+ }
limit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
for (regno = limit - 1; regno >= 0; regno--)
if ((regs_ever_live[regno] && ! call_used_regs[regno])
|| (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
{
- xops[0] = gen_rtx_REG (SImode, regno);
- if (do_rtl)
- {
- insn = emit_insn (gen_rtx (SET, VOIDmode,
- gen_rtx_MEM (SImode,
- gen_rtx (PRE_DEC, SImode,
- stack_pointer_rtx)),
- xops[0]));
-
- RTX_FRAME_RELATED_P (insn) = 1;
- }
- else
- {
- output_asm_insn ("push%L0 %0", xops);
-#ifdef INCOMING_RETURN_ADDR_RTX
- if (dwarf2out_do_frame ())
- {
- char *l = dwarf2out_cfi_label ();
-
- cfa_store_offset += 4;
- if (! frame_pointer_needed)
- {
- cfa_offset = cfa_store_offset;
- dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset);
- }
-
- dwarf2out_reg_save (l, regno, - cfa_store_offset);
- }
-#endif
- }
+ insn = emit_insn (gen_push (gen_rtx_REG (SImode, regno)));
+ RTX_FRAME_RELATED_P (insn) = 1;
}
#ifdef SUBTARGET_PROLOGUE
#endif
if (pic_reg_used)
- load_pic_register (do_rtl);
+ load_pic_register ();
/* If we are profiling, make sure no instructions are scheduled before
the call to mcount. However, if -fpic, the above call will have
done that. */
- if ((profile_flag || profile_block_flag)
- && ! pic_reg_used && do_rtl)
+ if ((profile_flag || profile_block_flag) && ! pic_reg_used)
emit_insn (gen_blockage ());
}
-/* Return 1 if it is appropriate to emit `ret' instructions in the
- body of a function. Do this only if the epilogue is simple, needing a
- couple of insns. Prior to reloading, we can't tell how many registers
- must be saved, so return 0 then. Return 0 if there is no frame
- marker to de-allocate.
-
- If NON_SAVING_SETJMP is defined and true, then it is not possible
- for the epilogue to be simple, so return 0. This is a special case
- since NON_SAVING_SETJMP will not cause regs_ever_live to change
- until final, but jump_optimize may need to know sooner if a
- `return' is OK. */
-
-int
-ix86_can_use_return_insn_p ()
-{
- int regno;
- int nregs = 0;
- int reglimit = (frame_pointer_needed
- ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
- int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
- || current_function_uses_const_pool);
-
-#ifdef NON_SAVING_SETJMP
- if (NON_SAVING_SETJMP && current_function_calls_setjmp)
- return 0;
-#endif
-
- if (! reload_completed)
- return 0;
-
- for (regno = reglimit - 1; regno >= 0; regno--)
- if ((regs_ever_live[regno] && ! call_used_regs[regno])
- || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
- nregs++;
-
- return nregs == 0 || ! frame_pointer_needed;
-}
-
-/* This function generates the assembly code for function exit.
- FILE is an stdio stream to output the code to.
- SIZE is an int: how many units of temporary storage to deallocate. */
-
-void
-function_epilogue (file, size)
- FILE *file ATTRIBUTE_UNUSED;
- int size ATTRIBUTE_UNUSED;
-{
- return;
-}
-
/* Restore function stack, frame, and registers. */
void
ix86_expand_epilogue ()
-{
- ix86_epilogue (1);
-}
-
-static void
-ix86_epilogue (do_rtl)
- int do_rtl;
{
register int regno;
register int limit;
int nregs;
- rtx xops[3];
int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
|| current_function_uses_const_pool);
int sp_valid = !frame_pointer_needed || current_function_sp_is_unchanging;
HOST_WIDE_INT offset;
HOST_WIDE_INT tsize = ix86_compute_frame_size (get_frame_size (), &nregs);
- /* sp is often unreliable so we may have to go off the frame pointer. */
+ /* SP is often unreliable so we may have to go off the frame pointer. */
offset = -(tsize + nregs * UNITS_PER_WORD);
- xops[2] = stack_pointer_rtx;
-
- /* When -fpic, we must emit a scheduling barrier, so that the instruction
- that restores %ebx (which is PIC_OFFSET_TABLE_REGNUM), does not get
- moved before any instruction which implicitly uses the got. This
- includes any instruction which uses a SYMBOL_REF or a LABEL_REF.
-
- Alternatively, this could be fixed by making the dependence on the
- PIC_OFFSET_TABLE_REGNUM explicit in the RTL. */
-
- if (flag_pic || profile_flag || profile_block_flag)
- emit_insn (gen_blockage ());
-
/* If we're only restoring one register and sp is not valid then
using a move instruction to restore the register since it's
less work than reloading sp and popping the register. Otherwise,
restore sp (if necessary) and pop the registers. */
- limit = frame_pointer_needed
- ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM;
+ limit = (frame_pointer_needed
+ ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
if (nregs > 1 || sp_valid)
{
if ( !sp_valid )
{
- xops[0] = adj_offsettable_operand (AT_BP (QImode), offset);
- if (do_rtl)
- emit_insn (gen_movsi_lea (xops[2], XEXP (xops[0], 0)));
- else
- output_asm_insn (AS2 (lea%L2,%0,%2), xops);
+ rtx addr_offset;
+ addr_offset = adj_offsettable_operand (AT_BP (QImode), offset);
+ addr_offset = XEXP (addr_offset, 0);
+
+ emit_insn (gen_rtx_SET (VOIDmode, stack_pointer_rtx, addr_offset));
}
for (regno = 0; regno < limit; regno++)
if ((regs_ever_live[regno] && ! call_used_regs[regno])
|| (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
{
- xops[0] = gen_rtx_REG (SImode, regno);
-
- if (do_rtl)
- emit_insn (gen_pop (xops[0]));
- else
- output_asm_insn ("pop%L0 %0", xops);
+ emit_insn (gen_popsi1 (gen_rtx_REG (SImode, regno)));
}
}
-
else
- for (regno = 0; regno < limit; regno++)
- if ((regs_ever_live[regno] && ! call_used_regs[regno])
- || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
- {
- xops[0] = gen_rtx_REG (SImode, regno);
- xops[1] = adj_offsettable_operand (AT_BP (Pmode), offset);
-
- if (do_rtl)
- emit_move_insn (xops[0], xops[1]);
- else
- output_asm_insn (AS2 (mov%L0,%1,%0), xops);
-
- offset += 4;
- }
+ {
+ for (regno = 0; regno < limit; regno++)
+ if ((regs_ever_live[regno] && ! call_used_regs[regno])
+ || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
+ {
+ emit_move_insn (gen_rtx_REG (SImode, regno),
+ adj_offsettable_operand (AT_BP (Pmode), offset));
+ offset += 4;
+ }
+ }
if (frame_pointer_needed)
{
/* If not an i386, mov & pop is faster than "leave". */
-
if (TARGET_USE_LEAVE)
- {
- if (do_rtl)
- emit_insn (gen_leave());
- else
- output_asm_insn ("leave", xops);
- }
+ emit_insn (gen_leave());
else
{
- xops[0] = frame_pointer_rtx;
- xops[1] = stack_pointer_rtx;
-
- if (do_rtl)
- {
- emit_insn (gen_epilogue_set_stack_ptr());
- emit_insn (gen_pop (xops[0]));
- }
- else
- {
- output_asm_insn (AS2 (mov%L2,%0,%2), xops);
- output_asm_insn ("pop%L0 %0", xops);
- }
+ emit_insn (gen_epilogue_deallocate_stack (stack_pointer_rtx,
+ frame_pointer_rtx));
+ emit_insn (gen_popsi1 (frame_pointer_rtx));
}
}
-
else if (tsize)
{
/* Intel's docs say that for 4 or 8 bytes of stack frame one should
use `pop' and not `add'. */
int use_pop = tsize == 4;
+ rtx edx = 0, ecx;
/* Use two pops only for the Pentium processors. */
if (tsize == 8 && !TARGET_386 && !TARGET_486)
{
rtx retval = current_function_return_rtx;
- xops[1] = gen_rtx_REG (SImode, 1); /* %edx */
+ edx = gen_rtx_REG (SImode, 1);
/* This case is a bit more complex. Since we cannot pop into
%ecx twice we need a second register. But this is only
available if the return value is not of DImode in which
case the %edx register is not available. */
use_pop = (retval == NULL
- || ! reg_overlap_mentioned_p (xops[1], retval));
+ || ! reg_overlap_mentioned_p (edx, retval));
}
if (use_pop)
{
- xops[0] = gen_rtx_REG (SImode, 2); /* %ecx */
-
- if (do_rtl)
- {
- /* We have to prevent the two pops here from being scheduled.
- GCC otherwise would try in some situation to put other
- instructions in between them which has a bad effect. */
- emit_insn (gen_blockage ());
- emit_insn (gen_pop (xops[0]));
- if (tsize == 8)
- emit_insn (gen_pop (xops[1]));
- }
- else
- {
- output_asm_insn ("pop%L0 %0", xops);
- if (tsize == 8)
- output_asm_insn ("pop%L1 %1", xops);
- }
+ ecx = gen_rtx_REG (SImode, 2);
+
+ /* We have to prevent the two pops here from being scheduled.
+ GCC otherwise would try in some situation to put other
+ instructions in between them which has a bad effect. */
+ emit_insn (gen_blockage ());
+ emit_insn (gen_popsi1 (ecx));
+ if (tsize == 8)
+ emit_insn (gen_popsi1 (edx));
}
else
{
/* If there is no frame pointer, we must still release the frame. */
- xops[0] = GEN_INT (tsize);
-
- if (do_rtl)
- emit_insn (gen_rtx (SET, VOIDmode, xops[2],
- gen_rtx (PLUS, SImode, xops[2], xops[0])));
- else
- output_asm_insn (AS2 (add%L2,%0,%2), xops);
+ emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (tsize)));
}
}
#ifdef FUNCTION_BLOCK_PROFILER_EXIT
if (profile_block_flag == 2)
{
- FUNCTION_BLOCK_PROFILER_EXIT(file);
+ FUNCTION_BLOCK_PROFILER_EXIT;
}
#endif
if (current_function_pops_args && current_function_args_size)
{
- xops[1] = GEN_INT (current_function_pops_args);
+ rtx popc = GEN_INT (current_function_pops_args);
/* i386 can only pop 32K bytes (maybe 64K? Is it signed?). If
asked to pop more, pop return address, do explicit add, and jump
if (current_function_pops_args >= 32768)
{
- /* ??? Which register to use here? */
- xops[0] = gen_rtx_REG (SImode, 2);
+ rtx ecx = gen_rtx_REG (SImode, 2);
- if (do_rtl)
- {
- emit_insn (gen_pop (xops[0]));
- emit_insn (gen_rtx (SET, VOIDmode, xops[2],
- gen_rtx (PLUS, SImode, xops[1], xops[2])));
- emit_jump_insn (xops[0]);
- }
- else
- {
- output_asm_insn ("pop%L0 %0", xops);
- output_asm_insn (AS2 (add%L2,%1,%2), xops);
- output_asm_insn ("jmp %*%0", xops);
- }
+ emit_insn (gen_popsi1 (ecx));
+ emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, popc));
+ emit_indirect_jump (ecx);
}
else
+ emit_jump_insn (gen_return_pop_internal (popc));
+ }
+ else
+ emit_jump_insn (gen_return_internal ());
+}
+\f
+/* Extract the parts of an RTL expression that is a valid memory address
+ for an instruction. Return false if the structure of the address is
+ grossly off. */
+
+static int
+ix86_decompose_address (addr, out)
+ register rtx addr;
+ struct ix86_address *out;
+{
+ rtx base = NULL_RTX;
+ rtx index = NULL_RTX;
+ rtx disp = NULL_RTX;
+ HOST_WIDE_INT scale = 1;
+ rtx scale_rtx = NULL_RTX;
+
+ if (GET_CODE (addr) == REG || GET_CODE (addr) == SUBREG)
+ base = addr;
+ else if (GET_CODE (addr) == PLUS)
+ {
+ rtx op0 = XEXP (addr, 0);
+ rtx op1 = XEXP (addr, 1);
+ enum rtx_code code0 = GET_CODE (op0);
+ enum rtx_code code1 = GET_CODE (op1);
+
+ if (code0 == REG || code0 == SUBREG)
+ {
+ if (code1 == REG || code1 == SUBREG)
+ index = op0, base = op1; /* index + base */
+ else
+ base = op0, disp = op1; /* base + displacement */
+ }
+ else if (code0 == MULT)
{
- if (do_rtl)
- emit_jump_insn (gen_return_pop_internal (xops[1]));
+ index = XEXP (op0, 0);
+ scale_rtx = XEXP (op0, 1);
+ if (code1 == REG || code1 == SUBREG)
+ base = op1; /* index*scale + base */
else
- output_asm_insn ("ret %1", xops);
+ disp = op1; /* index*scale + disp */
+ }
+ else if (code0 == PLUS && GET_CODE (XEXP (op0, 0)) == MULT)
+ {
+ index = XEXP (XEXP (op0, 0), 0); /* index*scale + base + disp */
+ scale_rtx = XEXP (XEXP (op0, 0), 1);
+ base = XEXP (op0, 1);
+ disp = op1;
}
+ else if (code0 == PLUS)
+ {
+ index = XEXP (op0, 0); /* index + base + disp */
+ base = XEXP (op0, 1);
+ disp = op1;
+ }
+ else
+ return FALSE;
+ }
+ else if (GET_CODE (addr) == MULT)
+ {
+ index = XEXP (addr, 0); /* index*scale */
+ scale_rtx = XEXP (addr, 1);
+ }
+ else if (GET_CODE (addr) == ASHIFT)
+ {
+ rtx tmp;
+
+ /* We're called for lea too, which implements ashift on occasion. */
+ index = XEXP (addr, 0);
+ tmp = XEXP (addr, 1);
+ if (GET_CODE (tmp) != CONST_INT)
+ return FALSE;
+ scale = INTVAL (tmp);
+ if ((unsigned HOST_WIDE_INT) scale > 3)
+ return FALSE;
+ scale = 1 << scale;
}
else
+ disp = addr; /* displacement */
+
+ /* Extract the integral value of scale. */
+ if (scale_rtx)
{
- if (do_rtl)
- emit_jump_insn (gen_return_internal ());
- else
- output_asm_insn ("ret", xops);
+ if (GET_CODE (scale_rtx) != CONST_INT)
+ return FALSE;
+ scale = INTVAL (scale_rtx);
}
-}
-\f
-/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression
- that is a valid memory address for an instruction.
- The MODE argument is the machine mode for the MEM expression
- that wants to use this address.
-
- On x86, legitimate addresses are:
- base movl (base),reg
- displacement movl disp,reg
- base + displacement movl disp(base),reg
- index + base movl (base,index),reg
- (index + base) + displacement movl disp(base,index),reg
- index*scale movl (,index,scale),reg
- index*scale + disp movl disp(,index,scale),reg
- index*scale + base movl (base,index,scale),reg
- (index*scale + base) + disp movl disp(base,index,scale),reg
-
- In each case, scale can be 1, 2, 4, 8. */
-
-/* This is exactly the same as print_operand_addr, except that
- it recognizes addresses instead of printing them.
- It only recognizes address in canonical form. LEGITIMIZE_ADDRESS should
- convert common non-canonical forms to canonical form so that they will
- be recognized. */
+ /* Allow arg pointer and stack pointer as index if there is not scaling */
+ if (base && index && scale == 1
+ && (index == arg_pointer_rtx || index == stack_pointer_rtx))
+ {
+ rtx tmp = base;
+ base = index;
+ index = tmp;
+ }
+
+ /* Special case: %ebp cannot be encoded as a base without a displacement. */
+ if (base == frame_pointer_rtx && !disp)
+ disp = const0_rtx;
+
+ /* Special case: on K6, [%esi] makes the instruction vector decoded.
+ Avoid this by transforming to [%esi+0]. */
+ if (ix86_cpu == PROCESSOR_K6 && !optimize_size
+ && base && !index && !disp
+ && REGNO_REG_CLASS (REGNO (base)) == SIREG)
+ disp = const0_rtx;
+
+ /* Special case: encode reg+reg instead of reg*2. */
+ if (!base && index && scale && scale == 2)
+ base = index, scale = 1;
+
+ /* Special case: scaling cannot be encoded without base or displacement. */
+ if (!base && !disp && index && scale != 1)
+ disp = const0_rtx;
+
+ out->base = base;
+ out->index = index;
+ out->disp = disp;
+ out->scale = scale;
-#define ADDR_INVALID(msg,insn) \
-do { \
- if (TARGET_DEBUG_ADDR) \
- { \
- fprintf (stderr, msg); \
- debug_rtx (insn); \
- } \
-} while (0)
+ return TRUE;
+}
+/* Determine if a given CONST RTX is a valid memory displacement
+ in PIC mode. */
+
int
legitimate_pic_address_disp_p (disp)
register rtx disp;
return 1;
}
+/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression that is a valid
+ memory address for an instruction. The MODE argument is the machine mode
+ for the MEM expression that wants to use this address.
+
+ It only recognizes address in canonical form. LEGITIMIZE_ADDRESS should
+ convert common non-canonical forms to canonical form so that they will
+ be recognized. */
+
int
legitimate_address_p (mode, addr, strict)
enum machine_mode mode;
register rtx addr;
int strict;
{
- rtx base = NULL_RTX;
- rtx indx = NULL_RTX;
- rtx scale = NULL_RTX;
- rtx disp = NULL_RTX;
+ struct ix86_address parts;
+ rtx base, index, disp;
+ HOST_WIDE_INT scale;
+ const char *reason = NULL;
+ rtx reason_rtx = NULL_RTX;
if (TARGET_DEBUG_ADDR)
{
fprintf (stderr,
"\n======\nGO_IF_LEGITIMATE_ADDRESS, mode = %s, strict = %d\n",
GET_MODE_NAME (mode), strict);
-
debug_rtx (addr);
}
- if (GET_CODE (addr) == REG || GET_CODE (addr) == SUBREG)
- base = addr;
-
- else if (GET_CODE (addr) == PLUS)
- {
- rtx op0 = XEXP (addr, 0);
- rtx op1 = XEXP (addr, 1);
- enum rtx_code code0 = GET_CODE (op0);
- enum rtx_code code1 = GET_CODE (op1);
-
- if (code0 == REG || code0 == SUBREG)
- {
- if (code1 == REG || code1 == SUBREG)
- {
- indx = op0; /* index + base */
- base = op1;
- }
-
- else
- {
- base = op0; /* base + displacement */
- disp = op1;
- }
- }
-
- else if (code0 == MULT)
- {
- indx = XEXP (op0, 0);
- scale = XEXP (op0, 1);
-
- if (code1 == REG || code1 == SUBREG)
- base = op1; /* index*scale + base */
-
- else
- disp = op1; /* index*scale + disp */
- }
-
- else if (code0 == PLUS && GET_CODE (XEXP (op0, 0)) == MULT)
- {
- indx = XEXP (XEXP (op0, 0), 0); /* index*scale + base + disp */
- scale = XEXP (XEXP (op0, 0), 1);
- base = XEXP (op0, 1);
- disp = op1;
- }
-
- else if (code0 == PLUS)
- {
- indx = XEXP (op0, 0); /* index + base + disp */
- base = XEXP (op0, 1);
- disp = op1;
- }
-
- else
- {
- ADDR_INVALID ("PLUS subcode is not valid.\n", op0);
- return FALSE;
- }
- }
-
- else if (GET_CODE (addr) == MULT)
+ if (! ix86_decompose_address (addr, &parts))
{
- indx = XEXP (addr, 0); /* index*scale */
- scale = XEXP (addr, 1);
+ reason = "decomposition failed";
+ goto error;
}
- else
- disp = addr; /* displacement */
-
- /* Allow arg pointer and stack pointer as index if there is not scaling */
- if (base && indx && !scale
- && (indx == arg_pointer_rtx || indx == stack_pointer_rtx))
- {
- rtx tmp = base;
- base = indx;
- indx = tmp;
- }
+ base = parts.base;
+ index = parts.index;
+ disp = parts.disp;
+ scale = parts.scale;
- /* Validate base register:
+ /* Validate base register.
Don't allow SUBREG's here, it can lead to spill failures when the base
is one word out of a two word structure, which is represented internally
if (base)
{
+ reason_rtx = base;
+
if (GET_CODE (base) != REG)
{
- ADDR_INVALID ("Base is not a register.\n", base);
- return FALSE;
+ reason = "base is not a register";
+ goto error;
}
if (GET_MODE (base) != Pmode)
{
- ADDR_INVALID ("Base is not in Pmode.\n", base);
- return FALSE;
+ reason = "base is not in Pmode";
+ goto error;
}
if ((strict && ! REG_OK_FOR_BASE_STRICT_P (base))
|| (! strict && ! REG_OK_FOR_BASE_NONSTRICT_P (base)))
{
- ADDR_INVALID ("Base is not valid.\n", base);
- return FALSE;
+ reason = "base is not valid";
+ goto error;
}
}
- /* Validate index register:
+ /* Validate index register.
Don't allow SUBREG's here, it can lead to spill failures when the index
is one word out of a two word structure, which is represented internally
as a DImode int. */
- if (indx)
+
+ if (index)
{
- if (GET_CODE (indx) != REG)
+ reason_rtx = index;
+
+ if (GET_CODE (index) != REG)
{
- ADDR_INVALID ("Index is not a register.\n", indx);
- return FALSE;
+ reason = "index is not a register";
+ goto error;
}
- if (GET_MODE (indx) != Pmode)
+ if (GET_MODE (index) != Pmode)
{
- ADDR_INVALID ("Index is not in Pmode.\n", indx);
- return FALSE;
+ reason = "index is not in Pmode";
+ goto error;
}
- if ((strict && ! REG_OK_FOR_INDEX_STRICT_P (indx))
- || (! strict && ! REG_OK_FOR_INDEX_NONSTRICT_P (indx)))
+ if ((strict && ! REG_OK_FOR_INDEX_STRICT_P (index))
+ || (! strict && ! REG_OK_FOR_INDEX_NONSTRICT_P (index)))
{
- ADDR_INVALID ("Index is not valid.\n", indx);
- return FALSE;
+ reason = "index is not valid";
+ goto error;
}
}
- else if (scale)
- abort (); /* scale w/o index invalid */
- /* Validate scale factor: */
- if (scale)
+ /* Validate scale factor. */
+ if (scale != 1)
{
- HOST_WIDE_INT value;
-
- if (GET_CODE (scale) != CONST_INT)
+ reason_rtx = GEN_INT (scale);
+ if (!index)
{
- ADDR_INVALID ("Scale is not valid.\n", scale);
- return FALSE;
+ reason = "scale without index";
+ goto error;
}
- value = INTVAL (scale);
- if (value != 1 && value != 2 && value != 4 && value != 8)
+ if (scale != 2 && scale != 4 && scale != 8)
{
- ADDR_INVALID ("Scale is not a good multiplier.\n", scale);
- return FALSE;
+ reason = "scale is not a valid multiplier";
+ goto error;
}
}
/* Validate displacement. */
if (disp)
{
+ reason_rtx = disp;
+
if (!CONSTANT_ADDRESS_P (disp))
{
- ADDR_INVALID ("Displacement is not valid.\n", disp);
- return FALSE;
+ reason = "displacement is not constant";
+ goto error;
}
- else if (GET_CODE (disp) == CONST_DOUBLE)
+ if (GET_CODE (disp) == CONST_DOUBLE)
{
- ADDR_INVALID ("Displacement is a const_double.\n", disp);
- return FALSE;
+ reason = "displacement is a const_double";
+ goto error;
}
if (flag_pic && SYMBOLIC_CONST (disp))
{
if (! legitimate_pic_address_disp_p (disp))
{
- ADDR_INVALID ("Displacement is an invalid PIC construct.\n",
- disp);
- return FALSE;
+ reason = "displacement is an invalid pic construct";
+ goto error;
}
+ /* Verify that a symbolic pic displacement includes
+ the pic_offset_table_rtx register. */
if (base != pic_offset_table_rtx
- && (indx != pic_offset_table_rtx || scale != NULL_RTX))
+ && (index != pic_offset_table_rtx || scale != 1))
{
- ADDR_INVALID ("PIC displacement against invalid base.\n", disp);
- return FALSE;
+ reason = "pic displacement against invalid base";
+ goto error;
}
}
-
else if (HALF_PIC_P ())
{
if (! HALF_PIC_ADDRESS_P (disp)
- || (base != NULL_RTX || indx != NULL_RTX))
+ || (base != NULL_RTX || index != NULL_RTX))
{
- ADDR_INVALID ("Displacement is an invalid half-pic reference.\n",
- disp);
- return FALSE;
+ reason = "displacement is an invalid half-pic reference";
+ goto error;
}
}
}
+ /* Everything looks valid. */
if (TARGET_DEBUG_ADDR)
- fprintf (stderr, "Address is valid.\n");
-
- /* Everything looks valid, return true */
+ fprintf (stderr, "Success.\n");
return TRUE;
+
+error:
+ if (TARGET_DEBUG_ADDR)
+ {
+ fprintf (stderr, "Error: %s\n", reason);
+ debug_rtx (reason_rtx);
+ }
+ return FALSE;
}
\f
/* Return a legitimate reference for ORIG (an address) using the
return new;
}
\f
-/* Emit insns to move operands[1] into operands[0]. */
-
-void
-emit_pic_move (operands, mode)
- rtx *operands;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- rtx temp = reload_in_progress ? operands[0] : gen_reg_rtx (Pmode);
-
- if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1]))
- operands[1] = force_reg (Pmode, operands[1]);
- else
- operands[1] = legitimize_pic_address (operands[1], temp);
-}
-\f
/* Try machine-dependent ways of modifying an illegitimate address
to be legitimate. If we find one, return the new, valid address.
This macro is used in only one place: `memory_address' in explow.c.
if (GET_CODE (XEXP (x, 0)) == CONST_INT)
{
output_pic_addr_const (file, XEXP (x, 0), code);
- fprintf (file, "+");
+ putc ('+', file);
output_pic_addr_const (file, XEXP (x, 1), code);
}
else if (GET_CODE (XEXP (x, 1)) == CONST_INT)
{
output_pic_addr_const (file, XEXP (x, 1), code);
- fprintf (file, "+");
+ putc ('+', file);
output_pic_addr_const (file, XEXP (x, 0), code);
}
else
break;
case MINUS:
+ putc (ASSEMBLER_DIALECT ? '(' : '[', file);
output_pic_addr_const (file, XEXP (x, 0), code);
- fprintf (file, "-");
+ putc ('-', file);
output_pic_addr_const (file, XEXP (x, 1), code);
+ putc (ASSEMBLER_DIALECT ? ')' : ']', file);
break;
case UNSPEC:
}
\f
static void
-put_jump_code (code, reverse, file)
+put_condition_code (code, mode, reverse, fp, file)
enum rtx_code code;
- int reverse;
+ enum machine_mode mode;
+ int reverse, fp;
FILE *file;
{
- int flags = cc_prev_status.flags;
- int ieee = (TARGET_IEEE_FP && (flags & CC_IN_80387)
- && !(cc_prev_status.flags & CC_FCOMI));
const char *suffix;
- if (flags & CC_Z_IN_NOT_C)
- switch (code)
- {
- case EQ:
- fputs (reverse ? "c" : "nc", file);
- return;
-
- case NE:
- fputs (reverse ? "nc" : "c", file);
- return;
-
- default:
- abort ();
- }
- if (ieee)
- {
- switch (code)
- {
- case LE:
- suffix = reverse ? "ae" : "b";
- break;
- case GT:
- case LT:
- case GE:
- suffix = reverse ? "ne" : "e";
- break;
- case EQ:
- suffix = reverse ? "ne" : "e";
- break;
- case NE:
- suffix = reverse ? "e" : "ne";
- break;
- default:
- abort ();
- }
- fputs (suffix, file);
- return;
- }
- if (flags & CC_TEST_AX)
- abort();
- if ((flags & CC_NO_OVERFLOW) && (code == LE || code == GT))
- abort ();
if (reverse)
code = reverse_condition (code);
+
switch (code)
{
case EQ:
suffix = "e";
break;
-
case NE:
suffix = "ne";
break;
-
case GT:
- suffix = flags & CC_IN_80387 ? "a" : "g";
+ if (mode == CCNOmode)
+ abort ();
+ suffix = "g";
break;
-
case GTU:
- suffix = "a";
+ /* ??? Use "nbe" instead of "a" for fcmov losage on some assemblers.
+ Those same assemblers have the same but opposite losage on cmov. */
+ suffix = fp ? "nbe" : "a";
break;
-
case LT:
- if (flags & CC_NO_OVERFLOW)
+ if (mode == CCNOmode)
suffix = "s";
else
- suffix = flags & CC_IN_80387 ? "b" : "l";
+ suffix = "l";
break;
-
case LTU:
suffix = "b";
break;
-
case GE:
- if (flags & CC_NO_OVERFLOW)
+ if (mode == CCNOmode)
suffix = "ns";
else
- suffix = flags & CC_IN_80387 ? "ae" : "ge";
+ suffix = "ge";
break;
-
case GEU:
- suffix = "ae";
+ /* ??? As above. */
+ suffix = fp ? "nb" : "ae";
break;
-
case LE:
- suffix = flags & CC_IN_80387 ? "be" : "le";
+ if (mode == CCNOmode)
+ abort ();
+ suffix = "le";
break;
-
case LEU:
suffix = "be";
break;
-
default:
abort ();
}
fputs (suffix, file);
}
-/* Append the correct conditional move suffix which corresponds to CODE. */
-
-static void
-put_condition_code (code, reverse_cc, mode, file)
- enum rtx_code code;
- int reverse_cc;
- enum mode_class mode;
- FILE * file;
+void
+print_reg (x, code, file)
+ rtx x;
+ int code;
+ FILE *file;
{
- int ieee = (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)
- && ! (cc_prev_status.flags & CC_FCOMI));
- if (reverse_cc && ! ieee)
- code = reverse_condition (code);
-
- if (mode == MODE_INT)
- switch (code)
- {
- case NE:
- if (cc_prev_status.flags & CC_Z_IN_NOT_C)
- fputs ("b", file);
- else
- fputs ("ne", file);
- return;
-
- case EQ:
- if (cc_prev_status.flags & CC_Z_IN_NOT_C)
- fputs ("ae", file);
- else
- fputs ("e", file);
- return;
-
- case GE:
- if (cc_prev_status.flags & CC_NO_OVERFLOW)
- fputs ("ns", file);
- else
- fputs ("ge", file);
- return;
-
- case GT:
- fputs ("g", file);
- return;
-
- case LE:
- fputs ("le", file);
- return;
-
- case LT:
- if (cc_prev_status.flags & CC_NO_OVERFLOW)
- fputs ("s", file);
- else
- fputs ("l", file);
- return;
-
- case GEU:
- fputs ("ae", file);
- return;
-
- case GTU:
- fputs ("a", file);
- return;
-
- case LEU:
- fputs ("be", file);
- return;
-
- case LTU:
- fputs ("b", file);
- return;
+ if (REGNO (x) == ARG_POINTER_REGNUM
+ || REGNO (x) == FLAGS_REG
+ || REGNO (x) == FPSR_REG)
+ abort ();
- default:
- output_operand_lossage ("Invalid %%C operand");
- }
+ if (ASSEMBLER_DIALECT == 0 || USER_LABEL_PREFIX[0] == 0)
+ putc ('%', file);
+
+ if (code == 'w')
+ code = 2;
+ else if (code == 'b')
+ code = 1;
+ else if (code == 'k')
+ code = 4;
+ else if (code == 'y')
+ code = 3;
+ else if (code == 'h')
+ code = 0;
+ else
+ code = GET_MODE_SIZE (GET_MODE (x));
- else if (mode == MODE_FLOAT)
- switch (code)
- {
- case NE:
- fputs (ieee ? (reverse_cc ? "ne" : "e") : "ne", file);
- return;
- case EQ:
- fputs (ieee ? (reverse_cc ? "ne" : "e") : "e", file);
- return;
- case GE:
- fputs (ieee ? (reverse_cc ? "ne" : "e") : "nb", file);
- return;
- case GT:
- fputs (ieee ? (reverse_cc ? "ne" : "e") : "nbe", file);
- return;
- case LE:
- fputs (ieee ? (reverse_cc ? "nb" : "b") : "be", file);
- return;
- case LT:
- fputs (ieee ? (reverse_cc ? "ne" : "e") : "b", file);
- return;
- case GEU:
- fputs (ieee ? (reverse_cc ? "ne" : "e") : "nb", file);
- return;
- case GTU:
- fputs (ieee ? (reverse_cc ? "ne" : "e") : "nbe", file);
- return;
- case LEU:
- fputs (ieee ? (reverse_cc ? "nb" : "b") : "be", file);
- return;
- case LTU:
- fputs (ieee ? (reverse_cc ? "ne" : "e") : "b", file);
- return;
- default:
- output_operand_lossage ("Invalid %%C operand");
+ switch (code)
+ {
+ case 3:
+ if (STACK_TOP_P (x))
+ {
+ fputs ("st(0)", file);
+ break;
+ }
+ /* FALLTHRU */
+ case 4:
+ case 8:
+ case 12:
+ if (! FP_REG_P (x))
+ putc ('e', file);
+ /* FALLTHRU */
+ case 2:
+ fputs (hi_reg_name[REGNO (x)], file);
+ break;
+ case 1:
+ fputs (qi_reg_name[REGNO (x)], file);
+ break;
+ case 0:
+ fputs (qi_high_reg_name[REGNO (x)], file);
+ break;
+ default:
+ abort ();
}
}
L,W,B,Q,S,T -- print the opcode suffix for specified size of operand.
C -- print opcode suffix for set/cmov insn.
c -- like C, but print reversed condition
- F -- print opcode suffix for fcmov insn.
- f -- like F, but print reversed condition
- D -- print the opcode suffix for a jump
- d -- like D, but print reversed condition
R -- print the prefix for register names.
z -- print the opcode suffix for the size of the current operand.
* -- print a star (in certain assembler syntax)
w -- print the operand as if it's a "word" (HImode) even if it isn't.
- J -- print the appropriate jump operand.
s -- print a shift double count, followed by the assemblers argument
delimiter.
b -- print the QImode name of the register for the indicated operand.
w -- likewise, print the HImode name of the register.
k -- likewise, print the SImode name of the register.
h -- print the QImode name for a "high" register, either ah, bh, ch or dh.
- y -- print "st(0)" instead of "st" as a register.
- P -- print as a PIC constant
- _ -- output "_" if YES_UNDERSCORES */
+ y -- print "st(0)" instead of "st" as a register. */
void
print_operand (file, x, code)
switch (code)
{
case '*':
- if (USE_STAR)
+ if (ASSEMBLER_DIALECT == 0)
putc ('*', file);
return;
- case '_':
-#ifdef YES_UNDERSCORES
- putc ('_', file);
-#endif
- return;
-
case 'L':
- PUT_OP_SIZE (code, 'l', file);
+ if (ASSEMBLER_DIALECT == 0)
+ putc ('l', file);
return;
case 'W':
- PUT_OP_SIZE (code, 'w', file);
+ if (ASSEMBLER_DIALECT == 0)
+ putc ('w', file);
return;
case 'B':
- PUT_OP_SIZE (code, 'b', file);
+ if (ASSEMBLER_DIALECT == 0)
+ putc ('b', file);
return;
case 'Q':
- PUT_OP_SIZE (code, 'l', file);
+ if (ASSEMBLER_DIALECT == 0)
+ putc ('l', file);
return;
case 'S':
- PUT_OP_SIZE (code, 's', file);
+ if (ASSEMBLER_DIALECT == 0)
+ putc ('s', file);
return;
case 'T':
- PUT_OP_SIZE (code, 't', file);
+ if (ASSEMBLER_DIALECT == 0)
+ putc ('t', file);
return;
case 'z':
if (STACK_REG_P (x))
return;
+ /* Intel syntax has no truck with instruction suffixes. */
+ if (ASSEMBLER_DIALECT != 0)
+ return;
+
/* this is the size of op from size of operand */
switch (GET_MODE_SIZE (GET_MODE (x)))
{
+ case 1:
+ putc ('b', file);
+ return;
+
case 2:
-#ifdef HAVE_GAS_FILDS_FISTS
- PUT_OP_SIZE ('W', 's', file);
-#endif
+ putc ('w', file);
return;
case 4:
if (GET_MODE (x) == SFmode)
{
- PUT_OP_SIZE ('S', 's', file);
+ putc ('s', file);
return;
}
else
- PUT_OP_SIZE ('L', 'l', file);
+ putc ('l', file);
return;
case 12:
- PUT_OP_SIZE ('T', 't', file);
- return;
+ putc ('t', file);
+ return;
case 8:
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT)
{
#ifdef GAS_MNEMONICS
- PUT_OP_SIZE ('Q', 'q', file);
- return;
+ putc ('q', file);
#else
- PUT_OP_SIZE ('Q', 'l', file); /* Fall through */
+ putc ('l', file);
+ putc ('l', file);
#endif
}
-
- PUT_OP_SIZE ('Q', 'l', file);
+ else
+ putc ('l', file);
return;
-
- default:
- abort ();
}
case 'b':
case 'k':
case 'h':
case 'y':
- case 'P':
case 'X':
+ case 'P':
break;
- case 'J':
- switch (GET_CODE (x))
- {
- /* These conditions are appropriate for testing the result
- of an arithmetic operation, not for a compare operation.
- Cases GE, LT assume CC_NO_OVERFLOW true. All cases assume
- CC_Z_IN_NOT_C false and not floating point. */
- case NE: fputs ("jne", file); return;
- case EQ: fputs ("je", file); return;
- case GE: fputs ("jns", file); return;
- case LT: fputs ("js", file); return;
- case GEU: fputs ("jmp", file); return;
- case GTU: fputs ("jne", file); return;
- case LEU: fputs ("je", file); return;
- case LTU: fputs ("#branch never", file); return;
-
- /* no matching branches for GT nor LE */
-
- default:
- abort ();
- }
-
case 's':
if (GET_CODE (x) == CONST_INT || ! SHIFT_DOUBLE_OMITS_COUNT)
{
PRINT_OPERAND (file, x, 0);
- fputs (AS2C (,) + 1, file);
+ putc (',', file);
}
-
- return;
-
- case 'D':
- put_jump_code (GET_CODE (x), 0, file);
- return;
-
- case 'd':
- put_jump_code (GET_CODE (x), 1, file);
return;
- /* This is used by the conditional move instructions. */
case 'C':
- put_condition_code (GET_CODE (x), 0, MODE_INT, file);
+ put_condition_code (GET_CODE (x), GET_MODE (XEXP (x, 0)), 0, 0, file);
return;
-
- /* Like above, but reverse condition */
- case 'c':
- put_condition_code (GET_CODE (x), 1, MODE_INT, file); return;
-
case 'F':
- put_condition_code (GET_CODE (x), 0, MODE_FLOAT, file);
+ put_condition_code (GET_CODE (x), GET_MODE (XEXP (x, 0)), 0, 1, file);
return;
/* Like above, but reverse condition */
+ case 'c':
+ put_condition_code (GET_CODE (x), GET_MODE (XEXP (x, 0)), 1, 0, file);
+ return;
case 'f':
- put_condition_code (GET_CODE (x), 1, MODE_FLOAT, file);
+ put_condition_code (GET_CODE (x), GET_MODE (XEXP (x, 0)), 1, 1, file);
return;
default:
{
char str[50];
-
sprintf (str, "invalid operand code `%c'", code);
output_operand_lossage (str);
}
else if (GET_CODE (x) == MEM)
{
- PRINT_PTR (x, file);
- if (CONSTANT_ADDRESS_P (XEXP (x, 0)))
+ /* No `byte ptr' prefix for call instructions. */
+ if (ASSEMBLER_DIALECT != 0 && code != 'X' && code != 'P')
{
- if (flag_pic)
- output_pic_addr_const (file, XEXP (x, 0), code);
- else
- output_addr_const (file, XEXP (x, 0));
+ char * size;
+ switch (GET_MODE_SIZE (GET_MODE (x)))
+ {
+ case 1: size = "BYTE"; break;
+ case 2: size = "WORD"; break;
+ case 4: size = "DWORD"; break;
+ case 8: size = "QWORD"; break;
+ case 12: size = "XWORD"; break;
+ default:
+ abort();
+ }
+ fputs (size, file);
+ fputs (" PTR ", file);
}
+
+ x = XEXP (x, 0);
+ if (flag_pic && CONSTANT_ADDRESS_P (x))
+ output_pic_addr_const (file, x, code);
else
- output_address (XEXP (x, 0));
+ output_address (x);
}
else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == SFmode)
REAL_VALUE_FROM_CONST_DOUBLE (r, x);
REAL_VALUE_TO_TARGET_SINGLE (r, l);
- PRINT_IMMED_PREFIX (file);
+
+ if (ASSEMBLER_DIALECT == 0)
+ putc ('$', file);
fprintf (file, "0x%lx", l);
}
if (code != 'P')
{
if (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE)
- PRINT_IMMED_PREFIX (file);
+ {
+ if (ASSEMBLER_DIALECT == 0)
+ putc ('$', file);
+ }
else if (GET_CODE (x) == CONST || GET_CODE (x) == SYMBOL_REF
|| GET_CODE (x) == LABEL_REF)
- PRINT_OFFSET_PREFIX (file);
+ {
+ if (ASSEMBLER_DIALECT == 0)
+ putc ('$', file);
+ else
+ fputs ("OFFSET FLAT:", file);
+ }
}
- if (flag_pic)
+ if (GET_CODE (x) == CONST_INT)
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x));
+ else if (flag_pic)
output_pic_addr_const (file, x, code);
else
output_addr_const (file, x);
FILE *file;
register rtx addr;
{
- register rtx reg1, reg2, breg, ireg;
- rtx offset;
-
- switch (GET_CODE (addr))
- {
- case REG:
- /* ESI addressing makes instruction vector decoded on the K6. We can
- avoid this by ESI+0 addressing. */
- if (REGNO_REG_CLASS (REGNO (addr)) == SIREG
- && ix86_cpu == PROCESSOR_K6 && !optimize_size)
- output_addr_const (file, const0_rtx);
- ADDR_BEG (file);
- fprintf (file, "%se", RP);
- fputs (hi_reg_name[REGNO (addr)], file);
- ADDR_END (file);
- break;
-
- case PLUS:
- reg1 = 0;
- reg2 = 0;
- ireg = 0;
- breg = 0;
- offset = 0;
- if (CONSTANT_ADDRESS_P (XEXP (addr, 0)))
- {
- offset = XEXP (addr, 0);
- addr = XEXP (addr, 1);
- }
- else if (CONSTANT_ADDRESS_P (XEXP (addr, 1)))
- {
- offset = XEXP (addr, 1);
- addr = XEXP (addr, 0);
- }
+ struct ix86_address parts;
+ rtx base, index, disp;
+ int scale;
- if (GET_CODE (addr) != PLUS)
- ;
- else if (GET_CODE (XEXP (addr, 0)) == MULT)
- reg1 = XEXP (addr, 0), addr = XEXP (addr, 1);
- else if (GET_CODE (XEXP (addr, 1)) == MULT)
- reg1 = XEXP (addr, 1), addr = XEXP (addr, 0);
- else if (GET_CODE (XEXP (addr, 0)) == REG)
- reg1 = XEXP (addr, 0), addr = XEXP (addr, 1);
- else if (GET_CODE (XEXP (addr, 1)) == REG)
- reg1 = XEXP (addr, 1), addr = XEXP (addr, 0);
-
- if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT)
- {
- if (reg1 == 0)
- reg1 = addr;
- else
- reg2 = addr;
+ if (! ix86_decompose_address (addr, &parts))
+ abort ();
- addr = 0;
- }
+ base = parts.base;
+ index = parts.index;
+ disp = parts.disp;
+ scale = parts.scale;
- if (offset != 0)
- {
- if (addr != 0)
- abort ();
- addr = offset;
- }
+ if (!base && !index)
+ {
+ /* Displacement only requires special attention. */
- if ((reg1 && GET_CODE (reg1) == MULT)
- || (reg2 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg2))))
- {
- breg = reg2;
- ireg = reg1;
- }
- else if (reg1 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg1)))
+ if (GET_CODE (disp) == CONST_INT)
{
- breg = reg1;
- ireg = reg2;
+ if (ASSEMBLER_DIALECT != 0)
+ fputs ("ds:", file);
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (addr));
}
-
- if (ireg != 0 || breg != 0)
+ else if (flag_pic)
+ output_pic_addr_const (file, addr, 0);
+ else
+ output_addr_const (file, addr);
+ }
+ else
+ {
+ if (ASSEMBLER_DIALECT == 0)
{
- int scale = 1;
-
- if (addr != 0)
+ if (disp)
{
if (flag_pic)
- output_pic_addr_const (file, addr, 0);
- else if (GET_CODE (addr) == LABEL_REF)
- output_asm_label (addr);
+ output_pic_addr_const (file, disp, 0);
+ else if (GET_CODE (disp) == LABEL_REF)
+ output_asm_label (disp);
else
- output_addr_const (file, addr);
- }
-
- if (ireg != 0 && GET_CODE (ireg) == MULT)
- {
- scale = INTVAL (XEXP (ireg, 1));
- ireg = XEXP (ireg, 0);
+ output_addr_const (file, disp);
}
- /* The stack pointer can only appear as a base register,
- never an index register, so exchange the regs if it is wrong. */
-
- if (scale == 1 && ireg && REGNO (ireg) == STACK_POINTER_REGNUM)
+ putc ('(', file);
+ if (base)
+ PRINT_REG (base, 0, file);
+ if (index)
{
- rtx tmp;
-
- tmp = breg;
- breg = ireg;
- ireg = tmp;
+ putc (',', file);
+ PRINT_REG (index, 0, file);
+ if (scale != 1)
+ fprintf (file, ",%d", scale);
}
-
- /* output breg+ireg*scale */
- PRINT_B_I_S (breg, ireg, scale, file);
- break;
+ putc (')', file);
}
-
- case MULT:
- {
- int scale;
-
- if (GET_CODE (XEXP (addr, 0)) == CONST_INT)
- {
- scale = INTVAL (XEXP (addr, 0));
- ireg = XEXP (addr, 1);
- }
- else
- {
- scale = INTVAL (XEXP (addr, 1));
- ireg = XEXP (addr, 0);
- }
-
- /* (reg,reg,) is shorter than (,reg,2). */
- if (scale == 2)
- {
- PRINT_B_I_S (ireg, ireg, 1, file);
- }
- else
- {
- output_addr_const (file, const0_rtx);
- PRINT_B_I_S (NULL_RTX, ireg, scale, file);
- }
- }
- break;
-
- default:
- if (GET_CODE (addr) == CONST_INT
- && INTVAL (addr) < 0x8000
- && INTVAL (addr) >= -0x8000)
- fprintf (file, "%d", (int) INTVAL (addr));
else
{
- if (flag_pic)
- output_pic_addr_const (file, addr, 0);
- else
- output_addr_const (file, addr);
- }
- }
-}
-\f
-/* Set the cc_status for the results of an insn whose pattern is EXP.
- On the 80386, we assume that only test and compare insns, as well
- as SI, HI, & DI mode ADD, SUB, NEG, AND, IOR, XOR, BSF, ASHIFT,
- ASHIFTRT, and LSHIFTRT instructions set the condition codes usefully.
- Also, we assume that jumps, moves and sCOND don't affect the condition
- codes. All else clobbers the condition codes, by assumption.
-
- We assume that ALL integer add, minus, etc. instructions effect the
- condition codes. This MUST be consistent with i386.md.
-
- We don't record any float test or compare - the redundant test &
- compare check in final.c does not handle stack-like regs correctly. */
-
-void
-notice_update_cc (exp)
- rtx exp;
-{
- if (GET_CODE (exp) == SET)
- {
- /* Jumps do not alter the cc's. */
- if (SET_DEST (exp) == pc_rtx)
- return;
-
- /* Moving register or memory into a register:
- it doesn't alter the cc's, but it might invalidate
- the RTX's which we remember the cc's came from.
- (Note that moving a constant 0 or 1 MAY set the cc's). */
- if (REG_P (SET_DEST (exp))
- && (REG_P (SET_SRC (exp)) || GET_CODE (SET_SRC (exp)) == MEM
- || GET_RTX_CLASS (GET_CODE (SET_SRC (exp))) == '<'
- || GET_CODE (SET_SRC (exp)) == IF_THEN_ELSE))
- {
- if (cc_status.value1
- && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1))
- cc_status.value1 = 0;
-
- if (cc_status.value2
- && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2))
- cc_status.value2 = 0;
-
- return;
- }
-
- /* Moving register into memory doesn't alter the cc's.
- It may invalidate the RTX's which we remember the cc's came from. */
- if (GET_CODE (SET_DEST (exp)) == MEM
- && (REG_P (SET_SRC (exp))
- || GET_RTX_CLASS (GET_CODE (SET_SRC (exp))) == '<'))
- {
- if (cc_status.value1
- && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1))
- cc_status.value1 = 0;
- if (cc_status.value2
- && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2))
- cc_status.value2 = 0;
-
- return;
- }
-
- /* Function calls clobber the cc's. */
- else if (GET_CODE (SET_SRC (exp)) == CALL)
- {
- CC_STATUS_INIT;
- return;
- }
-
- /* Tests and compares set the cc's in predictable ways. */
- else if (SET_DEST (exp) == cc0_rtx)
- {
- CC_STATUS_INIT;
- cc_status.value1 = SET_SRC (exp);
- return;
- }
+ rtx offset = NULL_RTX;
- /* Certain instructions effect the condition codes. */
- else if (GET_MODE (SET_SRC (exp)) == SImode
- || GET_MODE (SET_SRC (exp)) == HImode
- || GET_MODE (SET_SRC (exp)) == QImode)
- switch (GET_CODE (SET_SRC (exp)))
- {
- case ASHIFTRT: case LSHIFTRT: case ASHIFT:
- /* Shifts on the 386 don't set the condition codes if the
- shift count is zero. */
- if (GET_CODE (XEXP (SET_SRC (exp), 1)) != CONST_INT)
- {
- CC_STATUS_INIT;
- break;
- }
-
- /* We assume that the CONST_INT is non-zero (this rtx would
- have been deleted if it were zero. */
-
- case PLUS: case MINUS: case NEG:
- case AND: case IOR: case XOR:
- cc_status.flags = CC_NO_OVERFLOW;
- cc_status.value1 = SET_SRC (exp);
- cc_status.value2 = SET_DEST (exp);
- break;
-
- /* This is the bsf pattern used by ffs. */
- case UNSPEC:
- if (XINT (SET_SRC (exp), 1) == 5)
- {
- /* Only the Z flag is defined after bsf. */
- cc_status.flags
- = CC_NOT_POSITIVE | CC_NOT_NEGATIVE | CC_NO_OVERFLOW;
- cc_status.value1 = XVECEXP (SET_SRC (exp), 0, 0);
- cc_status.value2 = 0;
- break;
- }
- /* FALLTHRU */
+ if (disp)
+ {
+ /* Pull out the offset of a symbol; print any symbol itself. */
+ if (GET_CODE (disp) == CONST
+ && GET_CODE (XEXP (disp, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (disp, 0), 1)) == CONST_INT)
+ {
+ offset = XEXP (XEXP (disp, 0), 1);
+ disp = gen_rtx_CONST (VOIDmode,
+ XEXP (XEXP (disp, 0), 0));
+ }
- default:
- CC_STATUS_INIT;
- }
- else
- {
- CC_STATUS_INIT;
- }
- }
- else if (GET_CODE (exp) == PARALLEL
- && GET_CODE (XVECEXP (exp, 0, 0)) == SET)
- {
- if (SET_DEST (XVECEXP (exp, 0, 0)) == pc_rtx)
- return;
- if (SET_DEST (XVECEXP (exp, 0, 0)) == cc0_rtx)
+ if (flag_pic)
+ output_pic_addr_const (file, disp, 0);
+ else if (GET_CODE (disp) == LABEL_REF)
+ output_asm_label (disp);
+ else if (GET_CODE (disp) == CONST_INT)
+ offset = disp;
+ else
+ output_addr_const (file, disp);
+ }
- {
- CC_STATUS_INIT;
- if (stack_regs_mentioned_p (SET_SRC (XVECEXP (exp, 0, 0))))
+ putc ('[', file);
+ if (base)
{
- cc_status.flags |= CC_IN_80387;
- if (TARGET_CMOVE && stack_regs_mentioned_p
- (XEXP (SET_SRC (XVECEXP (exp, 0, 0)), 1)))
- cc_status.flags |= CC_FCOMI;
+ PRINT_REG (base, 0, file);
+ if (offset)
+ {
+ if (INTVAL (offset) >= 0)
+ putc ('+', file);
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (offset));
+ }
}
+ else if (offset)
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (offset));
else
- cc_status.value1 = SET_SRC (XVECEXP (exp, 0, 0));
- return;
- }
+ putc ('0', file);
- CC_STATUS_INIT;
- }
- else
- {
- CC_STATUS_INIT;
+ if (index)
+ {
+ putc ('+', file);
+ PRINT_REG (index, 0, file);
+ if (scale != 1)
+ fprintf (file, "*%d", scale);
+ }
+ putc (']', file);
+ }
}
}
\f
while (num--)
{
rtx op = operands[num];
- if (! reload_completed)
+ if (CONSTANT_P (op))
+ split_double (op, &lo_half[num], &hi_half[num]);
+ else if (! reload_completed)
{
lo_half[num] = gen_lowpart (SImode, op);
hi_half[num] = gen_highpart (SImode, op);
lo_half[num] = gen_rtx_REG (SImode, REGNO (op));
hi_half[num] = gen_rtx_REG (SImode, REGNO (op) + 1);
}
- else if (CONSTANT_P (op))
- split_double (op, &lo_half[num], &hi_half[num]);
else if (offsettable_memref_p (op))
{
rtx lo_addr = XEXP (op, 0);
}
}
\f
-/* Return 1 if this is a valid binary operation on a 387.
- OP is the expression matched, and MODE is its mode. */
-
-int
-binary_387_op (op, mode)
- register rtx op;
- enum machine_mode mode;
-{
- if (mode != VOIDmode && mode != GET_MODE (op))
- return 0;
-
- switch (GET_CODE (op))
- {
- case PLUS:
- case MINUS:
- case MULT:
- case DIV:
- return GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT;
-
- default:
- return 0;
- }
-}
-\f
-/* Return 1 if this is a valid shift or rotate operation on a 386.
- OP is the expression matched, and MODE is its mode. */
-
-int
-shift_op (op, mode)
- register rtx op;
- enum machine_mode mode;
-{
- rtx operand = XEXP (op, 0);
-
- if (mode != VOIDmode && mode != GET_MODE (op))
- return 0;
-
- if (GET_MODE (operand) != GET_MODE (op)
- || GET_MODE_CLASS (GET_MODE (op)) != MODE_INT)
- return 0;
-
- return (GET_CODE (op) == ASHIFT
- || GET_CODE (op) == ASHIFTRT
- || GET_CODE (op) == LSHIFTRT
- || GET_CODE (op) == ROTATE
- || GET_CODE (op) == ROTATERT);
-}
-
-/* Return 1 if OP is COMPARE rtx with mode VOIDmode.
- MODE is not used. */
-
-int
-VOIDmode_compare_op (op, mode)
- register rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return GET_CODE (op) == COMPARE && GET_MODE (op) == VOIDmode;
-}
-\f
/* Output code to perform a 387 binary operation in INSN, one of PLUS,
MINUS, MULT or DIV. OPERANDS are the insn operands, where operands[3]
is the expression of the binary operation. The output may either be
rtx insn;
rtx *operands;
{
- rtx temp;
- char *base_op;
static char buf[100];
+ rtx temp;
+ char *p;
switch (GET_CODE (operands[3]))
{
case PLUS:
- base_op = "fadd";
+ if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
+ || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
+ p = "fiadd";
+ else
+ p = "fadd";
break;
case MINUS:
- base_op = "fsub";
+ if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
+ || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
+ p = "fisub";
+ else
+ p = "fsub";
break;
case MULT:
- base_op = "fmul";
+ if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
+ || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
+ p = "fimul";
+ else
+ p = "fmul";
break;
case DIV:
- base_op = "fdiv";
+ if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
+ || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
+ p = "fidiv";
+ else
+ p = "fdiv";
break;
default:
abort ();
}
- strcpy (buf, base_op);
+ strcpy (buf, p);
switch (GET_CODE (operands[3]))
{
}
if (GET_CODE (operands[2]) == MEM)
- return strcat (buf, AS1 (%z2,%2));
-
- if (! STACK_REG_P (operands[1]) || ! STACK_REG_P (operands[2]))
- abort ();
+ {
+ p = "%z2\t%2";
+ break;
+ }
if (find_regno_note (insn, REG_DEAD, REGNO (operands[2])))
{
if (STACK_TOP_P (operands[0]))
- return strcat (buf, AS2 (p,%0,%2));
+ p = "p\t{%0,%2|%2, %0}";
else
- return strcat (buf, AS2 (p,%2,%0));
+ p = "p\t{%2,%0|%0, %2}";
+ break;
}
if (STACK_TOP_P (operands[0]))
- return strcat (buf, AS2C (%y2,%0));
+ p = "\t{%y2,%0|%0, %y2}";
else
- return strcat (buf, AS2C (%2,%0));
+ p = "\t{%2,%0|%0, %2}";
+ break;
case MINUS:
case DIV:
if (GET_CODE (operands[1]) == MEM)
- return strcat (buf, AS1 (r%z1,%1));
+ {
+ p = "r%z1\t%1";
+ break;
+ }
if (GET_CODE (operands[2]) == MEM)
- return strcat (buf, AS1 (%z2,%2));
+ {
+ p = "%z2\t%2";
+ break;
+ }
if (! STACK_REG_P (operands[1]) || ! STACK_REG_P (operands[2]))
abort ();
+ /* Note that the Unixware assembler, and the AT&T assembler before
+ that, are confusingly not reversed from Intel syntax in this
+ area. */
if (find_regno_note (insn, REG_DEAD, REGNO (operands[2])))
{
if (STACK_TOP_P (operands[0]))
- return strcat (buf, AS2 (p,%0,%2));
+ p = "p\t%0,%2";
else
- return strcat (buf, AS2 (rp,%2,%0));
+ p = "rp\t%2,%0";
+ break;
}
if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
{
if (STACK_TOP_P (operands[0]))
- return strcat (buf, AS2 (rp,%0,%1));
+ p = "rp\t%0,%1";
else
- return strcat (buf, AS2 (p,%1,%0));
+ p = "p\t%1,%0";
+ break;
}
if (STACK_TOP_P (operands[0]))
{
if (STACK_TOP_P (operands[1]))
- return strcat (buf, AS2C (%y2,%0));
+ p = "\t%y2,%0";
else
- return strcat (buf, AS2 (r,%y1,%0));
+ p = "r\t%y1,%0";
+ break;
}
else if (STACK_TOP_P (operands[1]))
- return strcat (buf, AS2C (%1,%0));
+ p = "\t%1,%0";
else
- return strcat (buf, AS2 (r,%2,%0));
+ p = "r\t%2,%0";
+ break;
default:
abort ();
}
+
+ strcat (buf, p);
+ return buf;
}
-\f
+
/* Output code for INSN to convert a float to a signed int. OPERANDS
- are the insn operands. The input may be SFmode, DFmode, or XFmode
- and the output operand may be SImode or DImode. As a special case,
- make sure that the 387 stack top dies if the output mode is DImode,
- because the hardware requires this. */
+ are the insn operands. The output may be [SD]Imode and the input
+ operand may be [SDX]Fmode. */
char *
output_fix_trunc (insn, operands)
rtx *operands;
{
int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
- rtx xops[2];
+ int dimode_p = GET_MODE (operands[0]) == DImode;
+ rtx xops[4];
- if (! STACK_TOP_P (operands[1]))
- abort ();
+ /* Jump through a hoop or two for DImode, since the hardware has no
+ non-popping instruction. We used to do this a different way, but
+ that was somewhat fragile and broke with post-reload splitters. */
+ if (dimode_p)
+ {
+ if (! STACK_TOP_P (operands[1]))
+ {
+ rtx tmp;
+
+ output_asm_insn ("fst\t%y1", operands);
- if (GET_MODE (operands[0]) == DImode && ! stack_top_dies)
+ /* The scratch we allocated sure better have died. */
+ if (! stack_top_dies)
+ abort ();
+
+ tmp = operands[1];
+ operands[1] = operands[5];
+ operands[5] = tmp;
+ }
+ else if (! stack_top_dies)
+ output_asm_insn ("fld\t%y1", operands);
+ }
+
+ if (! STACK_TOP_P (operands[1]))
abort ();
- xops[0] = GEN_INT (0x0c00);
- xops[1] = operands[5];
+ xops[0] = GEN_INT (12);
+ xops[1] = adj_offsettable_operand (operands[2], 1);
+ xops[1] = change_address (xops[1], QImode, NULL_RTX);
- output_asm_insn (AS1 (fnstc%W2,%2), operands);
- output_asm_insn (AS2 (mov%W5,%2,%w5), operands);
- output_asm_insn (AS2 (or%W1,%0,%w1), xops);
- output_asm_insn (AS2 (mov%W3,%w5,%3), operands);
- output_asm_insn (AS1 (fldc%W3,%3), operands);
+ xops[2] = operands[0];
+ if (GET_CODE (operands[0]) != MEM)
+ xops[2] = operands[3];
- xops[0] = NON_STACK_REG_P (operands[0]) ? operands[4] : operands[0];
+ output_asm_insn ("fnstcw\t%2", operands);
+ output_asm_insn ("mov{l}\t{%2, %4|%4, %2}", operands);
+ output_asm_insn ("mov{b}\t{%0, %1|%1, %0}", xops);
+ output_asm_insn ("fldcw\t%2", operands);
+ output_asm_insn ("mov{l}\t{%4, %2|%2, %4}", operands);
- if (stack_top_dies)
- output_asm_insn (AS1 (fistp%z0,%y0), xops);
+ if (stack_top_dies || dimode_p)
+ output_asm_insn ("fistp%z2\t%2", xops);
else
- output_asm_insn (AS1 (fist%z0,%y0), xops);
+ output_asm_insn ("fist%z2\t%2", xops);
+
+ output_asm_insn ("fldcw\t%2", operands);
- if (NON_STACK_REG_P (operands[0]))
+ if (GET_CODE (operands[0]) != MEM)
{
- if (GET_MODE (operands[0]) == SImode)
- output_asm_insn (AS2 (mov%L0,%4,%0), operands);
- else
+ if (dimode_p)
{
- xops[0] = operands[0];
- xops[1] = operands[4];
- output_asm_insn (output_move_double (xops), xops);
+ split_di (operands+0, 1, xops+0, xops+1);
+ split_di (operands+3, 1, xops+2, xops+3);
+ output_asm_insn ("mov{l}\t{%2, %0|%0, %2}", xops);
+ output_asm_insn ("mov{l}\t{%3, %1|%1, %3}", xops);
}
+ else
+ output_asm_insn ("mov{l}\t{%3,%0|%0, %3}", operands);
}
- return AS1 (fldc%W2,%2);
+ return "";
}
-\f
-/* Output code for INSN to extend a float. OPERANDS are the insn
- operands. The output may be DFmode or XFmode and the input operand
- may be SFmode or DFmode. Operands 2 and 3 are scratch memory and
- are only necessary if operands 0 or 1 are non-stack registers. */
-void
-output_float_extend (insn, operands)
+/* Output code for INSN to compare OPERANDS. EFLAGS_P is 1 when fcomi
+ should be used and 2 when fnstsw should be used. UNORDERED_P is true
+ when fucom should be used. */
+
+char *
+output_fp_compare (insn, operands, eflags_p, unordered_p)
rtx insn;
rtx *operands;
+ int eflags_p, unordered_p;
{
- int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
- rtx xops[2];
+ int stack_top_dies;
+ rtx cmp_op0 = operands[0];
+ rtx cmp_op1 = operands[1];
+
+ if (eflags_p == 2)
+ {
+ cmp_op0 = cmp_op1;
+ cmp_op1 = operands[2];
+ }
- if (! STACK_TOP_P (operands[0]) && ! STACK_TOP_P (operands[1]))
+ if (! STACK_TOP_P (cmp_op0))
abort ();
- if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1]) && stack_top_dies)
- return;
+ stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
- if (STACK_TOP_P (operands[0]) )
+ if (STACK_REG_P (cmp_op1)
+ && stack_top_dies
+ && find_regno_note (insn, REG_DEAD, REGNO (cmp_op1))
+ && REGNO (cmp_op1) != FIRST_STACK_REG)
{
- if (NON_STACK_REG_P (operands[1]))
+ /* If both the top of the 387 stack dies, and the other operand
+ is also a stack register that dies, then this must be a
+ `fcompp' float compare */
+
+ if (eflags_p == 1)
+ {
+ /* There is no double popping fcomi variant. Fortunately,
+ eflags is immune from the fstp's cc clobbering. */
+ if (unordered_p)
+ output_asm_insn ("fucomip\t{%y1, %0|%0, %y1}", operands);
+ else
+ output_asm_insn ("fcomip\t{%y1, %0|%0, %y1}", operands);
+ return "fstp\t%y0";
+ }
+ else
{
- if (GET_MODE (operands[1]) == SFmode)
- output_asm_insn (AS2 (mov%L0,%1,%2), operands);
+ if (eflags_p == 2)
+ {
+ if (unordered_p)
+ return "fucompp\n\tfnstsw\t%0";
+ else
+ return "fcompp\n\tfnstsw\t%0";
+ }
else
{
- xops[0] = operands[2];
- xops[1] = operands[1];
- output_asm_insn (output_move_double (xops), xops);
+ if (unordered_p)
+ return "fucompp";
+ else
+ return "fcompp";
}
}
-
- xops[0] = NON_STACK_REG_P (operands[1]) ? operands[2] : operands[1];
-
- output_asm_insn (AS1 (fld%z0,%y0), xops);
}
else
{
- xops[0] = NON_STACK_REG_P (operands[0]) ? operands[3] : operands[0];
+ /* Encoded here as eflags_p | intmode | unordered_p | stack_top_dies. */
- if (stack_top_dies
- || (GET_CODE (xops[0]) == MEM && GET_MODE (xops[0]) == XFmode))
- {
- output_asm_insn (AS1 (fstp%z0,%y0), xops);
- if (! stack_top_dies)
- output_asm_insn (AS1 (fld%z0,%y0), xops);
- }
- else
- output_asm_insn (AS1 (fst%z0,%y0), xops);
+ static char * const alt[24] =
+ {
+ "fcom%z1\t%y1",
+ "fcomp%z1\t%y1",
+ "fucom%z1\t%y1",
+ "fucomp%z1\t%y1",
+
+ "ficom%z1\t%y1",
+ "ficomp%z1\t%y1",
+ NULL,
+ NULL,
+
+ "fcomi\t{%y1, %0|%0, %y1}",
+ "fcomip\t{%y1, %0|%0, %y1}",
+ "fucomi\t{%y1, %0|%0, %y1}",
+ "fucomip\t{%y1, %0|%0, %y1}",
+
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ "fcom%z2\t%y2\n\tfnstsw\t%0",
+ "fcomp%z2\t%y2\n\tfnstsw\t%0",
+ "fucom%z2\t%y2\n\tfnstsw\t%0",
+ "fucomp%z2\t%y2\n\tfnstsw\t%0",
+
+ "ficom%z2\t%y2\n\tfnstsw\t%0",
+ "ficomp%z2\t%y2\n\tfnstsw\t%0",
+ NULL,
+ NULL
+ };
+
+ int mask;
+ char *ret;
+
+ mask = eflags_p << 3;
+ mask |= (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT) << 2;
+ mask |= unordered_p << 1;
+ mask |= stack_top_dies;
+
+ if (mask >= 24)
+ abort ();
+ ret = alt[mask];
+ if (ret == NULL)
+ abort ();
- if (NON_STACK_REG_P (operands[0]))
- {
- xops[0] = operands[0];
- xops[1] = operands[3];
- output_asm_insn (output_move_double (xops), xops);
- }
+ return ret;
}
}
-\f
-/* Output code for INSN to compare OPERANDS. The two operands might
- not have the same mode: one might be within a FLOAT or FLOAT_EXTEND
- expression. If the compare is in mode CCFPEQmode, use an opcode that
- will not fault if a qNaN is present. */
-
-char *
-output_float_compare (insn, operands)
- rtx insn;
- rtx *operands;
-{
- int stack_top_dies;
- rtx body = XVECEXP (PATTERN (insn), 0, 0);
- int unordered_compare = GET_MODE (SET_SRC (body)) == CCFPEQmode;
- rtx tmp;
- int cc0_set = 1;
- int i;
-
- if (TARGET_CMOVE && STACK_REG_P (operands[1])
- && STACK_REG_P (operands[0]))
- {
- cc_status.flags |= CC_FCOMI;
- cc_prev_status.flags &= ~CC_TEST_AX;
- }
-
- if (! STACK_TOP_P (operands[0]))
- {
- tmp = operands[0];
- operands[0] = operands[1];
- operands[1] = tmp;
- cc_status.flags |= CC_REVERSED;
- }
-
- if (! STACK_TOP_P (operands[0]))
- abort ();
- stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+/* Output assembler code to FILE to initialize basic-block profiling.
- if (STACK_REG_P (operands[1])
- && stack_top_dies
- && find_regno_note (insn, REG_DEAD, REGNO (operands[1]))
- && REGNO (operands[1]) == FIRST_STACK_REG + 1)
- {
- /* If both the top of the 387 stack dies, and the other operand
- is also a stack register that dies, then this must be a
- `fcompp' float compare */
+ If profile_block_flag == 2
- if (unordered_compare)
- {
- if (cc_status.flags & CC_FCOMI)
- {
- output_asm_insn (AS2 (fucomip,%y1,%0), operands);
- output_asm_insn (AS1 (fstp, %y0), operands);
- if (!TARGET_IEEE_FP)
- cc0_set = 0;
- }
- else
- output_asm_insn ("fucompp", operands);
- }
- else
- {
- if (cc_status.flags & CC_FCOMI)
- {
- output_asm_insn (AS2 (fcomip, %y1,%0), operands);
- output_asm_insn (AS1 (fstp, %y0), operands);
- if (!TARGET_IEEE_FP)
- cc0_set = 0;
- }
- else
- output_asm_insn ("fcompp", operands);
- }
- }
- else
- {
- static char buf[100];
+ Output code to call the subroutine `__bb_init_trace_func'
+ and pass two parameters to it. The first parameter is
+ the address of a block allocated in the object module.
+ The second parameter is the number of the first basic block
+ of the function.
- /* Decide if this is a float compare or an unordered float compare. */
+ The name of the block is a local symbol made with this statement:
+
+ ASM_GENERATE_INTERNAL_LABEL (BUFFER, "LPBX", 0);
- if (unordered_compare)
- strcpy (buf, (cc_status.flags & CC_FCOMI) ? "fucomi" : "fucom");
- else
- strcpy (buf, (cc_status.flags & CC_FCOMI) ? "fcomi" : "fcom");
+ Of course, since you are writing the definition of
+ `ASM_GENERATE_INTERNAL_LABEL' as well as that of this macro, you
+ can take a short cut in the definition of this macro and use the
+ name that you know will result.
- /* Modify the opcode if the 387 stack is to be popped. */
+ The number of the first basic block of the function is
+ passed to the macro in BLOCK_OR_LABEL.
- if (stack_top_dies)
- strcat (buf, "p");
+ If described in a virtual assembler language the code to be
+ output looks like:
- if (cc_status.flags & CC_FCOMI)
- {
- output_asm_insn (strcat (buf, AS2 (%z1,%y1,%0)), operands);
- if (!TARGET_IEEE_FP)
- cc0_set = 0;
- }
- else
- output_asm_insn (strcat (buf, AS1 (%z1,%y1)), operands);
- }
+ parameter1 <- LPBX0
+ parameter2 <- BLOCK_OR_LABEL
+ call __bb_init_trace_func
- /* Now retrieve the condition code. */
- if (cc0_set)
- {
- char *r = output_fp_cc0_set (insn);
- if (r[0]) output_asm_insn (r, operands);
- }
+ else if profile_block_flag != 0
+ Output code to call the subroutine `__bb_init_func'
+ and pass one single parameter to it, which is the same
+ as the first parameter to `__bb_init_trace_func'.
- /* We emit fstp instruction after integer comparsions to improve
- scheduling. */
- for (i = 0; i < 2 ; i++)
- {
- if (STACK_REG_P (operands[i])
- && find_regno_note (insn, REG_DEAD, REGNO (operands[i]))
- && REGNO (operands[i]) != FIRST_STACK_REG
- && (!stack_top_dies || REGNO (operands[i]) != FIRST_STACK_REG + 1))
- {
- rtx xexp[2];
- xexp[0] = gen_rtx_REG (DFmode,
- REGNO (operands[i]) - (stack_top_dies != 0));
- output_asm_insn (AS1 (fstp, %y0), xexp);
- }
- }
+ The first word of this parameter is a flag which will be nonzero if
+ the object module has already been initialized. So test this word
+ first, and do not call `__bb_init_func' if the flag is nonzero.
+ Note: When profile_block_flag == 2 the test need not be done
+ but `__bb_init_trace_func' *must* be called.
- return "";
+ BLOCK_OR_LABEL may be used to generate a label number as a
+ branch destination in case `__bb_init_func' will not be called.
+ If described in a virtual assembler language the code to be
+ output looks like:
-}
-\f
-/* Output opcodes to transfer the results of FP compare or test INSN
- from the FPU to the CPU flags. If TARGET_IEEE_FP, ensure that if the
- result of the compare or test is unordered, no comparison operator
- succeeds except NE. Return an output template, if any. */
+ cmp (LPBX0),0
+ jne local_label
+ parameter1 <- LPBX0
+ call __bb_init_func
+ local_label:
+*/
-char *
-output_fp_cc0_set (insn)
- rtx insn;
+void
+ix86_output_function_block_profiler (file, block_or_label)
+ FILE *file;
+ int block_or_label;
{
- rtx xops[3];
- rtx next;
- enum rtx_code code;
-
- if (!(cc_status.flags & CC_FCOMI))
- {
- xops[0] = gen_rtx_REG (HImode, 0);
- output_asm_insn (AS1 (fnsts%W0,%0), xops);
- }
+ static int num_func = 0;
+ rtx xops[8];
+ char block_table[80], false_label[80];
- if (! TARGET_IEEE_FP)
- {
- if (!(cc_status.flags & CC_REVERSED))
- {
- next = next_cc0_user (insn);
-
- if (GET_CODE (PATTERN (next)) == SET
- && SET_DEST (PATTERN (next)) == pc_rtx
- && GET_CODE (SET_SRC (PATTERN (next))) == IF_THEN_ELSE)
- code = GET_CODE (XEXP (SET_SRC (PATTERN (next)), 0));
- else if (GET_CODE (PATTERN (next)) == SET)
- code = GET_CODE (SET_SRC (PATTERN (next)));
- else
- return "sahf";
-
- if (code == GT || code == LT || code == EQ || code == NE
- || code == LE || code == GE)
- {
- /* We will test eax directly. */
- cc_status.flags |= CC_TEST_AX;
- return "";
- }
- }
+ ASM_GENERATE_INTERNAL_LABEL (block_table, "LPBX", 0);
- return "sahf";
- }
+ xops[1] = gen_rtx_SYMBOL_REF (VOIDmode, block_table);
+ xops[5] = stack_pointer_rtx;
+ xops[7] = gen_rtx_REG (Pmode, 0); /* eax */
- next = next_cc0_user (insn);
- if (next == NULL_RTX)
- abort ();
+ CONSTANT_POOL_ADDRESS_P (xops[1]) = TRUE;
- if (GET_CODE (PATTERN (next)) == SET
- && SET_DEST (PATTERN (next)) == pc_rtx
- && GET_CODE (SET_SRC (PATTERN (next))) == IF_THEN_ELSE)
- code = GET_CODE (XEXP (SET_SRC (PATTERN (next)), 0));
- else if (GET_CODE (PATTERN (next)) == SET)
+ switch (profile_block_flag)
{
- if (GET_CODE (SET_SRC (PATTERN (next))) == IF_THEN_ELSE)
- code = GET_CODE (XEXP (SET_SRC (PATTERN (next)), 0));
- else
- code = GET_CODE (SET_SRC (PATTERN (next)));
- }
+ case 2:
+ xops[2] = GEN_INT (block_or_label);
+ xops[3] = gen_rtx_MEM (Pmode,
+ gen_rtx_SYMBOL_REF (VOIDmode, "__bb_init_trace_func"));
+ xops[6] = GEN_INT (8);
- else if (GET_CODE (PATTERN (next)) == PARALLEL
- && GET_CODE (XVECEXP (PATTERN (next), 0, 0)) == SET)
- {
- if (GET_CODE (SET_SRC (XVECEXP (PATTERN (next), 0, 0))) == IF_THEN_ELSE)
- code = GET_CODE (XEXP (SET_SRC (XVECEXP (PATTERN (next), 0, 0)), 0));
+ output_asm_insn ("push{l}\t%2", xops);
+ if (!flag_pic)
+ output_asm_insn ("push{l}\t%1", xops);
else
- code = GET_CODE (SET_SRC (XVECEXP (PATTERN (next), 0, 0)));
- }
- else
- abort ();
-
- if (cc_status.flags & CC_FCOMI)
- {
- /* It is very tricky. We have to do it right. */
-
- xops [0] = gen_rtx_REG (QImode, 0);
-
- switch (code)
{
- case GT:
- case GE:
- break;
-
- case LT:
- output_asm_insn (AS1 (setb,%b0), xops);
- output_asm_insn (AS1 (setp,%h0), xops);
- output_asm_insn (AS2 (cmp%B0,%b0,%h0), xops);
- break;
-
- case LE:
- output_asm_insn (AS1 (setbe,%b0), xops);
- output_asm_insn (AS1 (setnp,%h0), xops);
- output_asm_insn (AS2 (xor%B0,%b0,%h0), xops);
- break;
-
- case EQ:
- case NE:
- output_asm_insn (AS1 (setne,%b0), xops);
- output_asm_insn (AS1 (setp,%h0), xops);
- output_asm_insn (AS2 (or%B0,%b0,%h0), xops);
- break;
-
- case GTU:
- case LTU:
- case GEU:
- case LEU:
- default:
- abort ();
+ output_asm_insn ("lea{l}\t{%a1, %7|%7, %a1}", xops);
+ output_asm_insn ("push{l}\t%7", xops);
}
- }
- else
- {
- xops[0] = gen_rtx_REG (QImode, 0);
-
- switch (code)
- {
- case GT:
- xops[1] = GEN_INT (0x45);
- output_asm_insn (AS2 (and%B0,%1,%h0), xops);
- /* je label */
- break;
-
- case LT:
- xops[1] = GEN_INT (0x45);
- xops[2] = GEN_INT (0x01);
- output_asm_insn (AS2 (and%B0,%1,%h0), xops);
- output_asm_insn (AS2 (cmp%B0,%2,%h0), xops);
- /* je label */
- break;
+ output_asm_insn ("call\t%P3", xops);
+ output_asm_insn ("add{l}\t{%6, %5|%5, %6}", xops);
+ break;
- case GE:
- xops[1] = GEN_INT (0x05);
- output_asm_insn (AS2 (and%B0,%1,%h0), xops);
- /* je label */
- break;
+ default:
+ ASM_GENERATE_INTERNAL_LABEL (false_label, "LPBZ", num_func);
- case LE:
- xops[1] = GEN_INT (0x45);
- xops[2] = GEN_INT (0x40);
- output_asm_insn (AS2 (and%B0,%1,%h0), xops);
- output_asm_insn (AS1 (dec%B0,%h0), xops);
- output_asm_insn (AS2 (cmp%B0,%2,%h0), xops);
- /* jb label */
- break;
+ xops[0] = const0_rtx;
+ xops[2] = gen_rtx_MEM (Pmode,
+ gen_rtx_SYMBOL_REF (VOIDmode, false_label));
+ xops[3] = gen_rtx_MEM (Pmode,
+ gen_rtx_SYMBOL_REF (VOIDmode, "__bb_init_func"));
+ xops[4] = gen_rtx_MEM (Pmode, xops[1]);
+ xops[6] = GEN_INT (4);
- case EQ:
- xops[1] = GEN_INT (0x45);
- xops[2] = GEN_INT (0x40);
- output_asm_insn (AS2 (and%B0,%1,%h0), xops);
- output_asm_insn (AS2 (cmp%B0,%2,%h0), xops);
- /* je label */
- break;
+ CONSTANT_POOL_ADDRESS_P (xops[2]) = TRUE;
- case NE:
- xops[1] = GEN_INT (0x44);
- xops[2] = GEN_INT (0x40);
- output_asm_insn (AS2 (and%B0,%1,%h0), xops);
- output_asm_insn (AS2 (xor%B0,%2,%h0), xops);
- /* jne label */
- break;
+ output_asm_insn ("cmp{l}\t{%0, %4|%4, %0}", xops);
+ output_asm_insn ("jne\t%2", xops);
- case GTU:
- case LTU:
- case GEU:
- case LEU:
- default:
- abort ();
+ if (!flag_pic)
+ output_asm_insn ("push{l}\t%1", xops);
+ else
+ {
+ output_asm_insn ("lea{l}\t{%a1, %7|%7, %a2}", xops);
+ output_asm_insn ("push{l}\t%7", xops);
}
+ output_asm_insn ("call\t%P3", xops);
+ output_asm_insn ("add{l}\t{%6, %5|%5, %6}", xops);
+ ASM_OUTPUT_INTERNAL_LABEL (file, "LPBZ", num_func);
+ num_func++;
+ break;
}
-
- return "";
}
-\f
-#define MAX_386_STACK_LOCALS 2
-static rtx i386_stack_locals[(int) MAX_MACHINE_MODE][MAX_386_STACK_LOCALS];
+/* Output assembler code to FILE to increment a counter associated
+ with basic block number BLOCKNO.
-/* Define the structure for the machine field in struct function. */
-struct machine_function
-{
- rtx i386_stack_locals[(int) MAX_MACHINE_MODE][MAX_386_STACK_LOCALS];
- rtx pic_label_rtx;
- char pic_label_name[256];
-};
+ If profile_block_flag == 2
-/* Functions to save and restore i386_stack_locals.
- These will be called, via pointer variables,
- from push_function_context and pop_function_context. */
+ Output code to initialize the global structure `__bb' and
+ call the function `__bb_trace_func' which will increment the
+ counter.
-void
-save_386_machine_status (p)
- struct function *p;
-{
- p->machine
- = (struct machine_function *) xmalloc (sizeof (struct machine_function));
- bcopy ((char *) i386_stack_locals, (char *) p->machine->i386_stack_locals,
- sizeof i386_stack_locals);
- p->machine->pic_label_rtx = pic_label_rtx;
- bcopy (pic_label_name, p->machine->pic_label_name, 256);
-}
+ `__bb' consists of two words. In the first word the number
+ of the basic block has to be stored. In the second word
+ the address of a block allocated in the object module
+ has to be stored.
-void
-restore_386_machine_status (p)
- struct function *p;
-{
- bcopy ((char *) p->machine->i386_stack_locals, (char *) i386_stack_locals,
- sizeof i386_stack_locals);
- pic_label_rtx = p->machine->pic_label_rtx;
- bcopy (p->machine->pic_label_name, pic_label_name, 256);
- free (p->machine);
- p->machine = NULL;
-}
+ The basic block number is given by BLOCKNO.
-/* Clear stack slot assignments remembered from previous functions.
- This is called from INIT_EXPANDERS once before RTL is emitted for each
- function. */
+ The address of the block is given by the label created with
-void
-clear_386_stack_locals ()
-{
- enum machine_mode mode;
- int n;
+ ASM_GENERATE_INTERNAL_LABEL (BUFFER, "LPBX", 0);
- for (mode = VOIDmode; (int) mode < (int) MAX_MACHINE_MODE;
- mode = (enum machine_mode) ((int) mode + 1))
- for (n = 0; n < MAX_386_STACK_LOCALS; n++)
- i386_stack_locals[(int) mode][n] = NULL_RTX;
+ by FUNCTION_BLOCK_PROFILER.
- pic_label_rtx = NULL_RTX;
- bzero (pic_label_name, 256);
- /* Arrange to save and restore i386_stack_locals around nested functions. */
- save_machine_status = save_386_machine_status;
- restore_machine_status = restore_386_machine_status;
-}
+ Of course, since you are writing the definition of
+ `ASM_GENERATE_INTERNAL_LABEL' as well as that of this macro, you
+ can take a short cut in the definition of this macro and use the
+ name that you know will result.
-/* Return a MEM corresponding to a stack slot with mode MODE.
- Allocate a new slot if necessary.
+ If described in a virtual assembler language the code to be
+ output looks like:
- The RTL for a function can have several slots available: N is
- which slot to use. */
+ move BLOCKNO -> (__bb)
+ move LPBX0 -> (__bb+4)
+ call __bb_trace_func
-rtx
-assign_386_stack_local (mode, n)
- enum machine_mode mode;
- int n;
-{
- if (n < 0 || n >= MAX_386_STACK_LOCALS)
- abort ();
+ Note that function `__bb_trace_func' must not change the
+ machine state, especially the flag register. To grant
+ this, you must output code to save and restore registers
+ either in this macro or in the macros MACHINE_STATE_SAVE
+ and MACHINE_STATE_RESTORE. The last two macros will be
+ used in the function `__bb_trace_func', so you must make
+ sure that the function prologue does not change any
+ register prior to saving it with MACHINE_STATE_SAVE.
- if (i386_stack_locals[(int) mode][n] == NULL_RTX)
- i386_stack_locals[(int) mode][n]
- = assign_stack_local (mode, GET_MODE_SIZE (mode), 0);
+ else if profile_block_flag != 0
- return i386_stack_locals[(int) mode][n];
-}
-\f
-int is_mul(op,mode)
- register rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return (GET_CODE (op) == MULT);
-}
+ Output code to increment the counter directly.
+ Basic blocks are numbered separately from zero within each
+ compiled object module. The count associated with block number
+ BLOCKNO is at index BLOCKNO in an array of words; the name of
+ this array is a local symbol made with this statement:
-int is_div(op,mode)
- register rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return (GET_CODE (op) == DIV);
-}
-\f
-#ifdef NOTYET
-/* Create a new copy of an rtx.
- Recursively copies the operands of the rtx,
- except for those few rtx codes that are sharable.
- Doesn't share CONST */
+ ASM_GENERATE_INTERNAL_LABEL (BUFFER, "LPBX", 2);
-rtx
-copy_all_rtx (orig)
- register rtx orig;
-{
- register rtx copy;
- register int i, j;
- register RTX_CODE code;
- register const char *format_ptr;
+ Of course, since you are writing the definition of
+ `ASM_GENERATE_INTERNAL_LABEL' as well as that of this macro, you
+ can take a short cut in the definition of this macro and use the
+ name that you know will result.
- code = GET_CODE (orig);
+ If described in a virtual assembler language the code to be
+ output looks like:
- switch (code)
- {
- case REG:
- case QUEUED:
- case CONST_INT:
- case CONST_DOUBLE:
- case SYMBOL_REF:
- case CODE_LABEL:
- case PC:
- case CC0:
- case SCRATCH:
- /* SCRATCH must be shared because they represent distinct values. */
- return orig;
+ inc (LPBX2+4*BLOCKNO)
+*/
-#if 0
- case CONST:
- /* CONST can be shared if it contains a SYMBOL_REF. If it contains
- a LABEL_REF, it isn't sharable. */
- if (GET_CODE (XEXP (orig, 0)) == PLUS
- && GET_CODE (XEXP (XEXP (orig, 0), 0)) == SYMBOL_REF
- && GET_CODE (XEXP (XEXP (orig, 0), 1)) == CONST_INT)
- return orig;
- break;
-#endif
- /* A MEM with a constant address is not sharable. The problem is that
- the constant address may need to be reloaded. If the mem is shared,
- then reloading one copy of this mem will cause all copies to appear
- to have been reloaded. */
- }
+void
+ix86_output_block_profiler (file, blockno)
+ FILE *file ATTRIBUTE_UNUSED;
+ int blockno;
+{
+ rtx xops[8], cnt_rtx;
+ char counts[80];
+ char *block_table = counts;
+
+ switch (profile_block_flag)
+ {
+ case 2:
+ ASM_GENERATE_INTERNAL_LABEL (block_table, "LPBX", 0);
- copy = rtx_alloc (code);
- PUT_MODE (copy, GET_MODE (orig));
- copy->in_struct = orig->in_struct;
- copy->volatil = orig->volatil;
- copy->unchanging = orig->unchanging;
- copy->integrated = orig->integrated;
- /* intel1 */
- copy->is_spill_rtx = orig->is_spill_rtx;
+ xops[1] = gen_rtx_SYMBOL_REF (VOIDmode, block_table);
+ xops[2] = GEN_INT (blockno);
+ xops[3] = gen_rtx_MEM (Pmode,
+ gen_rtx_SYMBOL_REF (VOIDmode, "__bb_trace_func"));
+ xops[4] = gen_rtx_SYMBOL_REF (VOIDmode, "__bb");
+ xops[5] = plus_constant (xops[4], 4);
+ xops[0] = gen_rtx_MEM (SImode, xops[4]);
+ xops[6] = gen_rtx_MEM (SImode, xops[5]);
- format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
+ CONSTANT_POOL_ADDRESS_P (xops[1]) = TRUE;
- for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++)
- {
- switch (*format_ptr++)
+ output_asm_insn ("pushf", xops);
+ output_asm_insn ("mov{l}\t{%2, %0|%0, %2}", xops);
+ if (flag_pic)
{
- case 'e':
- XEXP (copy, i) = XEXP (orig, i);
- if (XEXP (orig, i) != NULL)
- XEXP (copy, i) = copy_rtx (XEXP (orig, i));
- break;
+ xops[7] = gen_rtx_REG (Pmode, 0); /* eax */
+ output_asm_insn ("push{l}\t%7", xops);
+ output_asm_insn ("lea{l}\t{%a1, %7|%7, %a1}", xops);
+ output_asm_insn ("mov{l}\t{%7, %6|%6, %7}", xops);
+ output_asm_insn ("pop{l}\t%7", xops);
+ }
+ else
+ output_asm_insn ("mov{l}\t{%1, %6|%6, %1}", xops);
+ output_asm_insn ("call\t%P3", xops);
+ output_asm_insn ("popf", xops);
- case '0':
- case 'u':
- XEXP (copy, i) = XEXP (orig, i);
- break;
+ break;
- case 'E':
- case 'V':
- XVEC (copy, i) = XVEC (orig, i);
- if (XVEC (orig, i) != NULL)
- {
- XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i));
- for (j = 0; j < XVECLEN (copy, i); j++)
- XVECEXP (copy, i, j) = copy_rtx (XVECEXP (orig, i, j));
- }
- break;
+ default:
+ ASM_GENERATE_INTERNAL_LABEL (counts, "LPBX", 2);
+ cnt_rtx = gen_rtx_SYMBOL_REF (VOIDmode, counts);
+ SYMBOL_REF_FLAG (cnt_rtx) = TRUE;
- case 'w':
- XWINT (copy, i) = XWINT (orig, i);
- break;
+ if (blockno)
+ cnt_rtx = plus_constant (cnt_rtx, blockno*4);
- case 'i':
- XINT (copy, i) = XINT (orig, i);
- break;
+ if (flag_pic)
+ cnt_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, cnt_rtx);
- case 's':
- case 'S':
- XSTR (copy, i) = XSTR (orig, i);
- break;
+ xops[0] = gen_rtx_MEM (SImode, cnt_rtx);
+ output_asm_insn ("inc{l}\t%0", xops);
- default:
- abort ();
- }
+ break;
}
- return copy;
}
-
\f
-/* Try to rewrite a memory address to make it valid */
-
void
-rewrite_address (mem_rtx)
- rtx mem_rtx;
+ix86_expand_move (mode, operands)
+ enum machine_mode mode;
+ rtx operands[];
{
- rtx index_rtx, base_rtx, offset_rtx, scale_rtx, ret_rtx;
- int scale = 1;
- int offset_adjust = 0;
- int was_only_offset = 0;
- rtx mem_addr = XEXP (mem_rtx, 0);
- char *storage = oballoc (0);
- int in_struct = 0;
- int is_spill_rtx = 0;
-
- in_struct = MEM_IN_STRUCT_P (mem_rtx);
- is_spill_rtx = RTX_IS_SPILL_P (mem_rtx);
-
- if (GET_CODE (mem_addr) == PLUS
- && GET_CODE (XEXP (mem_addr, 1)) == PLUS
- && GET_CODE (XEXP (XEXP (mem_addr, 1), 0)) == REG)
- {
- /* This part is utilized by the combiner. */
- ret_rtx
- = gen_rtx (PLUS, GET_MODE (mem_addr),
- gen_rtx (PLUS, GET_MODE (XEXP (mem_addr, 1)),
- XEXP (mem_addr, 0), XEXP (XEXP (mem_addr, 1), 0)),
- XEXP (XEXP (mem_addr, 1), 1));
-
- if (memory_address_p (GET_MODE (mem_rtx), ret_rtx))
- {
- XEXP (mem_rtx, 0) = ret_rtx;
- RTX_IS_SPILL_P (ret_rtx) = is_spill_rtx;
- return;
- }
-
- obfree (storage);
- }
-
- /* This part is utilized by loop.c.
- If the address contains PLUS (reg,const) and this pattern is invalid
- in this case - try to rewrite the address to make it valid. */
- storage = oballoc (0);
- index_rtx = base_rtx = offset_rtx = NULL;
+ int strict = (reload_in_progress || reload_completed);
+ int want_clobber = 0;
+ rtx insn;
- /* Find the base index and offset elements of the memory address. */
- if (GET_CODE (mem_addr) == PLUS)
+ if (flag_pic && mode == Pmode && symbolic_operand (operands[1], Pmode))
{
- if (GET_CODE (XEXP (mem_addr, 0)) == REG)
- {
- if (GET_CODE (XEXP (mem_addr, 1)) == REG)
- base_rtx = XEXP (mem_addr, 1), index_rtx = XEXP (mem_addr, 0);
- else
- base_rtx = XEXP (mem_addr, 0), offset_rtx = XEXP (mem_addr, 1);
- }
-
- else if (GET_CODE (XEXP (mem_addr, 0)) == MULT)
- {
- index_rtx = XEXP (mem_addr, 0);
- if (GET_CODE (XEXP (mem_addr, 1)) == REG)
- base_rtx = XEXP (mem_addr, 1);
- else
- offset_rtx = XEXP (mem_addr, 1);
- }
+ /* Emit insns to move operands[1] into operands[0]. */
- else if (GET_CODE (XEXP (mem_addr, 0)) == PLUS)
+ if (GET_CODE (operands[0]) == MEM)
+ operands[1] = force_reg (Pmode, operands[1]);
+ else
{
- if (GET_CODE (XEXP (XEXP (mem_addr, 0), 0)) == PLUS
- && GET_CODE (XEXP (XEXP (XEXP (mem_addr, 0), 0), 0)) == MULT
- && (GET_CODE (XEXP (XEXP (XEXP (XEXP (mem_addr, 0), 0), 0), 0))
- == REG)
- && (GET_CODE (XEXP (XEXP (XEXP (XEXP (mem_addr, 0), 0), 0), 1))
- == CONST_INT)
- && (GET_CODE (XEXP (XEXP (XEXP (mem_addr, 0), 0), 1))
- == CONST_INT)
- && GET_CODE (XEXP (XEXP (mem_addr, 0), 1)) == REG
- && GET_CODE (XEXP (mem_addr, 1)) == SYMBOL_REF)
- {
- index_rtx = XEXP (XEXP (XEXP (mem_addr, 0), 0), 0);
- offset_rtx = XEXP (mem_addr, 1);
- base_rtx = XEXP (XEXP (mem_addr, 0), 1);
- offset_adjust = INTVAL (XEXP (XEXP (XEXP (mem_addr, 0), 0), 1));
- }
- else
- {
- offset_rtx = XEXP (mem_addr, 1);
- index_rtx = XEXP (XEXP (mem_addr, 0), 0);
- base_rtx = XEXP (XEXP (mem_addr, 0), 1);
- }
+ rtx temp = operands[0];
+ if (GET_CODE (temp) != REG)
+ temp = gen_reg_rtx (Pmode);
+ temp = legitimize_pic_address (operands[1], temp);
+ if (temp == operands[0])
+ return;
+ operands[1] = temp;
}
+ }
+ else
+ {
+ if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
+ operands[1] = force_reg (mode, operands[1]);
- else if (GET_CODE (XEXP (mem_addr, 0)) == CONST_INT)
+ if (FLOAT_MODE_P (mode))
{
- was_only_offset = 1;
- index_rtx = NULL;
- base_rtx = NULL;
- offset_rtx = XEXP (mem_addr, 1);
- offset_adjust = INTVAL (XEXP (mem_addr, 0));
- if (offset_adjust == 0)
- {
- XEXP (mem_rtx, 0) = offset_rtx;
- RTX_IS_SPILL_P (XEXP (mem_rtx, 0)) = is_spill_rtx;
- return;
- }
+ /* If we are loading a floating point constant that isn't 0 or 1
+ into a register, force the value to memory now, since we'll
+ get better code out the back end. */
+
+ if (strict)
+ ;
+ else if (GET_CODE (operands[0]) == MEM)
+ operands[1] = force_reg (mode, operands[1]);
+ else if (GET_CODE (operands[1]) == CONST_DOUBLE
+ && ! standard_80387_constant_p (operands[1]))
+ operands[1] = validize_mem (force_const_mem (mode, operands[1]));
}
else
{
- obfree (storage);
- return;
+ /* Try to guess when a cc clobber on the move might be fruitful. */
+ if (!strict
+ && GET_CODE (operands[0]) == REG
+ && operands[1] == const0_rtx
+ && !flag_peephole2)
+ want_clobber = 1;
}
}
- else if (GET_CODE (mem_addr) == MULT)
- index_rtx = mem_addr;
- else
- {
- obfree (storage);
- return;
- }
- if (index_rtx != 0 && GET_CODE (index_rtx) == MULT)
+ insn = gen_rtx_SET (VOIDmode, operands[0], operands[1]);
+ if (want_clobber)
{
- if (GET_CODE (XEXP (index_rtx, 1)) != CONST_INT)
- {
- obfree (storage);
- return;
- }
-
- scale_rtx = XEXP (index_rtx, 1);
- scale = INTVAL (scale_rtx);
- index_rtx = copy_all_rtx (XEXP (index_rtx, 0));
+ rtx clob;
+ clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, FLAGS_REG));
+ insn = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, insn, clob));
}
- /* Now find which of the elements are invalid and try to fix them. */
- if (index_rtx && GET_CODE (index_rtx) == CONST_INT && base_rtx == NULL)
- {
- offset_adjust = INTVAL (index_rtx) * scale;
+ emit_insn (insn);
+}
- if (offset_rtx != 0 && CONSTANT_P (offset_rtx))
- offset_rtx = plus_constant (offset_rtx, offset_adjust);
- else if (offset_rtx == 0)
- offset_rtx = const0_rtx;
+/* Attempt to expand a binary operator. Make the expansion closer to the
+ actual machine, then just general_operand, which will allow 3 separate
+ memory references (one output, two input) in a single insn. Return
+ whether the insn fails, or succeeds. */
- RTX_IS_SPILL_P (XEXP (mem_rtx, 0)) = is_spill_rtx;
- XEXP (mem_rtx, 0) = offset_rtx;
- return;
+void
+ix86_expand_binary_operator (code, mode, operands)
+ enum rtx_code code;
+ enum machine_mode mode;
+ rtx operands[];
+{
+ int matching_memory;
+ rtx src1, src2, dst, op, clob;
+
+ dst = operands[0];
+ src1 = operands[1];
+ src2 = operands[2];
+
+ /* Recognize <var1> = <value> <op> <var1> for commutative operators */
+ if (GET_RTX_CLASS (code) == 'c'
+ && (rtx_equal_p (dst, src2)
+ || immediate_operand (src1, mode)))
+ {
+ rtx temp = src1;
+ src1 = src2;
+ src2 = temp;
}
- if (base_rtx && GET_CODE (base_rtx) == PLUS
- && GET_CODE (XEXP (base_rtx, 0)) == REG
- && GET_CODE (XEXP (base_rtx, 1)) == CONST_INT)
+ /* If the destination is memory, and we do not have matching source
+ operands, do things in registers. */
+ matching_memory = 0;
+ if (GET_CODE (dst) == MEM)
{
- offset_adjust += INTVAL (XEXP (base_rtx, 1));
- base_rtx = copy_all_rtx (XEXP (base_rtx, 0));
+ if (rtx_equal_p (dst, src1))
+ matching_memory = 1;
+ else if (GET_RTX_CLASS (code) == 'c'
+ && rtx_equal_p (dst, src2))
+ matching_memory = 2;
+ else
+ dst = gen_reg_rtx (mode);
+ }
+
+ /* Both source operands cannot be in memory. */
+ if (GET_CODE (src1) == MEM && GET_CODE (src2) == MEM)
+ {
+ if (matching_memory != 2)
+ src2 = force_reg (mode, src2);
+ else
+ src1 = force_reg (mode, src1);
}
- else if (base_rtx && GET_CODE (base_rtx) == CONST_INT)
+ /* If the operation is not commutable, source 1 cannot be a constant. */
+ if (CONSTANT_P (src1) && GET_RTX_CLASS (code) != 'c')
+ src1 = force_reg (mode, src1);
+
+ /* If optimizing, copy to regs to improve CSE */
+ if (optimize && !reload_in_progress && !reload_completed)
{
- offset_adjust += INTVAL (base_rtx);
- base_rtx = NULL;
+ if (GET_CODE (dst) == MEM)
+ dst = gen_reg_rtx (mode);
+ if (GET_CODE (src1) == MEM)
+ src1 = force_reg (mode, src1);
+ if (GET_CODE (src2) == MEM)
+ src2 = force_reg (mode, src2);
}
- if (index_rtx && GET_CODE (index_rtx) == PLUS
- && GET_CODE (XEXP (index_rtx, 0)) == REG
- && GET_CODE (XEXP (index_rtx, 1)) == CONST_INT)
+ /* Emit the instruction. */
+
+ op = gen_rtx_SET (VOIDmode, dst, gen_rtx_fmt_ee (code, mode, src1, src2));
+ if (reload_in_progress)
+ {
+ /* Reload doesn't know about the flags register, and doesn't know that
+ it doesn't want to clobber it. We can only do this with PLUS. */
+ if (code != PLUS)
+ abort ();
+ emit_insn (op);
+ }
+ else
{
- offset_adjust += INTVAL (XEXP (index_rtx, 1)) * scale;
- index_rtx = copy_all_rtx (XEXP (index_rtx, 0));
+ clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, FLAGS_REG));
+ emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, op, clob)));
}
- if (index_rtx)
+ /* Fix up the destination if needed. */
+ if (dst != operands[0])
+ emit_move_insn (operands[0], dst);
+}
+
+/* Return TRUE or FALSE depending on whether the binary operator meets the
+ appropriate constraints. */
+
+int
+ix86_binary_operator_ok (code, mode, operands)
+ enum rtx_code code;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+ rtx operands[3];
+{
+ /* Both source operands cannot be in memory. */
+ if (GET_CODE (operands[1]) == MEM && GET_CODE (operands[2]) == MEM)
+ return 0;
+ /* If the operation is not commutable, source 1 cannot be a constant. */
+ if (CONSTANT_P (operands[1]) && GET_RTX_CLASS (code) != 'c')
+ return 0;
+ /* If the destination is memory, we must have a matching source operand. */
+ if (GET_CODE (operands[0]) == MEM
+ && ! (rtx_equal_p (operands[0], operands[1])
+ || (GET_RTX_CLASS (code) == 'c'
+ && rtx_equal_p (operands[0], operands[2]))))
+ return 0;
+ return 1;
+}
+
+/* Attempt to expand a unary operator. Make the expansion closer to the
+ actual machine, then just general_operand, which will allow 2 separate
+ memory references (one output, one input) in a single insn. Return
+ whether the insn fails, or succeeds. */
+
+int
+ix86_expand_unary_operator (code, mode, operands)
+ enum rtx_code code;
+ enum machine_mode mode;
+ rtx operands[];
+{
+ /* If optimizing, copy to regs to improve CSE */
+ if (optimize
+ && ((reload_in_progress | reload_completed) == 0)
+ && GET_CODE (operands[1]) == MEM)
+ operands[1] = force_reg (GET_MODE (operands[1]), operands[1]);
+
+ if (! ix86_unary_operator_ok (code, mode, operands))
{
- if (! LEGITIMATE_INDEX_P (index_rtx)
- && ! (index_rtx == stack_pointer_rtx && scale == 1
- && base_rtx == NULL))
+ if (optimize == 0
+ && ((reload_in_progress | reload_completed) == 0)
+ && GET_CODE (operands[1]) == MEM)
{
- obfree (storage);
- return;
+ operands[1] = force_reg (GET_MODE (operands[1]), operands[1]);
+ if (! ix86_unary_operator_ok (code, mode, operands))
+ return FALSE;
}
+ else
+ return FALSE;
}
- if (base_rtx)
+ return TRUE;
+}
+
+/* Return TRUE or FALSE depending on whether the unary operator meets the
+ appropriate constraints. */
+
+int
+ix86_unary_operator_ok (code, mode, operands)
+ enum rtx_code code ATTRIBUTE_UNUSED;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+ rtx operands[2] ATTRIBUTE_UNUSED;
+{
+ return TRUE;
+}
+
+/* Produce an unsigned comparison for a given signed comparison. */
+
+static enum rtx_code
+unsigned_comparison (code)
+ enum rtx_code code;
+{
+ switch (code)
{
- if (! LEGITIMATE_INDEX_P (base_rtx) && GET_CODE (base_rtx) != REG)
+ case GT:
+ code = GTU;
+ break;
+ case LT:
+ code = LTU;
+ break;
+ case GE:
+ code = GEU;
+ break;
+ case LE:
+ code = LEU;
+ break;
+ case EQ:
+ case NE:
+ case LEU:
+ case LTU:
+ case GEU:
+ case GTU:
+ break;
+ default:
+ abort ();
+ }
+ return code;
+}
+
+/* Generate insn patterns to do an integer compare of OPERANDS. */
+
+static rtx
+ix86_expand_int_compare (code, op0, op1)
+ enum rtx_code code;
+ rtx op0, op1;
+{
+ enum machine_mode cmpmode;
+ rtx tmp, flags;
+
+ cmpmode = SELECT_CC_MODE (code, op0, op1);
+ flags = gen_rtx_REG (cmpmode, FLAGS_REG);
+
+ /* This is very simple, but making the interface the same as in the
+ FP case makes the rest of the code easier. */
+ tmp = gen_rtx_COMPARE (cmpmode, op0, op1);
+ emit_insn (gen_rtx_SET (VOIDmode, flags, tmp));
+
+ /* Return the test that should be put into the flags user, i.e.
+ the bcc, scc, or cmov instruction. */
+ return gen_rtx_fmt_ee (code, VOIDmode, flags, const0_rtx);
+}
+
+/* Generate insn patterns to do a floating point compare of OPERANDS.
+ If UNORDERED, allow for unordered compares. */
+
+static rtx
+ix86_expand_fp_compare (code, op0, op1, unordered)
+ enum rtx_code code;
+ rtx op0, op1;
+ int unordered;
+{
+ enum machine_mode fpcmp_mode;
+ enum machine_mode intcmp_mode;
+ rtx tmp;
+
+ /* When not doing IEEE compliant compares, disable unordered. */
+ if (! TARGET_IEEE_FP)
+ unordered = 0;
+ fpcmp_mode = unordered ? CCFPUmode : CCFPmode;
+
+ /* ??? If we knew whether invalid-operand exceptions were masked,
+ we could rely on fcom to raise an exception and take care of
+ NaNs. But we don't. We could know this from c9x math bits. */
+ if (TARGET_IEEE_FP)
+ unordered = 1;
+
+ /* All of the unordered compare instructions only work on registers.
+ The same is true of the XFmode compare instructions. */
+ if (unordered || GET_MODE (op0) == XFmode)
+ {
+ op0 = force_reg (GET_MODE (op0), op0);
+ op1 = force_reg (GET_MODE (op1), op1);
+ }
+ else
+ {
+ /* %%% We only allow op1 in memory; op0 must be st(0). So swap
+ things around if they appear profitable, otherwise force op0
+ into a register. */
+
+ if (standard_80387_constant_p (op0) == 0
+ || (GET_CODE (op0) == MEM
+ && ! (standard_80387_constant_p (op1) == 0
+ || GET_CODE (op1) == MEM)))
{
- obfree (storage);
- return;
+ rtx tmp;
+ tmp = op0, op0 = op1, op1 = tmp;
+ code = swap_condition (code);
+ }
+
+ if (GET_CODE (op0) != REG)
+ op0 = force_reg (GET_MODE (op0), op0);
+
+ if (CONSTANT_P (op1))
+ {
+ if (standard_80387_constant_p (op1))
+ op1 = force_reg (GET_MODE (op1), op1);
+ else
+ op1 = validize_mem (force_const_mem (GET_MODE (op1), op1));
}
}
- if (offset_adjust != 0)
+ /* %%% fcomi is probably always faster, even when dealing with memory,
+ since compare-and-branch would be three insns instead of four. */
+ if (TARGET_CMOVE && !unordered)
{
- if (offset_rtx != 0 && CONSTANT_P (offset_rtx))
- offset_rtx = plus_constant (offset_rtx, offset_adjust);
- else
- offset_rtx = const0_rtx;
+ if (GET_CODE (op0) != REG)
+ op0 = force_reg (GET_MODE (op0), op0);
+ if (GET_CODE (op1) != REG)
+ op1 = force_reg (GET_MODE (op1), op1);
+
+ tmp = gen_rtx_COMPARE (fpcmp_mode, op0, op1);
+ tmp = gen_rtx_SET (VOIDmode, gen_rtx_REG (fpcmp_mode, FLAGS_REG), tmp);
+ emit_insn (tmp);
+
+ /* The FP codes work out to act like unsigned. */
+ code = unsigned_comparison (code);
+ intcmp_mode = fpcmp_mode;
+ }
+ else
+ {
+ /* Sadness wrt reg-stack pops killing fpsr -- gotta get fnstsw first. */
- if (index_rtx)
+ rtx tmp2;
+ tmp = gen_rtx_COMPARE (fpcmp_mode, op0, op1);
+ tmp2 = gen_rtx_UNSPEC (HImode, gen_rtvec (1, tmp), 9);
+ tmp = gen_reg_rtx (HImode);
+ emit_insn (gen_rtx_SET (VOIDmode, tmp, tmp2));
+
+ if (! unordered)
{
- if (base_rtx)
+ /* We have two options here -- use sahf, or testing bits of ah
+ directly. On PPRO, they are equivalent, sahf being one byte
+ smaller. On Pentium, sahf is non-pairable while test is UV
+ pairable. */
+
+ if (TARGET_USE_SAHF || optimize_size)
{
- if (scale != 1)
- {
- ret_rtx = gen_rtx (PLUS, GET_MODE (base_rtx),
- gen_rtx (MULT, GET_MODE (index_rtx),
- index_rtx, scale_rtx),
- base_rtx);
-
- if (GET_CODE (offset_rtx) != CONST_INT
- || INTVAL (offset_rtx) != 0)
- ret_rtx = gen_rtx (PLUS, GET_MODE (ret_rtx),
- ret_rtx, offset_rtx);
- }
- else
- {
- ret_rtx = gen_rtx (PLUS, GET_MODE (index_rtx),
- index_rtx, base_rtx);
+ do_sahf:
- if (GET_CODE (offset_rtx) != CONST_INT
- || INTVAL (offset_rtx) != 0)
- ret_rtx = gen_rtx (PLUS, GET_MODE (ret_rtx),
- ret_rtx, offset_rtx);
- }
+ /* The FP codes work out to act like unsigned. */
+ code = unsigned_comparison (code);
+ emit_insn (gen_x86_sahf_1 (tmp));
+ intcmp_mode = CCmode;
}
else
{
- if (scale != 1)
- {
- ret_rtx = gen_rtx (MULT, GET_MODE (index_rtx),
- index_rtx, scale_rtx);
-
- if (GET_CODE (offset_rtx) != CONST_INT
- || INTVAL (offset_rtx) != 0)
- ret_rtx = gen_rtx (PLUS, GET_MODE (ret_rtx),
- ret_rtx, offset_rtx);
- }
- else
+ /*
+ * The numbers below correspond to the bits of the FPSW in AH.
+ * C3, C2, and C0 are in bits 0x40, 0x40, and 0x01 respectively.
+ *
+ * cmp C3 C2 C0
+ * > 0 0 0
+ * < 0 0 1
+ * = 1 0 0
+ * un 1 1 1
+ */
+
+ int mask;
+
+ switch (code)
{
- if (GET_CODE (offset_rtx) == CONST_INT
- && INTVAL (offset_rtx) == 0)
- ret_rtx = index_rtx;
- else
- ret_rtx = gen_rtx (PLUS, GET_MODE (index_rtx),
- index_rtx, offset_rtx);
+ case GT:
+ mask = 0x01;
+ code = EQ;
+ break;
+ case LT:
+ mask = 0x01;
+ code = NE;
+ break;
+ case GE:
+ /* We'd have to use `xorb 1,ah; andb 0x41,ah', so it's
+ faster in all cases to just fall back on sahf. */
+ goto do_sahf;
+ case LE:
+ mask = 0x41;
+ code = NE;
+ break;
+ case EQ:
+ mask = 0x40;
+ code = NE;
+ break;
+ case NE:
+ mask = 0x40;
+ code = EQ;
+ break;
+ default:
+ abort ();
}
+
+ emit_insn (gen_testqi_ext_0 (tmp, GEN_INT (mask)));
+ intcmp_mode = CCNOmode;
}
}
else
{
- if (base_rtx)
- {
- if (GET_CODE (offset_rtx) == CONST_INT
- && INTVAL (offset_rtx) == 0)
- ret_rtx = base_rtx;
- else
- ret_rtx = gen_rtx (PLUS, GET_MODE (base_rtx), base_rtx,
- offset_rtx);
- }
- else if (was_only_offset)
- ret_rtx = offset_rtx;
- else
+ /* In the unordered case, we have to check C2 for NaN's, which
+ doesn't happen to work out to anything nice combination-wise.
+ So do some bit twiddling on the value we've got in AH to come
+ up with an appropriate set of condition codes. */
+
+ intcmp_mode = CCNOmode;
+ switch (code)
{
- obfree (storage);
- return;
+ case GT:
+ emit_insn (gen_testqi_ext_0 (tmp, GEN_INT (0x45)));
+ code = EQ;
+ break;
+ case LT:
+ emit_insn (gen_andqi_ext_0 (tmp, tmp, GEN_INT (0x45)));
+ emit_insn (gen_cmpqi_ext_3 (tmp, GEN_INT (0x01)));
+ intcmp_mode = CCmode;
+ code = EQ;
+ break;
+ case GE:
+ emit_insn (gen_testqi_ext_0 (tmp, GEN_INT (0x05)));
+ code = EQ;
+ break;
+ case LE:
+ emit_insn (gen_andqi_ext_0 (tmp, tmp, GEN_INT (0x45)));
+ emit_insn (gen_addqi_ext_1 (tmp, tmp, constm1_rtx));
+ emit_insn (gen_cmpqi_ext_3 (tmp, GEN_INT (0x40)));
+ intcmp_mode = CCmode;
+ code = LTU;
+ break;
+ case EQ:
+ emit_insn (gen_andqi_ext_0 (tmp, tmp, GEN_INT (0x45)));
+ emit_insn (gen_cmpqi_ext_3 (tmp, GEN_INT (0x40)));
+ intcmp_mode = CCmode;
+ code = EQ;
+ break;
+ case NE:
+ emit_insn (gen_andqi_ext_0 (tmp, tmp, GEN_INT (0x45)));
+ emit_insn (gen_xorcqi_ext_1 (tmp, tmp, GEN_INT (0x40)));
+ code = NE;
+ break;
+ default:
+ abort ();
}
}
-
- XEXP (mem_rtx, 0) = ret_rtx;
- RTX_IS_SPILL_P (XEXP (mem_rtx, 0)) = is_spill_rtx;
- return;
}
+
+ /* Return the test that should be put into the flags user, i.e.
+ the bcc, scc, or cmov instruction. */
+ return gen_rtx_fmt_ee (code, VOIDmode,
+ gen_rtx_REG (intcmp_mode, FLAGS_REG),
+ const0_rtx);
+}
+
+static rtx
+ix86_expand_compare (code, unordered)
+ enum rtx_code code;
+ int unordered;
+{
+ rtx op0, op1, ret;
+ op0 = ix86_compare_op0;
+ op1 = ix86_compare_op1;
+
+ if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
+ ret = ix86_expand_fp_compare (code, op0, op1, unordered);
else
+ ret = ix86_expand_int_compare (code, op0, op1);
+
+ return ret;
+}
+
+void
+ix86_expand_branch (code, unordered, label)
+ enum rtx_code code;
+ int unordered;
+ rtx label;
+{
+ rtx tmp, lo[2], hi[2], label2;
+ enum rtx_code code1, code2, code3;
+
+ if (GET_MODE (ix86_compare_op0) != DImode)
{
- obfree (storage);
+ tmp = ix86_expand_compare (code, unordered);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode, label),
+ pc_rtx);
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
return;
}
-}
-#endif /* NOTYET */
-\f
-/* Return 1 if the first insn to set cc before INSN also sets the register
- REG_RTX; otherwise return 0. */
-int
-last_to_set_cc (reg_rtx, insn)
- rtx reg_rtx, insn;
-{
- rtx prev_insn = PREV_INSN (insn);
- while (prev_insn)
+ /* Expand DImode branch into multiple compare+branch. */
+
+ if (CONSTANT_P (ix86_compare_op0) && ! CONSTANT_P (ix86_compare_op1))
{
- if (GET_CODE (prev_insn) == NOTE)
- ;
+ tmp = ix86_compare_op0;
+ ix86_compare_op0 = ix86_compare_op1;
+ ix86_compare_op1 = tmp;
+ code = swap_condition (code);
+ }
+ split_di (&ix86_compare_op0, 1, lo+0, hi+0);
+ split_di (&ix86_compare_op1, 1, lo+1, hi+1);
- else if (GET_CODE (prev_insn) == INSN)
- {
- if (GET_CODE (PATTERN (prev_insn)) != SET)
- return (0);
+ /* When comparing for equality, we can use (hi0^hi1)|(lo0^lo1) to avoid
+ two branches. This costs one extra insn, so disable when optimizing
+ for size. */
- if (rtx_equal_p (SET_DEST (PATTERN (prev_insn)), reg_rtx))
- {
- if (sets_condition_code (SET_SRC (PATTERN (prev_insn))))
- return (1);
+ if ((code == EQ || code == NE)
+ && (!optimize_size
+ || hi[1] == const0_rtx || lo[1] == const0_rtx))
+ {
+ rtx xor0, xor1;
- return (0);
- }
+ xor1 = hi[0];
+ if (hi[1] != const0_rtx)
+ {
+ xor1 = expand_binop (SImode, xor_optab, xor1, hi[1],
+ NULL_RTX, 0, OPTAB_WIDEN);
+ }
- else if (! doesnt_set_condition_code (SET_SRC (PATTERN (prev_insn))))
- return (0);
+ xor0 = lo[0];
+ if (lo[1] != const0_rtx)
+ {
+ xor0 = expand_binop (SImode, xor_optab, xor0, lo[1],
+ NULL_RTX, 0, OPTAB_WIDEN);
}
- else
- return (0);
+ tmp = expand_binop (SImode, ior_optab, xor1, xor0,
+ NULL_RTX, 0, OPTAB_WIDEN);
- prev_insn = PREV_INSN (prev_insn);
+ ix86_compare_op0 = tmp;
+ ix86_compare_op1 = const0_rtx;
+ ix86_expand_branch (code, unordered, label);
+ return;
}
- return (0);
-}
-\f
-int
-doesnt_set_condition_code (pat)
- rtx pat;
-{
- switch (GET_CODE (pat))
+ /* Otherwise, if we are doing less-than, op1 is a constant and the
+ low word is zero, then we can just examine the high word. */
+
+ if (GET_CODE (hi[1]) == CONST_INT && lo[1] == const0_rtx
+ && (code == LT || code == LTU))
{
- case MEM:
- case REG:
- return 1;
+ ix86_compare_op0 = hi[0];
+ ix86_compare_op1 = hi[1];
+ ix86_expand_branch (code, unordered, label);
+ return;
+ }
- default:
- return 0;
+ /* Otherwise, we need two or three jumps. */
+
+ label2 = gen_label_rtx ();
+ code1 = code;
+ code2 = swap_condition (code);
+ code3 = unsigned_condition (code);
+
+ switch (code)
+ {
+ case LT: case GT: case LTU: case GTU:
+ break;
+
+ case LE: code1 = LT; code2 = GT; break;
+ case GE: code1 = GT; code2 = LT; break;
+ case LEU: code1 = LTU; code2 = GTU; break;
+ case GEU: code1 = GTU; code2 = LTU; break;
+
+ case EQ: code1 = NIL; code2 = NE; break;
+ case NE: code2 = NIL; break;
+
+ default:
+ abort ();
}
+
+ /*
+ * a < b =>
+ * if (hi(a) < hi(b)) goto true;
+ * if (hi(a) > hi(b)) goto false;
+ * if (lo(a) < lo(b)) goto true;
+ * false:
+ */
+
+ ix86_compare_op0 = hi[0];
+ ix86_compare_op1 = hi[1];
+
+ if (code1 != NIL)
+ ix86_expand_branch (code1, unordered, label);
+ if (code2 != NIL)
+ ix86_expand_branch (code2, unordered, label2);
+
+ ix86_compare_op0 = lo[0];
+ ix86_compare_op1 = lo[1];
+ ix86_expand_branch (code3, unordered, label);
+
+ if (code2 != NIL)
+ emit_label (label2);
}
-\f
+
int
-sets_condition_code (pat)
- rtx pat;
+ix86_expand_setcc (code, unordered, dest)
+ enum rtx_code code;
+ int unordered;
+ rtx dest;
{
- switch (GET_CODE (pat))
+ rtx ret, tmp;
+ int type;
+
+ if (GET_MODE (ix86_compare_op0) == DImode)
+ return 0; /* FAIL */
+
+ /* Three modes of generation:
+ 0 -- destination does not overlap compare sources:
+ clear dest first, emit strict_low_part setcc.
+ 1 -- destination does overlap compare sources:
+ emit subreg setcc, zero extend.
+ 2 -- destination is in QImode:
+ emit setcc only.
+ */
+
+ type = 0;
+ /* %%% reload problems with in-out. Revisit. */
+ type = 1;
+
+ if (GET_MODE (dest) == QImode)
+ type = 2;
+ else if (reg_overlap_mentioned_p (dest, ix86_compare_op0)
+ || reg_overlap_mentioned_p (dest, ix86_compare_op0))
+ type = 1;
+
+ if (type == 0)
+ emit_move_insn (dest, const0_rtx);
+
+ ret = ix86_expand_compare (code, unordered);
+ PUT_MODE (ret, QImode);
+
+ tmp = dest;
+ if (type == 0)
{
- case PLUS:
- case MINUS:
- case AND:
- case IOR:
- case XOR:
- case NOT:
- case NEG:
- case MULT:
- case DIV:
- case MOD:
- case UDIV:
- case UMOD:
- return 1;
+ tmp = gen_lowpart (QImode, dest);
+ tmp = gen_rtx_STRICT_LOW_PART (VOIDmode, tmp);
+ }
+ else if (type == 1)
+ {
+ if (!cse_not_expected)
+ tmp = gen_reg_rtx (QImode);
+ else
+ tmp = gen_lowpart (QImode, dest);
+ }
- default:
- return (0);
+ emit_insn (gen_rtx_SET (VOIDmode, tmp, ret));
+
+ if (type == 1)
+ {
+ rtx clob;
+
+ tmp = gen_rtx_ZERO_EXTEND (GET_MODE (dest), tmp);
+ tmp = gen_rtx_SET (VOIDmode, dest, tmp);
+ clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, FLAGS_REG));
+ tmp = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, tmp, clob));
+ emit_insn (tmp);
}
+
+ return 1; /* DONE */
}
-\f
+
int
-str_immediate_operand (op, mode)
- register rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+ix86_expand_int_movcc (operands)
+ rtx operands[];
{
- if (GET_CODE (op) == CONST_INT && INTVAL (op) <= 32 && INTVAL (op) >= 0)
- return 1;
+ enum rtx_code code = GET_CODE (operands[1]), compare_code;
+ rtx compare_seq, compare_op;
- return 0;
+ start_sequence ();
+ compare_op = ix86_expand_compare (code, code == EQ || code == NE);
+ compare_seq = gen_sequence ();
+ end_sequence ();
+
+ compare_code = GET_CODE (compare_op);
+
+ /* Don't attempt mode expansion here -- if we had to expand 5 or 6
+ HImode insns, we'd be swallowed in word prefix ops. */
+
+ if (GET_MODE (operands[0]) != HImode
+ && GET_CODE (operands[2]) == CONST_INT
+ && GET_CODE (operands[3]) == CONST_INT)
+ {
+ rtx out = operands[0];
+ HOST_WIDE_INT ct = INTVAL (operands[2]);
+ HOST_WIDE_INT cf = INTVAL (operands[3]);
+ HOST_WIDE_INT diff;
+
+ /* Special cases: */
+ if (ct == 0)
+ {
+ ct = cf;
+ cf = 0;
+ compare_code = reverse_condition (compare_code);
+ code = reverse_condition (code);
+ }
+ if (cf == 0 && ct == -1 && (compare_code == LTU || compare_code == GEU))
+ {
+ /*
+ * xorl dest,dest
+ * cmpl op0,op1
+ * sbbl dest,dest
+ *
+ * Size 6.
+ */
+
+ /* Detect overlap between destination and compare sources. */
+ rtx tmp = out;
+
+ if (reg_overlap_mentioned_p (out, ix86_compare_op0)
+ || reg_overlap_mentioned_p (out, ix86_compare_op0))
+ tmp = gen_reg_rtx (SImode);
+
+ emit_insn (compare_seq);
+ emit_insn (gen_x86_movsicc_0_m1 (tmp));
+
+ if (compare_code == GEU)
+ emit_insn (gen_one_cmplsi2 (tmp, tmp));
+
+ if (tmp != out)
+ emit_move_insn (out, tmp);
+
+ return 1; /* DONE */
+ }
+
+ diff = ct - cf;
+ if (diff < 0)
+ {
+ HOST_WIDE_INT tmp;
+ tmp = ct, ct = cf, cf = tmp;
+ diff = -diff;
+ compare_code = reverse_condition (compare_code);
+ code = reverse_condition (code);
+ }
+ if (diff == 1 || diff == 2 || diff == 4 || diff == 8
+ || diff == 3 || diff == 5 || diff == 9)
+ {
+ /*
+ * xorl dest,dest
+ * cmpl op1,op2
+ * setcc dest
+ * lea cf(dest*(ct-cf)),dest
+ *
+ * Size 14.
+ *
+ * This also catches the degenerate setcc-only case.
+ */
+
+ rtx tmp;
+ int nops;
+
+ out = emit_store_flag (out, code, ix86_compare_op0,
+ ix86_compare_op1, VOIDmode, 0, 1);
+
+ nops = 0;
+ if (diff == 1)
+ tmp = out;
+ else
+ {
+ tmp = gen_rtx_MULT (SImode, out, GEN_INT (diff & ~1));
+ nops++;
+ if (diff & 1)
+ {
+ tmp = gen_rtx_PLUS (SImode, tmp, out);
+ nops++;
+ }
+ }
+ if (cf != 0)
+ {
+ tmp = gen_rtx_PLUS (SImode, tmp, GEN_INT (cf));
+ nops++;
+ }
+ if (tmp != out)
+ {
+ if (nops == 0)
+ emit_move_insn (out, tmp);
+ else if (nops == 1)
+ {
+ rtx clob;
+
+ clob = gen_rtx_REG (CCmode, FLAGS_REG);
+ clob = gen_rtx_CLOBBER (VOIDmode, clob);
+
+ tmp = gen_rtx_SET (VOIDmode, out, tmp);
+ tmp = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, tmp, clob));
+ emit_insn (tmp);
+ }
+ else
+ emit_insn (gen_rtx_SET (VOIDmode, out, tmp));
+ }
+ if (out != operands[0])
+ emit_move_insn (operands[0], out);
+
+ return 1; /* DONE */
+ }
+
+ /*
+ * General case: Jumpful:
+ * xorl dest,dest cmpl op1, op2
+ * cmpl op1, op2 movl ct, dest
+ * setcc dest jcc 1f
+ * decl dest movl cf, dest
+ * andl (cf-ct),dest 1:
+ * addl ct,dest
+ *
+ * Size 20. Size 14.
+ *
+ * This is reasonably steep, but branch mispredict costs are
+ * high on modern cpus, so consider failing only if optimizing
+ * for space.
+ *
+ * %%% Parameterize branch_cost on the tuning architecture, then
+ * use that. The 80386 couldn't care less about mispredicts.
+ */
+
+ if (!optimize_size && !TARGET_CMOVE)
+ {
+ if (ct == 0)
+ {
+ ct = cf;
+ cf = 0;
+ compare_code = reverse_condition (compare_code);
+ code = reverse_condition (code);
+ }
+
+ out = emit_store_flag (out, code, ix86_compare_op0,
+ ix86_compare_op1, VOIDmode, 0, 1);
+
+ emit_insn (gen_addsi3 (out, out, constm1_rtx));
+ emit_insn (gen_andsi3 (out, out, GEN_INT (cf-ct)));
+ if (ct != 0)
+ emit_insn (gen_addsi3 (out, out, GEN_INT (ct)));
+ if (out != operands[0])
+ emit_move_insn (operands[0], out);
+
+ return 1; /* DONE */
+ }
+ }
+
+ if (!TARGET_CMOVE)
+ {
+ /* Try a few things more with specific constants and a variable. */
+
+ optab op = NULL;
+ rtx var, orig_out, out, tmp;
+
+ if (optimize_size)
+ return 0; /* FAIL */
+
+ /* If one of the two operands is an interesting constant, load a
+ constant with the above and mask it in with a logical operation. */
+
+ if (GET_CODE (operands[2]) == CONST_INT)
+ {
+ var = operands[3];
+ if (INTVAL (operands[2]) == 0)
+ operands[3] = constm1_rtx, op = and_optab;
+ else if (INTVAL (operands[2]) == -1)
+ operands[3] = const0_rtx, op = ior_optab;
+ }
+ else if (GET_CODE (operands[3]) == CONST_INT)
+ {
+ var = operands[2];
+ if (INTVAL (operands[3]) == 0)
+ operands[2] = constm1_rtx, op = and_optab;
+ else if (INTVAL (operands[3]) == -1)
+ operands[2] = const0_rtx, op = ior_optab;
+ }
+
+ if (op == NULL)
+ return 0; /* FAIL */
+
+ orig_out = operands[0];
+ tmp = gen_reg_rtx (GET_MODE (orig_out));
+ operands[0] = tmp;
+
+ /* Recurse to get the constant loaded. */
+ if (ix86_expand_int_movcc (operands) == 0)
+ return 0; /* FAIL */
+
+ /* Mask in the interesting variable. */
+ out = expand_binop (GET_MODE (orig_out), op, var, tmp, orig_out, 0,
+ OPTAB_WIDEN);
+ if (out != orig_out)
+ emit_move_insn (orig_out, out);
+
+ return 1; /* DONE */
+ }
+
+ /*
+ * For comparison with above,
+ *
+ * movl cf,dest
+ * movl ct,tmp
+ * cmpl op1,op2
+ * cmovcc tmp,dest
+ *
+ * Size 15.
+ */
+
+ if (! nonimmediate_operand (operands[2], GET_MODE (operands[0])))
+ operands[2] = force_reg (GET_MODE (operands[0]), operands[2]);
+ if (! nonimmediate_operand (operands[3], GET_MODE (operands[0])))
+ operands[3] = force_reg (GET_MODE (operands[0]), operands[3]);
+
+ emit_insn (compare_seq);
+ emit_insn (gen_rtx_SET (VOIDmode, operands[0],
+ gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]),
+ compare_op, operands[2],
+ operands[3])));
+
+ return 1; /* DONE */
}
-\f
+
int
-is_fp_insn (insn)
- rtx insn;
+ix86_expand_fp_movcc (operands)
+ rtx operands[];
{
- if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET
- && (GET_MODE (SET_DEST (PATTERN (insn))) == DFmode
- || GET_MODE (SET_DEST (PATTERN (insn))) == SFmode
- || GET_MODE (SET_DEST (PATTERN (insn))) == XFmode))
- return 1;
+ enum rtx_code code;
+ enum machine_mode mode;
+ rtx tmp;
- return 0;
-}
+ /* The floating point conditional move instructions don't directly
+ support conditions resulting from a signed integer comparison. */
-/* Return 1 if the mode of the SET_DEST of insn is floating point
- and it is not an fld or a move from memory to memory.
- Otherwise return 0 */
+ code = GET_CODE (operands[1]);
+ switch (code)
+ {
+ case LT:
+ case LE:
+ case GE:
+ case GT:
+ tmp = gen_reg_rtx (QImode);
+ ix86_expand_setcc (code, 0, tmp);
+ code = NE;
+ ix86_compare_op0 = tmp;
+ ix86_compare_op1 = const0_rtx;
+ break;
+
+ default:
+ break;
+ }
-int
-is_fp_dest (insn)
- rtx insn;
-{
- if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET
- && (GET_MODE (SET_DEST (PATTERN (insn))) == DFmode
- || GET_MODE (SET_DEST (PATTERN (insn))) == SFmode
- || GET_MODE (SET_DEST (PATTERN (insn))) == XFmode)
- && GET_CODE (SET_DEST (PATTERN (insn))) == REG
- && REGNO (SET_DEST (PATTERN (insn))) >= FIRST_FLOAT_REG
- && GET_CODE (SET_SRC (PATTERN (insn))) != MEM)
- return 1;
+ mode = SELECT_CC_MODE (code, ix86_compare_op0, ix86_compare_op1);
+ emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_REG (mode, FLAGS_REG),
+ gen_rtx_COMPARE (mode,
+ ix86_compare_op0,
+ ix86_compare_op1)));
+ emit_insn (gen_rtx_SET (VOIDmode, operands[0],
+ gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]),
+ gen_rtx_fmt_ee (code, VOIDmode,
+ gen_rtx_REG (mode, FLAGS_REG),
+ const0_rtx),
+ operands[2],
+ operands[3])));
- return 0;
+ return 1;
}
-/* Return 1 if the mode of the SET_DEST of INSN is floating point and is
- memory and the source is a register. */
-
int
-is_fp_store (insn)
- rtx insn;
+ix86_split_movdi (operands)
+ rtx operands[];
{
- if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET
- && (GET_MODE (SET_DEST (PATTERN (insn))) == DFmode
- || GET_MODE (SET_DEST (PATTERN (insn))) == SFmode
- || GET_MODE (SET_DEST (PATTERN (insn))) == XFmode)
- && GET_CODE (SET_DEST (PATTERN (insn))) == MEM
- && GET_CODE (SET_SRC (PATTERN (insn))) == REG)
- return 1;
+ split_di (operands+0, 1, operands+2, operands+3);
+ split_di (operands+1, 1, operands+4, operands+5);
+ if (reg_overlap_mentioned_p (operands[2], operands[1]))
+ {
+ rtx tmp;
+ if (!reg_overlap_mentioned_p (operands[3], operands[4]))
+ {
+ tmp = operands[2], operands[2] = operands[3], operands[3] = tmp;
+ tmp = operands[4], operands[4] = operands[5], operands[5] = tmp;
+ }
+ else
+ {
+ emit_insn (gen_push (operands[4]));
+ emit_insn (gen_rtx_SET (VOIDmode, operands[3], operands[5]));
+ emit_insn (gen_popsi1 (operands[2]));
+
+ return 1; /* DONE */
+ }
+ }
return 0;
}
-\f
-/* Return 1 if DEP_INSN sets a register which INSN uses as a base
- or index to reference memory.
- otherwise return 0 */
-int
-agi_dependent (insn, dep_insn)
- rtx insn, dep_insn;
+void
+ix86_split_ashldi (operands, scratch)
+ rtx *operands, scratch;
{
- int push = 0, push_dep = 0;
- if (GET_CODE (dep_insn) == INSN
- && GET_CODE (PATTERN (dep_insn)) == SET
- && GET_CODE (SET_DEST (PATTERN (dep_insn))) == REG
- && reg_mentioned_in_mem (SET_DEST (PATTERN (dep_insn)), insn))
- return 1;
+ rtx low[2], high[2];
+ int count;
- if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET
- && GET_CODE (SET_DEST (PATTERN (insn))) == MEM
- && push_operand (SET_DEST (PATTERN (insn)),
- GET_MODE (SET_DEST (PATTERN (insn)))))
- push = 1;
+ if (GET_CODE (operands[2]) == CONST_INT)
+ {
+ split_di (operands, 2, low, high);
+ count = INTVAL (operands[2]) & 63;
- if (GET_CODE (dep_insn) == INSN && GET_CODE (PATTERN (dep_insn)) == SET
- && GET_CODE (SET_DEST (PATTERN (dep_insn))) == MEM
- && push_operand (SET_DEST (PATTERN (dep_insn)),
- GET_MODE (SET_DEST (PATTERN (dep_insn)))))
- push_dep = 1;
+ if (count >= 32)
+ {
+ emit_move_insn (high[0], low[1]);
+ emit_move_insn (low[0], const0_rtx);
- /* CPUs contain special hardware to allow two pushes. */
- if (push && push_dep)
- return 0;
+ if (count > 32)
+ emit_insn (gen_ashlsi3 (high[0], high[0], GEN_INT (count - 32)));
+ }
+ else
+ {
+ if (!rtx_equal_p (operands[0], operands[1]))
+ emit_move_insn (operands[0], operands[1]);
+ emit_insn (gen_x86_shld_1 (high[0], low[0], GEN_INT (count)));
+ emit_insn (gen_ashlsi3 (low[0], low[0], GEN_INT (count)));
+ }
+ }
+ else
+ {
+ if (!rtx_equal_p (operands[0], operands[1]))
+ emit_move_insn (operands[0], operands[1]);
- /* Push operation implicitly change stack pointer causing AGI stalls. */
- if (push_dep && reg_mentioned_in_mem (stack_pointer_rtx, insn))
- return 1;
+ split_di (operands, 1, low, high);
- /* Push also implicitly read stack pointer. */
- if (push && modified_in_p (stack_pointer_rtx, dep_insn))
- return 1;
+ emit_insn (gen_x86_shld_1 (high[0], low[0], operands[2]));
+ emit_insn (gen_ashlsi3 (low[0], low[0], operands[2]));
- return 0;
+ if (TARGET_CMOVE && (! reload_completed || scratch))
+ {
+ if (! reload_completed)
+ scratch = force_reg (SImode, const0_rtx);
+ else
+ emit_move_insn (scratch, const0_rtx);
+
+ emit_insn (gen_x86_shift_adj_1 (high[0], low[0], operands[2],
+ scratch));
+ }
+ else
+ emit_insn (gen_x86_shift_adj_2 (high[0], low[0], operands[2]));
+ }
}
-\f
-/* Return 1 if reg is used in rtl as a base or index for a memory ref
- otherwise return 0. */
-int
-reg_mentioned_in_mem (reg, rtl)
- rtx reg, rtl;
+void
+ix86_split_ashrdi (operands, scratch)
+ rtx *operands, scratch;
{
- register const char *fmt;
- register int i, j;
- register enum rtx_code code;
+ rtx low[2], high[2];
+ int count;
- if (rtl == NULL)
- return 0;
+ if (GET_CODE (operands[2]) == CONST_INT)
+ {
+ split_di (operands, 2, low, high);
+ count = INTVAL (operands[2]) & 63;
- code = GET_CODE (rtl);
+ if (count >= 32)
+ {
+ emit_move_insn (low[0], high[1]);
- switch (code)
+ if (! reload_completed)
+ emit_insn (gen_ashrsi3 (high[0], low[0], GEN_INT (31)));
+ else
+ {
+ emit_move_insn (high[0], low[0]);
+ emit_insn (gen_ashrsi3 (high[0], high[0], GEN_INT (31)));
+ }
+
+ if (count > 32)
+ emit_insn (gen_ashrsi3 (low[0], low[0], GEN_INT (count - 32)));
+ }
+ else
+ {
+ if (!rtx_equal_p (operands[0], operands[1]))
+ emit_move_insn (operands[0], operands[1]);
+ emit_insn (gen_x86_shrd_1 (low[0], high[0], GEN_INT (count)));
+ emit_insn (gen_ashrsi3 (high[0], high[0], GEN_INT (count)));
+ }
+ }
+ else
{
- case HIGH:
- case CONST_INT:
- case CONST:
- case CONST_DOUBLE:
- case SYMBOL_REF:
- case LABEL_REF:
- case PC:
- case CC0:
- case SUBREG:
- return 0;
- default:
- break;
+ if (!rtx_equal_p (operands[0], operands[1]))
+ emit_move_insn (operands[0], operands[1]);
+
+ split_di (operands, 1, low, high);
+
+ emit_insn (gen_x86_shrd_1 (low[0], high[0], operands[2]));
+ emit_insn (gen_ashrsi3 (high[0], high[0], operands[2]));
+
+ if (TARGET_CMOVE && (!reload_completed || scratch))
+ {
+ if (! reload_completed)
+ scratch = gen_reg_rtx (SImode);
+ emit_move_insn (scratch, high[0]);
+ emit_insn (gen_ashrsi3 (scratch, scratch, GEN_INT (31)));
+ emit_insn (gen_x86_shift_adj_1 (low[0], high[0], operands[2],
+ scratch));
+ }
+ else
+ emit_insn (gen_x86_shift_adj_3 (low[0], high[0], operands[2]));
}
+}
- if (code == MEM && reg_mentioned_p (reg, rtl))
- return 1;
+void
+ix86_split_lshrdi (operands, scratch)
+ rtx *operands, scratch;
+{
+ rtx low[2], high[2];
+ int count;
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ if (GET_CODE (operands[2]) == CONST_INT)
{
- if (fmt[i] == 'E')
+ split_di (operands, 2, low, high);
+ count = INTVAL (operands[2]) & 63;
+
+ if (count >= 32)
{
- for (j = XVECLEN (rtl, i) - 1; j >= 0; j--)
- if (reg_mentioned_in_mem (reg, XVECEXP (rtl, i, j)))
- return 1;
- }
+ emit_move_insn (low[0], high[1]);
+ emit_move_insn (high[0], const0_rtx);
- else if (fmt[i] == 'e' && reg_mentioned_in_mem (reg, XEXP (rtl, i)))
- return 1;
+ if (count > 32)
+ emit_insn (gen_lshrsi3 (low[0], low[0], GEN_INT (count - 32)));
+ }
+ else
+ {
+ if (!rtx_equal_p (operands[0], operands[1]))
+ emit_move_insn (operands[0], operands[1]);
+ emit_insn (gen_x86_shrd_1 (low[0], high[0], GEN_INT (count)));
+ emit_insn (gen_lshrsi3 (high[0], high[0], GEN_INT (count)));
+ }
}
+ else
+ {
+ if (!rtx_equal_p (operands[0], operands[1]))
+ emit_move_insn (operands[0], operands[1]);
- return 0;
+ split_di (operands, 1, low, high);
+
+ emit_insn (gen_x86_shrd_1 (low[0], high[0], operands[2]));
+ emit_insn (gen_lshrsi3 (high[0], high[0], operands[2]));
+
+ /* Heh. By reversing the arguments, we can reuse this pattern. */
+ if (TARGET_CMOVE && (! reload_completed || scratch))
+ {
+ if (! reload_completed)
+ scratch = force_reg (SImode, const0_rtx);
+ else
+ emit_move_insn (scratch, const0_rtx);
+
+ emit_insn (gen_x86_shift_adj_1 (low[0], high[0], operands[2],
+ scratch));
+ }
+ else
+ emit_insn (gen_x86_shift_adj_2 (low[0], high[0], operands[2]));
+ }
}
-\f
-/* Output the appropriate insns for doing strlen if not just doing repnz; scasb
- operands[0] = result, initialized with the startaddress
- operands[1] = alignment of the address.
- operands[2] = scratch register, initialized with the startaddress when
- not aligned, otherwise undefined
+/* Expand the appropriate insns for doing strlen if not just doing
+ repnz; scasb
+
+ out = result, initialized with the start address
+ align_rtx = alignment of the address.
+ scratch = scratch register, initialized with the startaddress when
+ not aligned, otherwise undefined
This is just the body. It needs the initialisations mentioned above and
some address computing at the end. These things are done in i386.md. */
-char *
-output_strlen_unroll (operands)
- rtx operands[];
+void
+ix86_expand_strlensi_unroll_1 (out, align_rtx, scratch)
+ rtx out, align_rtx, scratch;
{
- rtx xops[18];
-
- xops[0] = operands[0]; /* Result */
- /* operands[1]; * Alignment */
- xops[1] = operands[2]; /* Scratch */
- xops[2] = GEN_INT (0);
- xops[3] = GEN_INT (2);
- xops[4] = GEN_INT (3);
- xops[5] = GEN_INT (4);
- /* xops[6] = gen_label_rtx (); * label when aligned to 3-byte */
- /* xops[7] = gen_label_rtx (); * label when aligned to 2-byte */
- xops[8] = gen_label_rtx (); /* label of main loop */
-
- if (TARGET_USE_Q_REG && QI_REG_P (xops[1]))
- xops[9] = gen_label_rtx (); /* pentium optimisation */
-
- xops[10] = gen_label_rtx (); /* end label 2 */
- xops[11] = gen_label_rtx (); /* end label 1 */
- xops[12] = gen_label_rtx (); /* end label */
- /* xops[13] * Temporary used */
- xops[14] = GEN_INT (0xff);
- xops[15] = GEN_INT (0xff00);
- xops[16] = GEN_INT (0xff0000);
- xops[17] = GEN_INT (0xff000000);
+ int align;
+ rtx tmp;
+ rtx align_2_label = NULL_RTX;
+ rtx align_3_label = NULL_RTX;
+ rtx align_4_label = gen_label_rtx ();
+ rtx end_0_label = gen_label_rtx ();
+ rtx end_2_label = gen_label_rtx ();
+ rtx end_3_label = gen_label_rtx ();
+ rtx mem;
+ rtx flags = gen_rtx_REG (CCNOmode, FLAGS_REG);
+
+ align = 0;
+ if (GET_CODE (align_rtx) == CONST_INT)
+ align = INTVAL (align_rtx);
/* Loop to check 1..3 bytes for null to get an aligned pointer. */
/* Is there a known alignment and is it less than 4? */
- if (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) < 4)
+ if (align < 4)
{
/* Is there a known alignment and is it not 2? */
- if (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 2)
+ if (align != 2)
{
- xops[6] = gen_label_rtx (); /* Label when aligned to 3-byte */
- xops[7] = gen_label_rtx (); /* Label when aligned to 2-byte */
-
- /* Leave just the 3 lower bits.
- If this is a q-register, then the high part is used later
- therefore use andl rather than andb. */
- output_asm_insn (AS2 (and%L1,%4,%1), xops);
-
- /* Is aligned to 4-byte address when zero */
- output_asm_insn (AS1 (je,%l8), xops);
-
- /* Side-effect even Parity when %eax == 3 */
- output_asm_insn (AS1 (jp,%6), xops);
-
- /* Is it aligned to 2 bytes ? */
- if (QI_REG_P (xops[1]))
- output_asm_insn (AS2 (cmp%L1,%3,%1), xops);
- else
- output_asm_insn (AS2 (cmp%L1,%3,%1), xops);
-
- output_asm_insn (AS1 (je,%7), xops);
+ align_3_label = gen_label_rtx (); /* Label when aligned to 3-byte */
+ align_2_label = gen_label_rtx (); /* Label when aligned to 2-byte */
+
+ /* Leave just the 3 lower bits. */
+ align_rtx = expand_binop (SImode, and_optab, scratch, GEN_INT (3),
+ NULL_RTX, 0, OPTAB_WIDEN);
+
+ emit_insn (gen_cmpsi_0 (align_rtx, const0_rtx));
+
+ tmp = gen_rtx_EQ (VOIDmode, flags, const0_rtx);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode,
+ align_4_label),
+ pc_rtx);
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
+
+ emit_insn (gen_cmpsi_1 (align_rtx, GEN_INT (2)));
+
+ tmp = gen_rtx_EQ (VOIDmode, flags, const0_rtx);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode,
+ align_2_label),
+ pc_rtx);
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
+
+ tmp = gen_rtx_GTU (VOIDmode, flags, const0_rtx);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode,
+ align_3_label),
+ pc_rtx);
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
}
else
{
/* Since the alignment is 2, we have to check 2 or 0 bytes;
check if is aligned to 4 - byte. */
- output_asm_insn (AS2 (and%L1,%3,%1), xops);
- /* Is aligned to 4-byte address when zero */
- output_asm_insn (AS1 (je,%l8), xops);
+ align_rtx = expand_binop (SImode, and_optab, scratch, GEN_INT (2),
+ NULL_RTX, 0, OPTAB_WIDEN);
+
+ emit_insn (gen_cmpsi_0 (align_rtx, const0_rtx));
+
+ tmp = gen_rtx_EQ (VOIDmode, flags, const0_rtx);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode,
+ align_4_label),
+ pc_rtx);
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
}
- xops[13] = gen_rtx_MEM (QImode, xops[0]);
+ mem = gen_rtx_MEM (QImode, out);
- /* Now compare the bytes; compare with the high part of a q-reg
- gives shorter code. */
- if (QI_REG_P (xops[1]))
- {
- /* Compare the first n unaligned byte on a byte per byte basis. */
- output_asm_insn (AS2 (cmp%B1,%h1,%13), xops);
+ /* Now compare the bytes. */
- /* When zero we reached the end. */
- output_asm_insn (AS1 (je,%l12), xops);
+ /* Compare the first n unaligned byte on a byte per byte basis. */
+ emit_insn (gen_cmpqi_0 (mem, const0_rtx));
- /* Increment the address. */
- output_asm_insn (AS1 (inc%L0,%0), xops);
+ tmp = gen_rtx_EQ (VOIDmode, flags, const0_rtx);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode, end_0_label),
+ pc_rtx);
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
- /* Not needed with an alignment of 2 */
- if (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 2)
- {
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
- CODE_LABEL_NUMBER (xops[7]));
- output_asm_insn (AS2 (cmp%B1,%h1,%13), xops);
- output_asm_insn (AS1 (je,%l12), xops);
- output_asm_insn (AS1 (inc%L0,%0), xops);
-
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
- CODE_LABEL_NUMBER (xops[6]));
- }
+ /* Increment the address. */
+ emit_insn (gen_addsi3 (out, out, const1_rtx));
- output_asm_insn (AS2 (cmp%B1,%h1,%13), xops);
- }
- else
- {
- output_asm_insn (AS2 (cmp%B13,%2,%13), xops);
- output_asm_insn (AS1 (je,%l12), xops);
- output_asm_insn (AS1 (inc%L0,%0), xops);
+ /* Not needed with an alignment of 2 */
+ if (align != 2)
+ {
+ emit_label (align_2_label);
- /* Not needed with an alignment of 2 */
- if (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 2)
- {
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
- CODE_LABEL_NUMBER (xops[7]));
- output_asm_insn (AS2 (cmp%B13,%2,%13), xops);
- output_asm_insn (AS1 (je,%l12), xops);
- output_asm_insn (AS1 (inc%L0,%0), xops);
-
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
- CODE_LABEL_NUMBER (xops[6]));
- }
+ emit_insn (gen_cmpqi_0 (mem, const0_rtx));
- output_asm_insn (AS2 (cmp%B13,%2,%13), xops);
- }
+ tmp = gen_rtx_EQ (VOIDmode, flags, const0_rtx);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode,
+ end_0_label),
+ pc_rtx);
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
+
+ emit_insn (gen_addsi3 (out, out, const1_rtx));
+
+ emit_label (align_3_label);
+ }
+
+ emit_insn (gen_cmpqi_0 (mem, const0_rtx));
- output_asm_insn (AS1 (je,%l12), xops);
- output_asm_insn (AS1 (inc%L0,%0), xops);
+ tmp = gen_rtx_EQ (VOIDmode, flags, const0_rtx);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode, end_0_label),
+ pc_rtx);
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
+
+ emit_insn (gen_addsi3 (out, out, const1_rtx));
}
- /* Generate loop to check 4 bytes at a time. It is not a good idea to
- align this loop. It gives only huge programs, but does not help to
- speed up. */
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (xops[8]));
+ /* Generate loop to check 4 bytes at a time. It is not a good idea to
+ align this loop. It gives only huge programs, but does not help to
+ speed up. */
+ emit_label (align_4_label);
- xops[13] = gen_rtx_MEM (SImode, xops[0]);
- output_asm_insn (AS2 (mov%L1,%13,%1), xops);
+ mem = gen_rtx_MEM (SImode, out);
+ emit_move_insn (scratch, mem);
- if (QI_REG_P (xops[1]))
- {
- /* On i586 it is faster to combine the hi- and lo- part as
- a kind of lookahead. If anding both yields zero, then one
- of both *could* be zero, otherwise none of both is zero;
- this saves one instruction, on i486 this is slower
- tested with P-90, i486DX2-66, AMD486DX2-66 */
- if (TARGET_PENTIUM)
- {
- output_asm_insn (AS2 (test%B1,%h1,%b1), xops);
- output_asm_insn (AS1 (jne,%l9), xops);
- }
+ /* Check first byte. */
+ emit_insn (gen_cmpqi_0 (gen_lowpart (QImode, scratch), const0_rtx));
+ tmp = gen_rtx_EQ (VOIDmode, flags, const0_rtx);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode, end_0_label),
+ pc_rtx);
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
+
+ /* Check second byte. */
+ emit_insn (gen_cmpqi_ext_3 (scratch, const0_rtx));
+ tmp = gen_rtx_EQ (VOIDmode, flags, const0_rtx);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode, end_3_label),
+ pc_rtx);
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
+
+ /* Check third byte. */
+ emit_insn (gen_testsi_1 (scratch, GEN_INT (0x00ff0000)));
+ tmp = gen_rtx_EQ (VOIDmode, flags, const0_rtx);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode, end_2_label),
+ pc_rtx);
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
+
+ /* Check fourth byte and increment address. */
+ emit_insn (gen_addsi3 (out, out, GEN_INT (4)));
+ emit_insn (gen_testsi_1 (scratch, GEN_INT (0xff000000)));
+ tmp = gen_rtx_NE (VOIDmode, flags, const0_rtx);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode, align_4_label),
+ pc_rtx);
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
+
+ /* Now generate fixups when the compare stops within a 4-byte word. */
+ emit_insn (gen_subsi3 (out, out, GEN_INT (3)));
+
+ emit_label (end_2_label);
+ emit_insn (gen_addsi3 (out, out, const1_rtx));
+
+ emit_label (end_3_label);
+ emit_insn (gen_addsi3 (out, out, const1_rtx));
+
+ emit_label (end_0_label);
+}
+\f
+#define MAX_386_STACK_LOCALS 2
+
+static rtx ix86_stack_locals[(int) MAX_MACHINE_MODE][MAX_386_STACK_LOCALS];
+
+/* Define the structure for the machine field in struct function. */
+struct machine_function
+{
+ rtx ix86_stack_locals[(int) MAX_MACHINE_MODE][MAX_386_STACK_LOCALS];
+};
+
+/* Functions to save and restore ix86_stack_locals.
+ These will be called, via pointer variables,
+ from push_function_context and pop_function_context. */
+
+void
+save_386_machine_status (p)
+ struct function *p;
+{
+ p->machine
+ = (struct machine_function *) xmalloc (sizeof (struct machine_function));
+ bcopy ((char *) ix86_stack_locals, (char *) p->machine->ix86_stack_locals,
+ sizeof ix86_stack_locals);
+}
+
+void
+restore_386_machine_status (p)
+ struct function *p;
+{
+ bcopy ((char *) p->machine->ix86_stack_locals, (char *) ix86_stack_locals,
+ sizeof ix86_stack_locals);
+ free (p->machine);
+ p->machine = NULL;
+}
+
+/* Clear stack slot assignments remembered from previous functions.
+ This is called from INIT_EXPANDERS once before RTL is emitted for each
+ function. */
+
+void
+clear_386_stack_locals ()
+{
+ enum machine_mode mode;
+ int n;
+
+ for (mode = VOIDmode; (int) mode < (int) MAX_MACHINE_MODE;
+ mode = (enum machine_mode) ((int) mode + 1))
+ for (n = 0; n < MAX_386_STACK_LOCALS; n++)
+ ix86_stack_locals[(int) mode][n] = NULL_RTX;
+
+ /* Arrange to save and restore ix86_stack_locals around nested functions. */
+ save_machine_status = save_386_machine_status;
+ restore_machine_status = restore_386_machine_status;
+}
+
+/* Return a MEM corresponding to a stack slot with mode MODE.
+ Allocate a new slot if necessary.
+
+ The RTL for a function can have several slots available: N is
+ which slot to use. */
+
+rtx
+assign_386_stack_local (mode, n)
+ enum machine_mode mode;
+ int n;
+{
+ if (n < 0 || n >= MAX_386_STACK_LOCALS)
+ abort ();
+
+ if (ix86_stack_locals[(int) mode][n] == NULL_RTX)
+ ix86_stack_locals[(int) mode][n]
+ = assign_stack_local (mode, GET_MODE_SIZE (mode), 0);
+
+ return ix86_stack_locals[(int) mode][n];
+}
+\f
+/* Calculate the length of the memory address in the instruction
+ encoding. Does not include the one-byte modrm, opcode, or prefix. */
+
+static int
+memory_address_length (addr)
+ rtx addr;
+{
+ struct ix86_address parts;
+ rtx base, index, disp;
+ int len;
+
+ if (GET_CODE (addr) == PRE_DEC
+ || GET_CODE (addr) == POST_INC)
+ return 0;
- /* Check first byte. */
- output_asm_insn (AS2 (test%B1,%b1,%b1), xops);
- output_asm_insn (AS1 (je,%l12), xops);
+ if (! ix86_decompose_address (addr, &parts))
+ abort ();
- /* Check second byte. */
- output_asm_insn (AS2 (test%B1,%h1,%h1), xops);
- output_asm_insn (AS1 (je,%l11), xops);
+ base = parts.base;
+ index = parts.index;
+ disp = parts.disp;
+ len = 0;
- if (TARGET_PENTIUM)
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
- CODE_LABEL_NUMBER (xops[9]));
+ /* Register Indirect. */
+ if (base && !index && !disp)
+ {
+ /* Special cases: ebp and esp need the two-byte modrm form. */
+ if (addr == stack_pointer_rtx
+ || addr == arg_pointer_rtx
+ || addr == frame_pointer_rtx)
+ len = 1;
}
+ /* Direct Addressing. */
+ else if (disp && !base && !index)
+ len = 4;
+
else
{
- /* Check first byte. */
- output_asm_insn (AS2 (test%L1,%14,%1), xops);
- output_asm_insn (AS1 (je,%l12), xops);
+ /* Find the length of the displacement constant. */
+ if (disp)
+ {
+ if (GET_CODE (disp) == CONST_INT
+ && CONST_OK_FOR_LETTER_P (INTVAL (disp), 'K'))
+ len = 1;
+ else
+ len = 4;
+ }
- /* Check second byte. */
- output_asm_insn (AS2 (test%L1,%15,%1), xops);
- output_asm_insn (AS1 (je,%l11), xops);
+ /* An index requires the two-byte modrm form. */
+ if (index)
+ len += 1;
}
- /* Check third byte. */
- output_asm_insn (AS2 (test%L1,%16,%1), xops);
- output_asm_insn (AS1 (je,%l10), xops);
+ return len;
+}
- /* Check fourth byte and increment address. */
- output_asm_insn (AS2 (add%L0,%5,%0), xops);
- output_asm_insn (AS2 (test%L1,%17,%1), xops);
- output_asm_insn (AS1 (jne,%l8), xops);
+int
+ix86_attr_length_default (insn)
+ rtx insn;
+{
+ enum attr_type type;
+ int len = 0, i;
+
+ type = get_attr_type (insn);
+ extract_insn (insn);
+ switch (type)
+ {
+ case TYPE_INCDEC:
+ case TYPE_SETCC:
+ case TYPE_ICMOV:
+ case TYPE_FMOV:
+ case TYPE_FOP:
+ case TYPE_FCMP:
+ case TYPE_FOP1:
+ case TYPE_FMUL:
+ case TYPE_FDIV:
+ case TYPE_FSGN:
+ case TYPE_FPSPC:
+ case TYPE_FCMOV:
+ case TYPE_IBR:
+ break;
- /* Now generate fixups when the compare stops within a 4-byte word. */
- output_asm_insn (AS2 (sub%L0,%4,%0), xops);
+ case TYPE_ALU1:
+ case TYPE_NEGNOT:
+ case TYPE_ALU:
+ case TYPE_ICMP:
+ case TYPE_IMOVX:
+ case TYPE_ISHIFT:
+ case TYPE_IMUL:
+ case TYPE_IDIV:
+ case TYPE_PUSH:
+ case TYPE_POP:
+ for (i = recog_n_operands - 1; i >= 0; --i)
+ if (CONSTANT_P (recog_operand[i]))
+ {
+ if (GET_CODE (recog_operand[i]) == CONST_INT
+ && CONST_OK_FOR_LETTER_P (INTVAL (recog_operand[i]), 'K'))
+ len += 1;
+ else
+ len += GET_MODE_SIZE (GET_MODE (recog_operand[0]));
+ }
+ break;
+
+ case TYPE_IMOV:
+ if (CONSTANT_P (recog_operand[1]))
+ len += GET_MODE_SIZE (GET_MODE (recog_operand[0]));
+ break;
+
+ case TYPE_CALL:
+ if (constant_call_address_operand (recog_operand[0]))
+ return 5;
+ break;
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (xops[10]));
- output_asm_insn (AS1 (inc%L0,%0), xops);
+ case TYPE_CALLV:
+ if (constant_call_address_operand (recog_operand[1]))
+ return 5;
+ break;
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (xops[11]));
- output_asm_insn (AS1 (inc%L0,%0), xops);
+ case TYPE_LEA:
+ len += memory_address_length (SET_SRC (single_set (insn)));
+ goto just_opcode;
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (xops[12]));
+ case TYPE_OTHER:
+ case TYPE_MULTI:
+ return 15;
- return "";
+ default:
+ abort ();
+ }
+
+ for (i = recog_n_operands - 1; i >= 0; --i)
+ if (GET_CODE (recog_operand[i]) == MEM)
+ {
+ len += memory_address_length (XEXP (recog_operand[i], 0));
+ break;
+ }
+
+just_opcode:
+ len += get_attr_length_opcode (insn);
+ len += get_attr_length_prefix (insn);
+
+ return len;
}
+\f
+/* Return the maximum number of instructions a cpu can issue. */
-char *
-output_fp_conditional_move (which_alternative, operands)
- int which_alternative;
- rtx operands[];
+int
+ix86_issue_rate ()
{
- enum rtx_code code = GET_CODE (operands[1]);
-
- /* This should never happen. */
- if (!(cc_prev_status.flags & CC_IN_80387)
- && (code == GT || code == LE || code == GE || code == LT))
- abort ();
-
- switch (which_alternative)
+ switch (ix86_cpu)
{
- case 0:
- /* r <- cond ? arg : r */
- output_asm_insn (AS2 (fcmov%F1,%2,%0), operands);
- break;
+ case PROCESSOR_PENTIUM:
+ case PROCESSOR_K6:
+ return 2;
- case 1:
- /* r <- cond ? r : arg */
- output_asm_insn (AS2 (fcmov%f1,%3,%0), operands);
- break;
+ case PROCESSOR_PENTIUMPRO:
+ return 3;
default:
- abort ();
+ return 1;
}
-
- return "";
}
-char *
-output_int_conditional_move (which_alternative, operands)
- int which_alternative;
- rtx operands[];
-{
- enum rtx_code code = GET_CODE (operands[1]);
+/* A subroutine of ix86_adjust_cost -- return true iff INSN reads flags set
+ by DEP_INSN and nothing set by DEP_INSN. */
- /* This is very tricky. We have to do it right. For a code segement
- like:
+static int
+ix86_flags_dependant (insn, dep_insn, insn_type)
+ rtx insn, dep_insn;
+ enum attr_type insn_type;
+{
+ rtx set, set2;
- int foo, bar;
- ....
- foo = foo - x;
- if (foo >= 0)
- bar = y;
+ /* Simplify the test for uninteresting insns. */
+ if (insn_type != TYPE_SETCC
+ && insn_type != TYPE_ICMOV
+ && insn_type != TYPE_FCMOV
+ && insn_type != TYPE_IBR)
+ return 0;
- final_scan_insn () may delete the insn which sets CC. We have to
- tell final_scan_insn () if it should be reinserted. When CODE is
- GT or LE, we have to check the CC_NO_OVERFLOW bit and return
- NULL_PTR to tell final to reinsert the test insn because the
- conditional move cannot be handled properly without it. */
- if ((code == GT || code == LE)
- && (cc_prev_status.flags & CC_NO_OVERFLOW))
- return NULL_PTR;
+ if ((set = single_set (dep_insn)) != 0)
+ {
+ set = SET_DEST (set);
+ set2 = NULL_RTX;
+ }
+ else if (GET_CODE (PATTERN (dep_insn)) == PARALLEL
+ && XVECLEN (PATTERN (dep_insn), 0) == 2
+ && GET_CODE (XVECEXP (PATTERN (dep_insn), 0, 0)) == SET
+ && GET_CODE (XVECEXP (PATTERN (dep_insn), 0, 1)) == SET)
+ {
+ set = SET_DEST (XVECEXP (PATTERN (dep_insn), 0, 0));
+ set2 = SET_DEST (XVECEXP (PATTERN (dep_insn), 0, 0));
+ }
- switch (which_alternative)
+ if (set && GET_CODE (set) == REG && REGNO (set) == FLAGS_REG)
{
- case 0:
- /* r <- cond ? arg : r */
- output_asm_insn (AS2 (cmov%C1,%2,%0), operands);
- break;
+ /* This test is true if the dependant insn reads the flags but
+ not any other potentially set register. */
+ if (reg_overlap_mentioned_p (set, PATTERN (insn))
+ && (!set2 || !reg_overlap_mentioned_p (set2, PATTERN (insn))))
+ return 1;
+ }
- case 1:
- /* r <- cond ? r : arg */
- output_asm_insn (AS2 (cmov%c1,%3,%0), operands);
- break;
+ return 0;
+}
- default:
- abort ();
+/* A subroutine of ix86_adjust_cost -- return true iff INSN has a memory
+ address with operands set by DEP_INSN. */
+
+static int
+ix86_agi_dependant (insn, dep_insn, insn_type)
+ rtx insn, dep_insn;
+ enum attr_type insn_type;
+{
+ rtx addr;
+
+ if (insn_type == TYPE_LEA)
+ addr = SET_SRC (single_set (insn));
+ else
+ {
+ int i;
+ extract_insn (insn);
+ for (i = recog_n_operands - 1; i >= 0; --i)
+ if (GET_CODE (recog_operand[i]) == MEM)
+ {
+ addr = XEXP (recog_operand[i], 0);
+ goto found;
+ }
+ return 0;
+ found:;
}
- return "";
+ return modified_in_p (addr, dep_insn);
}
int
-x86_adjust_cost (insn, link, dep_insn, cost)
+ix86_adjust_cost (insn, link, dep_insn, cost)
rtx insn, link, dep_insn;
int cost;
{
- rtx next_inst;
+ enum attr_type insn_type, dep_insn_type;
+ rtx set, set2;
- if (GET_CODE (dep_insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
- return 0;
+ /* We describe no anti or output depenancies. */
+ if (REG_NOTE_KIND (link) != 0)
+ return cost;
- if (GET_CODE (dep_insn) == INSN
- && GET_CODE (PATTERN (dep_insn)) == SET
- && GET_CODE (SET_DEST (PATTERN (dep_insn))) == REG
- && GET_CODE (insn) == INSN
- && GET_CODE (PATTERN (insn)) == SET
- && !reg_overlap_mentioned_p (SET_DEST (PATTERN (dep_insn)),
- SET_SRC (PATTERN (insn))))
- return 0; /* ??? */
+ /* If we can't recognize the insns, we can't really do anything. */
+ if (recog_memoized (insn) < 0 || recog_memoized (dep_insn) < 0)
+ return cost;
+ insn_type = get_attr_type (insn);
+ dep_insn_type = get_attr_type (dep_insn);
switch (ix86_cpu)
{
case PROCESSOR_PENTIUM:
- if (cost != 0 && is_fp_insn (insn) && is_fp_insn (dep_insn)
- && !is_fp_dest (dep_insn))
- return 0;
+ /* Address Generation Interlock adds a cycle of latency. */
+ if (ix86_agi_dependant (insn, dep_insn, insn_type))
+ cost += 1;
+
+ /* ??? Compares pair with jump/setcc. */
+ if (ix86_flags_dependant (insn, dep_insn, insn_type))
+ cost = 0;
+
+ /* Floating point stores require value to be ready one cycle ealier. */
+ if (insn_type == TYPE_FMOV
+ && get_attr_memory (insn) == MEMORY_STORE
+ && !ix86_agi_dependant (insn, dep_insn, insn_type))
+ cost += 1;
+ break;
- if (agi_dependent (insn, dep_insn))
- return cost ? cost + 1 : 2;
+ case PROCESSOR_PENTIUMPRO:
+ /* Since we can't represent delayed latencies of load+operation,
+ increase the cost here for non-imov insns. */
+ if (dep_insn_type != TYPE_IMOV
+ && dep_insn_type != TYPE_FMOV
+ && get_attr_memory (dep_insn) == MEMORY_LOAD)
+ cost += 1;
+
+ /* INT->FP conversion is expensive. */
+ if (get_attr_fp_int_src (dep_insn))
+ cost += 5;
+
+ /* There is one cycle extra latency between an FP op and a store. */
+ if (insn_type == TYPE_FMOV
+ && (set = single_set (dep_insn)) != NULL_RTX
+ && (set2 = single_set (insn)) != NULL_RTX
+ && rtx_equal_p (SET_DEST (set), SET_SRC (set2))
+ && GET_CODE (SET_DEST (set2)) == MEM)
+ cost += 1;
+ break;
- if (GET_CODE (insn) == INSN
- && GET_CODE (PATTERN (insn)) == SET
- && SET_DEST (PATTERN (insn)) == cc0_rtx
- && (next_inst = next_nonnote_insn (insn))
- && GET_CODE (next_inst) == JUMP_INSN)
- /* compare probably paired with jump */
- return 0;
+ case PROCESSOR_K6:
+ /* The esp dependency is resolved before the instruction is really
+ finished. */
+ if ((insn_type == TYPE_PUSH || insn_type == TYPE_POP)
+ && (dep_insn_type == TYPE_PUSH || dep_insn_type == TYPE_POP))
+ return 1;
- /* Stores stalls one cycle longer than other insns. */
- if (is_fp_insn (insn) && cost && is_fp_store (dep_insn))
- cost++;
+ /* Since we can't represent delayed latencies of load+operation,
+ increase the cost here for non-imov insns. */
+ if (get_attr_memory (dep_insn) == MEMORY_LOAD)
+ cost += (dep_insn_type != TYPE_IMOV) ? 2 : 1;
+
+ /* INT->FP conversion is expensive. */
+ if (get_attr_fp_int_src (dep_insn))
+ cost += 5;
break;
- case PROCESSOR_K6:
+
default:
- if (!is_fp_dest (dep_insn))
- {
- if(!agi_dependent (insn, dep_insn))
- return 0;
- if (TARGET_486)
- return 2;
- }
- else
- if (is_fp_store (insn) && is_fp_insn (dep_insn)
- && NEXT_INSN (insn) && NEXT_INSN (NEXT_INSN (insn))
- && NEXT_INSN (NEXT_INSN (NEXT_INSN (insn)))
- && (GET_CODE (NEXT_INSN (insn)) == INSN)
- && (GET_CODE (NEXT_INSN (NEXT_INSN (insn))) == JUMP_INSN)
- && (GET_CODE (NEXT_INSN (NEXT_INSN (NEXT_INSN (insn)))) == NOTE)
- && (NOTE_LINE_NUMBER (NEXT_INSN (NEXT_INSN (NEXT_INSN (insn))))
- == NOTE_INSN_LOOP_END))
- return 3;
break;
}
return cost;
}
-/* Output assembly code for a left shift.
+static union
+{
+ struct ppro_sched_data
+ {
+ rtx decode[3];
+ int issued_this_cycle;
+ } ppro;
+} ix86_sched_data;
- Always use "sal" when shifting a memory operand or for a non constant
- shift count.
+static int
+ix86_safe_length (insn)
+ rtx insn;
+{
+ if (recog_memoized (insn) >= 0)
+ return get_attr_length(insn);
+ else
+ return 128;
+}
- When optimizing for size, we know that src == dest, and we should always
- use "sal". If src != dest, then copy src to dest and use "sal".
-
- Pentium and PPro (speed):
+static int
+ix86_safe_length_prefix (insn)
+ rtx insn;
+{
+ if (recog_memoized (insn) >= 0)
+ return get_attr_length(insn);
+ else
+ return 0;
+}
+
+static enum attr_memory
+ix86_safe_memory (insn)
+ rtx insn;
+{
+ if (recog_memoized (insn) >= 0)
+ return get_attr_memory(insn);
+ else
+ return MEMORY_UNKNOWN;
+}
- When src == dest, use "add" for a shift counts of one, else use
- "sal". If we modeled Pentium AGI stalls and U/V pipelining better we
- would want to generate lea for some shifts on the Pentium.
+static enum attr_pent_pair
+ix86_safe_pent_pair (insn)
+ rtx insn;
+{
+ if (recog_memoized (insn) >= 0)
+ return get_attr_pent_pair(insn);
+ else
+ return PENT_PAIR_NP;
+}
- When src != dest, use "lea" for small shift counts. Otherwise,
- copy src to dest and use the normal shifting code. Exception for
- TARGET_DOUBLE_WITH_ADD. */
+static enum attr_ppro_uops
+ix86_safe_ppro_uops (insn)
+ rtx insn;
+{
+ if (recog_memoized (insn) >= 0)
+ return get_attr_ppro_uops (insn);
+ else
+ return PPRO_UOPS_MANY;
+}
-char *
-output_ashl (insn, operands)
- rtx insn, *operands;
+static void
+ix86_dump_ppro_packet (dump)
+ FILE *dump;
{
- /* Handle case where srcreg != dstreg. */
- if (REG_P (operands[0]) && REGNO (operands[0]) != REGNO (operands[1]))
+ if (ix86_sched_data.ppro.decode[0])
{
- if (TARGET_DOUBLE_WITH_ADD && INTVAL (operands[2]) == 1)
- switch (GET_MODE (operands[0]))
- {
- case SImode:
- output_asm_insn (AS2 (mov%L0,%1,%0), operands);
- return AS2 (add%L0,%1,%0);
- case HImode:
- output_asm_insn (AS2 (mov%L0,%k1,%k0), operands);
- if (i386_cc_probably_useless_p (insn))
- {
- CC_STATUS_INIT;
- return AS2 (add%L0,%k1,%k0);
- }
- return AS2 (add%W0,%k1,%k0);
- case QImode:
- output_asm_insn (AS2 (mov%B0,%1,%0), operands);
- return AS2 (add%B0,%1,%0);
- default:
- abort ();
- }
- else
- {
- CC_STATUS_INIT;
+ fprintf (dump, "PPRO packet: %d",
+ INSN_UID (ix86_sched_data.ppro.decode[0]));
+ if (ix86_sched_data.ppro.decode[1])
+ fprintf (dump, " %d", INSN_UID (ix86_sched_data.ppro.decode[1]));
+ if (ix86_sched_data.ppro.decode[2])
+ fprintf (dump, " %d", INSN_UID (ix86_sched_data.ppro.decode[2]));
+ fputc ('\n', dump);
+ }
+}
- /* This should be extremely rare (impossible?). We can not encode a
- shift of the stack pointer using an lea instruction. So copy the
- stack pointer into the destination register and use an lea. */
- if (operands[1] == stack_pointer_rtx)
- {
- output_asm_insn (AS2 (mov%L0,%k1,%k0), operands);
- operands[1] = operands[0];
- }
+/* We're beginning a new block. Initialize data structures as necessary. */
- /* For shifts up to and including 3 bits, use lea. */
- operands[1] = gen_rtx_MULT (SImode,
- gen_rtx_REG (SImode, REGNO (operands[1])),
- GEN_INT (1 << INTVAL (operands[2])));
- return AS2 (lea%L0,%a1,%k0);
- }
+void
+ix86_sched_init (dump, sched_verbose)
+ FILE *dump ATTRIBUTE_UNUSED;
+ int sched_verbose ATTRIBUTE_UNUSED;
+{
+ memset (&ix86_sched_data, 0, sizeof (ix86_sched_data));
+}
+
+/* Shift INSN to SLOT, and shift everything else down. */
+
+static void
+ix86_reorder_insn (insnp, slot)
+ rtx *insnp, *slot;
+{
+ if (insnp != slot)
+ {
+ rtx insn = *insnp;
+ do
+ insnp[0] = insnp[1];
+ while (++insnp != slot);
+ *insnp = insn;
}
+}
+
+/* Find an instruction with given pairability and minimal amount of cycles
+ lost by the fact that the CPU waits for both pipelines to finish before
+ reading next instructions. Also take care that both instructions together
+ can not exceed 7 bytes. */
+
+static rtx *
+ix86_pent_find_pair (e_ready, ready, type, first)
+ rtx *e_ready;
+ rtx *ready;
+ enum attr_pent_pair type;
+ rtx first;
+{
+ int mincycles, cycles;
+ enum attr_pent_pair tmp;
+ enum attr_memory memory;
+ rtx *insnp, *bestinsnp = NULL;
- /* Source and destination match. */
+ if (ix86_safe_length (first) > 7 + ix86_safe_length_prefix (first))
+ return NULL;
- /* Handle variable shift. */
- if (REG_P (operands[2]))
- switch (GET_MODE (operands[0]))
+ memory = ix86_safe_memory (first);
+ cycles = result_ready_cost (first);
+ mincycles = INT_MAX;
+
+ for (insnp = e_ready; insnp >= ready && mincycles; --insnp)
+ if ((tmp = ix86_safe_pent_pair (*insnp)) == type
+ && ix86_safe_length (*insnp) <= 7 + ix86_safe_length_prefix (*insnp))
{
- case SImode:
- return AS2 (sal%L0,%b2,%0);
- case HImode:
- if (REG_P (operands[0]) && i386_cc_probably_useless_p (insn))
+ enum attr_memory second_memory;
+ int secondcycles, currentcycles;
+
+ second_memory = ix86_safe_memory (*insnp);
+ secondcycles = result_ready_cost (*insnp);
+ currentcycles = abs (cycles - secondcycles);
+
+ if (secondcycles >= 1 && cycles >= 1)
{
- CC_STATUS_INIT;
- return AS2 (sal%L0,%b2,%k0);
+ /* Two read/modify/write instructions together takes two
+ cycles longer. */
+ if (memory == MEMORY_BOTH && second_memory == MEMORY_BOTH)
+ currentcycles += 2;
+
+ /* Read modify/write instruction followed by read/modify
+ takes one cycle longer. */
+ if (memory == MEMORY_BOTH && second_memory == MEMORY_LOAD
+ && tmp != PENT_PAIR_UV
+ && ix86_safe_pent_pair (first) != PENT_PAIR_UV)
+ currentcycles += 1;
}
- else
- return AS2 (sal%W0,%b2,%0);
- case QImode:
- return AS2 (sal%B0,%b2,%0);
- default:
- abort ();
+ if (currentcycles < mincycles)
+ bestinsnp = insnp, mincycles = currentcycles;
}
- /* Always perform shift by 1 using an add instruction. */
- if (REG_P (operands[0]) && operands[2] == const1_rtx)
- switch (GET_MODE (operands[0]))
+ return bestinsnp;
+}
+
+/* We are about to being issuing insns for this clock cycle.
+ Override the default sort algorithm to better slot instructions. */
+
+int
+ix86_sched_reorder (dump, sched_verbose, ready, n_ready, clock_var)
+ FILE *dump ATTRIBUTE_UNUSED;
+ int sched_verbose ATTRIBUTE_UNUSED;
+ rtx *ready;
+ int n_ready, clock_var;
+{
+ rtx *e_ready = ready + n_ready - 1;
+ rtx *insnp;
+ int i;
+
+ if (n_ready < 2)
+ goto out;
+
+ switch (ix86_cpu)
+ {
+ default:
+ goto out;
+
+ case PROCESSOR_PENTIUM:
+ /* This wouldn't be necessary if Haifa knew that static insn ordering
+ is important to which pipe an insn is issued to. So we have to make
+ some minor rearrangements. */
{
- case SImode:
- return AS2 (add%L0,%0,%0);
- case HImode:
- if (REG_P (operands[0]) && i386_cc_probably_useless_p (insn))
+ enum attr_pent_pair pair1, pair2;
+
+ pair1 = ix86_safe_pent_pair (*e_ready);
+
+ /* If the first insn is non-pairable, let it be. */
+ if (pair1 == PENT_PAIR_NP)
+ goto out;
+ pair2 = PENT_PAIR_NP;
+
+ /* If the first insn is UV or PV pairable, search for a PU
+ insn to go with. */
+ if (pair1 == PENT_PAIR_UV || pair1 == PENT_PAIR_PV)
+ {
+ insnp = ix86_pent_find_pair (e_ready-1, ready,
+ PENT_PAIR_PU, *e_ready);
+ if (insnp)
+ pair2 = PENT_PAIR_PU;
+ }
+
+ /* If the first insn is PU or UV pairable, search for a PV
+ insn to go with. */
+ if (pair2 == PENT_PAIR_NP
+ && (pair1 == PENT_PAIR_PU || pair1 == PENT_PAIR_UV))
+ {
+ insnp = ix86_pent_find_pair (e_ready-1, ready,
+ PENT_PAIR_PV, *e_ready);
+ if (insnp)
+ pair2 = PENT_PAIR_PV;
+ }
+
+ /* If the first insn is pairable, search for a UV
+ insn to go with. */
+ if (pair2 == PENT_PAIR_NP)
{
- CC_STATUS_INIT;
- return AS2 (add%L0,%k0,%k0);
+ insnp = ix86_pent_find_pair (e_ready-1, ready,
+ PENT_PAIR_UV, *e_ready);
+ if (insnp)
+ pair2 = PENT_PAIR_UV;
}
+
+ if (pair2 == PENT_PAIR_NP)
+ goto out;
+
+ /* Found something! Decide if we need to swap the order. */
+ if (pair1 == PENT_PAIR_PV || pair2 == PENT_PAIR_PU
+ || (pair1 == PENT_PAIR_UV && pair2 == PENT_PAIR_UV
+ && ix86_safe_memory (*e_ready) == MEMORY_BOTH
+ && ix86_safe_memory (*insnp) == MEMORY_LOAD))
+ ix86_reorder_insn (insnp, e_ready);
else
- return AS2 (add%W0,%0,%0);
- case QImode:
- return AS2 (add%B0,%0,%0);
- default:
- abort ();
+ ix86_reorder_insn (insnp, e_ready - 1);
}
+ break;
-#if 0
- /* ??? Currently disabled. Because our model of Pentium is far from being
- exact, this change will need some benchmarking. */
- /* Shift reg by 2 or 3 use an lea instruction for Pentium if this is
- insn is expected to issue into the V pipe (the insn's mode will be
- TImode for a U pipe, and !TImode for a V pipe instruction). */
- if (! optimize_size
- && REG_P (operands[0])
- && GET_CODE (operands[2]) == CONST_INT
- && INTVAL (operands[2]) <= 3
- && (int)ix86_cpu == (int)PROCESSOR_PENTIUM
- && GET_MODE (insn) != TImode)
- {
- CC_STATUS_INIT;
- operands[1] = gen_rtx_MULT (SImode, gen_rtx_REG (SImode, REGNO (operands[1])),
- GEN_INT (1 << INTVAL (operands[2])));
- return AS2 (lea%L0,%a1,%0);
- }
-#endif
+ case PROCESSOR_PENTIUMPRO:
+ {
+ rtx decode[3];
+ enum attr_ppro_uops cur_uops;
+ int issued_this_cycle;
- /* Otherwise use a shift instruction. */
- switch (GET_MODE (operands[0]))
- {
- case SImode:
- return AS2 (sal%L0,%2,%0);
- case HImode:
- if (REG_P (operands[0]) && i386_cc_probably_useless_p (insn))
- {
- CC_STATUS_INIT;
- return AS2 (sal%L0,%2,%k0);
- }
- else
- return AS2 (sal%W0,%2,%0);
- case QImode:
- return AS2 (sal%B0,%2,%0);
- default:
- abort ();
- }
-}
+ /* At this point .ppro.decode contains the state of the three
+ decoders from last "cycle". That is, those insns that were
+ actually independant. But here we're scheduling for the
+ decoder, and we may find things that are decodable in the
+ same cycle. */
-/* Given the memory address ADDR, calculate the length of the address or
- the length of just the displacement (controlled by DISP_LENGTH).
-
- The length returned does not include the one-byte modrm, opcode,
- or prefix. */
+ memcpy (decode, ix86_sched_data.ppro.decode, sizeof(decode));
+ issued_this_cycle = 0;
-int
-memory_address_info (addr, disp_length)
- rtx addr;
- int disp_length;
-{
- rtx base, index, disp, scale;
- rtx op0, op1;
- int len;
+ insnp = e_ready;
+ cur_uops = ix86_safe_ppro_uops (*insnp);
- if (GET_CODE (addr) == PRE_DEC
- || GET_CODE (addr) == POST_INC)
- return 0;
+ /* If the decoders are empty, and we've a complex insn at the
+ head of the priority queue, let it issue without complaint. */
+ if (decode[0] == NULL)
+ {
+ if (cur_uops == PPRO_UOPS_MANY)
+ {
+ decode[0] = *insnp;
+ goto ppro_done;
+ }
- /* Register Indirect. */
- if (register_operand (addr, Pmode))
- {
- /* Special cases: ebp and esp need the two-byte modrm form.
+ /* Otherwise, search for a 2-4 uop unsn to issue. */
+ while (cur_uops != PPRO_UOPS_FEW)
+ {
+ if (insnp == ready)
+ break;
+ cur_uops = ix86_safe_ppro_uops (*--insnp);
+ }
- We change [ESI] to [ESI+0] on the K6 when not optimizing
- for size. */
- if (addr == stack_pointer_rtx
- || addr == arg_pointer_rtx
- || addr == frame_pointer_rtx
- || (REGNO_REG_CLASS (REGNO (addr)) == SIREG
- && ix86_cpu == PROCESSOR_K6 && !optimize_size))
- return 1;
- else
- return 0;
- }
+ /* If so, move it to the head of the line. */
+ if (cur_uops == PPRO_UOPS_FEW)
+ ix86_reorder_insn (insnp, e_ready);
- /* Direct Addressing. */
- if (CONSTANT_P (addr))
- return 4;
+ /* Issue the head of the queue. */
+ issued_this_cycle = 1;
+ decode[0] = *e_ready--;
+ }
- index = base = disp = scale = NULL_RTX;
- op0 = XEXP (addr, 0);
- op1 = XEXP (addr, 1);
+ /* Look for simple insns to fill in the other two slots. */
+ for (i = 1; i < 3; ++i)
+ if (decode[i] == NULL)
+ {
+ if (ready >= e_ready)
+ goto ppro_done;
- if (GET_CODE (addr) == PLUS)
- {
- if (register_operand (op0, Pmode))
- {
- if (register_operand (op1, Pmode))
- index = op0, base = op1;
- else
- base = op0, disp = op1;
- }
- else if (GET_CODE (op0) == MULT)
- {
- index = XEXP (op0, 0);
- scale = XEXP (op0, 1);
- if (register_operand (op1, Pmode))
- base = op1;
- else
- disp = op1;
- }
- else if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 0)) == MULT)
- {
- index = XEXP (XEXP (op0, 0), 0);
- scale = XEXP (XEXP (op0, 0), 1);
- base = XEXP (op0, 1);
- disp = op1;
- }
- else if (GET_CODE (op0) == PLUS)
- {
- index = XEXP (op0, 0);
- base = XEXP (op0, 1);
- disp = op1;
- }
- else
- abort ();
- }
- else if (GET_CODE (addr) == MULT
- /* We're called for lea too, which implements ashift on occasion. */
- || GET_CODE (addr) == ASHIFT)
- {
- index = XEXP (addr, 0);
- scale = XEXP (addr, 1);
- }
- else
- abort ();
-
- /* Allow arg pointer and stack pointer as index if there is not scaling */
- if (base && index && !scale
- && (index == stack_pointer_rtx
- || index == arg_pointer_rtx
- || index == frame_pointer_rtx))
- {
- rtx tmp = base;
- base = index;
- index = tmp;
+ insnp = e_ready;
+ cur_uops = ix86_safe_ppro_uops (*insnp);
+ while (cur_uops != PPRO_UOPS_ONE)
+ {
+ if (insnp == ready)
+ break;
+ cur_uops = ix86_safe_ppro_uops (*--insnp);
+ }
+
+ /* Found one. Move it to the head of the queue and issue it. */
+ if (cur_uops == PPRO_UOPS_ONE)
+ {
+ ix86_reorder_insn (insnp, e_ready);
+ decode[i] = *e_ready--;
+ issued_this_cycle++;
+ continue;
+ }
+
+ /* ??? Didn't find one. Ideally, here we would do a lazy split
+ of 2-uop insns, issue one and queue the other. */
+ }
+
+ ppro_done:
+ if (issued_this_cycle == 0)
+ issued_this_cycle = 1;
+ ix86_sched_data.ppro.issued_this_cycle = issued_this_cycle;
+ }
+ break;
}
- /* Special case: ebp cannot be encoded as a base without a displacement. */
- if (base == frame_pointer_rtx && !disp)
- disp = const0_rtx;
+out:
+ return ix86_issue_rate ();
+}
- /* Scaling can not be encoded without base or displacement.
- Except for scale == 1 where we can encode reg + reg instead of reg * 2. */
- if (!base && index
- && (!scale || GET_CODE (scale) != CONST_INT || (INTVAL (scale) != 1)))
- disp = const0_rtx;
+/* We are about to issue INSN. Return the number of insns left on the
+ ready queue that can be issued this cycle. */
- /* Find the length of the displacement constant. */
- len = 0;
- if (disp)
+int
+ix86_variable_issue (dump, sched_verbose, insn, can_issue_more)
+ FILE *dump;
+ int sched_verbose;
+ rtx insn;
+ int can_issue_more;
+{
+ int i;
+ switch (ix86_cpu)
{
- if (GET_CODE (disp) == CONST_INT
- && CONST_OK_FOR_LETTER_P (INTVAL (disp), 'K'))
- len = 1;
- else
- len = 4;
- }
+ default:
+ return can_issue_more - 1;
- /* An index requires the two-byte modrm form. Not important
- if we are computing just length of the displacement. */
- if (index && ! disp_length)
- len += 1;
+ case PROCESSOR_PENTIUMPRO:
+ {
+ enum attr_ppro_uops uops = ix86_safe_ppro_uops (insn);
- return len;
+ if (uops == PPRO_UOPS_MANY)
+ {
+ if (sched_verbose)
+ ix86_dump_ppro_packet (dump);
+ ix86_sched_data.ppro.decode[0] = insn;
+ ix86_sched_data.ppro.decode[1] = NULL;
+ ix86_sched_data.ppro.decode[2] = NULL;
+ if (sched_verbose)
+ ix86_dump_ppro_packet (dump);
+ ix86_sched_data.ppro.decode[0] = NULL;
+ }
+ else if (uops == PPRO_UOPS_FEW)
+ {
+ if (sched_verbose)
+ ix86_dump_ppro_packet (dump);
+ ix86_sched_data.ppro.decode[0] = insn;
+ ix86_sched_data.ppro.decode[1] = NULL;
+ ix86_sched_data.ppro.decode[2] = NULL;
+ }
+ else
+ {
+ for (i = 0; i < 3; ++i)
+ if (ix86_sched_data.ppro.decode[i] == NULL)
+ {
+ ix86_sched_data.ppro.decode[i] = insn;
+ break;
+ }
+ if (i == 3)
+ abort ();
+ if (i == 2)
+ {
+ if (sched_verbose)
+ ix86_dump_ppro_packet (dump);
+ ix86_sched_data.ppro.decode[0] = NULL;
+ ix86_sched_data.ppro.decode[1] = NULL;
+ ix86_sched_data.ppro.decode[2] = NULL;
+ }
+ }
+ }
+ return --ix86_sched_data.ppro.issued_this_cycle;
+ }
}
-; GCC machine description for Intel X86.
-;; Copyright (C) 1988, 94, 95, 96, 97, 98, 1999 Free Software Foundation, Inc.
+;; GCC machine description for IA-32.
+;; Copyright (C) 1988, 94-98, 1999 Free Software Foundation, Inc.
;; Mostly by William Schelter.
-
+;;
;; This file is part of GNU CC.
-
+;;
;; GNU CC is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
-
+;;
;; GNU CC is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
-
+;;
;; You should have received a copy of the GNU General Public License
;; along with GNU CC; see the file COPYING. If not, write to
;; the Free Software Foundation, 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA. */
-
+;;
;; The original PO technology requires these to be ordered by speed,
;; so that assigner will pick the fastest.
-
+;;
;; See file "rtl.def" for documentation on define_insn, match_*, et. al.
-
+;;
;; Macro #define NOTICE_UPDATE_CC in file i386.h handles condition code
;; updates for most instructions.
-
+;;
;; Macro REG_CLASS_FROM_LETTER in file i386.h defines the register
;; constraint letters.
-
-;; the special asm out single letter directives following a '%' are:
+;;
+;; The special asm out single letter directives following a '%' are:
;; 'z' mov%z1 would be movl, movw, or movb depending on the mode of
;; operands[1].
;; 'L' Print the opcode suffix for a 32-bit integer opcode.
;; 'S' Print the opcode suffix for a 32-bit float opcode.
;; 'T' Print the opcode suffix for an 80-bit extended real XFmode float opcode.
;; 'J' Print the appropriate jump operand.
-
+;;
;; 'b' Print the QImode name of the register for the indicated operand.
;; %b0 would print %al if operands[0] is reg 0.
;; 'w' Likewise, print the HImode name of the register.
;; 'k' Likewise, print the SImode name of the register.
;; 'h' Print the QImode name for a "high" register, either ah, bh, ch or dh.
;; 'y' Print "st(0)" instead of "st" as a register.
-
+;;
;; UNSPEC usage:
;; 0 This is a `scas' operation. The mode of the UNSPEC is always SImode.
;; operand 0 is the memory address to scan.
;; 6 This is the @GOT offset of a PIC address.
;; 7 This is the @GOTOFF offset of a PIC address.
;; 8 This is a reference to a symbol's @PLT address.
+;; 9 This is an `fnstsw' operation.
+;; 10 This is a `sahf' operation.
+;; 11 This is a `fstcw' operation
\f
-;; This shadows the processor_type enumeration, so changes must be made
-;; to i386.h at the same time.
+;; Processor type. This attribute must exactly match the processor_type
+;; enumeration in i386.h.
+(define_attr "cpu" "i386,i486,pentium,pentiumpro,k6"
+ (const (symbol_ref "ix86_cpu")))
+;; A basic instruction type. Refinements due to arguments to be
+;; provided in other attributes.
(define_attr "type"
- "integer,binary,memory,test,compare,fcompare,idiv,imul,lea,fld,fpop,fpdiv,fpmul"
- (const_string "integer"))
-
-(define_attr "memory" "none,load,store"
- (cond [(eq_attr "type" "idiv,lea")
- (const_string "none")
-
- (eq_attr "type" "fld")
- (const_string "load")
-
- (eq_attr "type" "test")
- (if_then_else (match_operand 0 "memory_operand" "")
- (const_string "load")
- (const_string "none"))
-
- (eq_attr "type" "compare,fcompare")
- (if_then_else (ior (match_operand 0 "memory_operand" "")
- (match_operand 1 "memory_operand" ""))
- (const_string "load")
- (const_string "none"))
-
- (and (eq_attr "type" "integer,memory,fpop")
- (match_operand 0 "memory_operand" ""))
- (const_string "store")
-
- (and (eq_attr "type" "integer,memory,fpop")
+ "other,multi,alu1,negnot,alu,icmp,imov,imovx,lea,incdec,ishift,imul,idiv,ibr,setcc,push,pop,call,callv,icmov,fmov,fop,fop1,fsgn,fmul,fdiv,fpspc,fcmov,fcmp,fxch"
+ (const_string "other"))
+
+;; The (bounding maximum) length of an instruction in bytes.
+(define_attr "length" ""
+ (symbol_ref "ix86_attr_length_default(insn)"))
+
+;; Supporting: number of prefix bytes
+(define_attr "length_prefix" ""
+ (cond [(and (eq_attr "type" "alu,alu1,negnot,icmp,imovx,incdec,ishift,imul,idiv,imov,pop")
+ (match_operand:HI 0 "general_operand" ""))
+ (const_int 1)
+ (and (eq_attr "type" "push")
+ (match_operand:HI 1 "general_operand" ""))
+ (const_int 1)
+ ]
+ (const_int 0)))
+
+;; Supporting: bytes in the opcode+modrm.
+(define_attr "length_opcode" ""
+ (cond [(eq_attr "type" "imovx,setcc,icmov")
+ (const_int 3)
+ (and (eq_attr "type" "incdec")
+ (ior (match_operand:SI 1 "register_operand" "")
+ (match_operand:HI 1 "register_operand" "")))
+ (const_int 1)
+ (and (eq_attr "type" "push")
+ (not (match_operand 1 "memory_operand" "")))
+ (const_int 1)
+ (and (eq_attr "type" "pop")
+ (not (match_operand 0 "memory_operand" "")))
+ (const_int 1)
+ (and (eq_attr "type" "imov")
+ (and (match_operand 0 "register_operand" "")
+ (match_operand 1 "immediate_operand" "")))
+ (const_int 1)
+ ]
+ (const_int 2)))
+
+;; The `memory' attribute is `none' if no memory is referenced, `load' or
+;; `store' if there is a simple memory reference therein, or `unknown'
+;; if the instruction is complex.
+
+(define_attr "memory" "none,load,store,both,unknown"
+ (cond [(eq_attr "type" "other,multi")
+ (const_string "unknown")
+ (eq_attr "type" "lea,fcmov,fpspc")
+ (const_string "none")
+ (eq_attr "type" "push")
+ (if_then_else (match_operand 1 "memory_operand" "")
+ (const_string "both")
+ (const_string "store"))
+ (eq_attr "type" "pop,setcc")
+ (if_then_else (match_operand 0 "memory_operand" "")
+ (const_string "both")
+ (const_string "load"))
+ (eq_attr "type" "icmp")
+ (if_then_else (ior (match_operand 0 "memory_operand" "")
+ (match_operand 1 "memory_operand" ""))
+ (const_string "load")
+ (const_string "none"))
+ (eq_attr "type" "ibr")
+ (if_then_else (match_operand 0 "memory_operand" "")
+ (const_string "load")
+ (const_string "none"))
+ (eq_attr "type" "call")
+ (if_then_else (match_operand 0 "constant_call_address_operand" "")
+ (const_string "none")
+ (const_string "load"))
+ (eq_attr "type" "callv")
+ (if_then_else (match_operand 1 "constant_call_address_operand" "")
+ (const_string "none")
+ (const_string "load"))
+ (and (eq_attr "type" "alu1,negnot")
(match_operand 1 "memory_operand" ""))
- (const_string "load")
-
- (and (eq_attr "type" "binary,imul,fpmul,fpdiv")
- (ior (match_operand 1 "memory_operand" "")
- (match_operand 2 "memory_operand" "")))
- (const_string "load")]
-
+ (const_string "both")
+ (and (match_operand 0 "memory_operand" "")
+ (match_operand 1 "memory_operand" ""))
+ (const_string "both")
+ (match_operand 0 "memory_operand" "")
+ (const_string "store")
+ (match_operand 1 "memory_operand" "")
+ (const_string "load")
+ (and (eq_attr "type" "!icmp,alu1,negnot,fop1,fsgn,imov,imovx,fmov,fcmp")
+ (match_operand 2 "memory_operand" ""))
+ (const_string "load")
+ (and (eq_attr "type" "icmov")
+ (match_operand 3 "memory_operand" ""))
+ (const_string "load")
+ ]
(const_string "none")))
-;; Functional units
+;; Indicates if an instruction has both an immediate and a displacement.
+
+(define_attr "imm_disp" "false,true,unknown"
+ (cond [(eq_attr "type" "other,multi")
+ (const_string "unknown")
+ (and (eq_attr "type" "icmp,imov")
+ (and (match_operand 0 "memory_displacement_operand" "")
+ (match_operand 1 "immediate_operand" "")))
+ (const_string "true")
+ (and (eq_attr "type" "alu,ishift,imul,idiv")
+ (and (match_operand 0 "memory_displacement_operand" "")
+ (match_operand 2 "immediate_operand" "")))
+ (const_string "true")
+ ]
+ (const_string "false")))
+
+;; Indicates if an FP operation has an integer source.
+
+(define_attr "fp_int_src" "false,true"
+ (const_string "false"))
+
+;; Describe a user's asm statement.
+(define_asm_attributes
+ [(set_attr "length" "128")
+ (set_attr "type" "multi")])
+\f
+;; Pentium Scheduling
+;;
+;; The Pentium is an in-order core with two integer pipelines.
+
+;; Categorize how an instruction slots.
+
+;; The non-MMX Pentium slots an instruction with prefixes on U pipe only,
+;; while MMX Pentium can slot it on either U or V. Model non-MMX Pentium
+;; rules, because it results in noticeably better code on non-MMX Pentium
+;; and doesn't hurt much on MMX. (Prefixed instructions are not very
+;; common, so the scheduler usualy has a non-prefixed insn to pair).
+
+(define_attr "pent_pair" "uv,pu,pv,np"
+ (cond [(eq_attr "imm_disp" "true")
+ (const_string "np")
+ (eq_attr "type" "alu1,alu,imov,icmp,lea,incdec")
+ (if_then_else (eq_attr "length_prefix" "1")
+ (const_string "pu")
+ (const_string "uv"))
+ (eq_attr "type" "ibr")
+ (const_string "pv")
+ (and (eq_attr "type" "ishift")
+ (match_operand 2 "const_int_operand" ""))
+ (const_string "pu")
+ (and (eq_attr "type" "pop,push")
+ (eq_attr "memory" "!both"))
+ (if_then_else (eq_attr "length_prefix" "1")
+ (const_string "pu")
+ (const_string "uv"))
+ (and (eq_attr "type" "call")
+ (match_operand 0 "constant_call_address_operand" ""))
+ (const_string "pv")
+ (and (eq_attr "type" "callv")
+ (match_operand 1 "constant_call_address_operand" ""))
+ (const_string "pv")
+ ]
+ (const_string "np")))
+
+;; Rough readiness numbers. Fine tuning happens in i386.c.
+;;
+;; u describes pipe U
+;; v describes pipe V
+;; uv describes either pipe U or V for those that can issue to either
+;; np describes not paring
+;; fpu describes fpu
+;; fpm describes fp insns of different types are not pipelined.
+;;
+;; ??? fxch isn't handled; not an issue until sched3 after reg-stack is real.
-; (define_function_unit NAME MULTIPLICITY SIMULTANEITY
-; TEST READY-DELAY ISSUE-DELAY [CONFLICT-LIST])
+(define_function_unit "pent_np" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "type" "imul"))
+ 11 11)
-; pentiumpro has a reservation station with 5 ports
-; port 0 has integer, float add, integer divide, float divide, float
-; multiply, and shifter units.
-; port 1 has integer, and jump units.
-; port 2 has the load address generation unit
-; ports 3 and 4 have the store address generation units
+(define_function_unit "pent_mul" 1 1
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "type" "imul"))
+ 11 11)
-; pentium has two integer pipelines, the main u pipe and the secondary v pipe.
-; and a float pipeline
+; ??? IDIV for SI takes 46 cycles, for HI 30, for QI 22
+(define_function_unit "pent_np" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "type" "idiv"))
+ 46 46)
+
+; Fp reg-reg moves takes 1 cycle. Loads takes 1 cycle for SF/DF mode,
+; 3 cycles for XFmode. Stores takes 2 cycles for SF/DF and 3 for XF.
+; fldz and fld1 takes 2 cycles. Only reg-reg moves are pairable.
+; The integer <-> fp conversion is not modeled correctly. Fild behaves
+; like normal fp operation and fist takes 6 cycles.
+
+(define_function_unit "fpu" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (and (eq_attr "type" "fmov")
+ (ior (and (eq_attr "memory" "store")
+ (match_operand:XF 0 "memory_operand" ""))
+ (and (eq_attr "memory" "load")
+ (match_operand:XF 1 "memory_operand" "")))))
+ 3 3)
+
+(define_function_unit "pent_np" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (and (eq_attr "type" "fmov")
+ (ior (and (eq_attr "memory" "store")
+ (match_operand:XF 0 "memory_operand" ""))
+ (and (eq_attr "memory" "load")
+ (match_operand:XF 1 "memory_operand" "")))))
+ 3 3)
+
+(define_function_unit "fpu" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (and (eq_attr "type" "fmov")
+ (ior (match_operand 1 "immediate_operand" "")
+ (eq_attr "memory" "store"))))
+ 2 2)
-;; Floating point
+(define_function_unit "pent_np" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (and (eq_attr "type" "fmov")
+ (ior (match_operand 1 "immediate_operand" "")
+ (eq_attr "memory" "store"))))
+ 2 2)
-(define_function_unit "fp" 1 0
- (and (eq_attr "type" "fpop,fcompare") (eq_attr "cpu" "i386,i486"))
- 5 5)
+(define_function_unit "fpu" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (and (eq_attr "type" "fmov")
+ (eq_attr "memory" "none,load")))
+ 1 1)
+
+; Read/Modify/Write instructions usually take 3 cycles.
+(define_function_unit "pent_u" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (and (eq_attr "type" "alu,alu1,ishift")
+ (and (eq_attr "pent_pair" "pu")
+ (eq_attr "memory" "both"))))
+ 3 3)
+
+(define_function_unit "pent_uv" 2 0
+ (and (eq_attr "cpu" "pentium")
+ (and (eq_attr "type" "alu,alu1,ishift")
+ (and (eq_attr "pent_pair" "!np")
+ (eq_attr "memory" "both"))))
+ 3 3)
+
+(define_function_unit "pent_np" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (and (eq_attr "type" "alu,alu1,negnot,ishift")
+ (and (eq_attr "pent_pair" "np")
+ (eq_attr "memory" "both"))))
+ 3 3)
+
+; Read/Modify or Modify/Write instructions usually take 2 cycles.
+(define_function_unit "pent_u" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (and (eq_attr "type" "alu,ishift")
+ (and (eq_attr "pent_pair" "pu")
+ (eq_attr "memory" "load,store"))))
+ 2 2)
-(define_function_unit "fp" 1 0
- (and (eq_attr "type" "fpop,fcompare") (eq_attr "cpu" "pentium,pentiumpro"))
- 3 0)
+(define_function_unit "pent_uv" 2 0
+ (and (eq_attr "cpu" "pentium")
+ (and (eq_attr "type" "alu,ishift")
+ (and (eq_attr "pent_pair" "!np")
+ (eq_attr "memory" "load,store"))))
+ 2 2)
-(define_function_unit "fp" 1 0
- (and (eq_attr "type" "fpmul") (eq_attr "cpu" "pentium"))
- 7 0)
+(define_function_unit "pent_np" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (and (eq_attr "type" "alu,ishift")
+ (and (eq_attr "pent_pair" "np")
+ (eq_attr "memory" "load,store"))))
+ 2 2)
-(define_function_unit "fp" 1 0
- (and (eq_attr "type" "fpmul") (eq_attr "cpu" "pentiumpro"))
- 5 0)
+; Insns w/o memory operands and move instructions usually take one cycle.
+(define_function_unit "pent_u" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "pent_pair" "pu"))
+ 1 1)
+
+(define_function_unit "pent_v" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "pent_pair" "pv"))
+ 1 1)
+
+(define_function_unit "pent_uv" 2 0
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "pent_pair" "!np"))
+ 1 1)
+
+(define_function_unit "pent_np" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "pent_pair" "np"))
+ 1 1)
+
+; Pairable insns only conflict with other non-pairable insns.
+(define_function_unit "pent_np" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (and (eq_attr "type" "alu,alu1,ishift")
+ (and (eq_attr "pent_pair" "!np")
+ (eq_attr "memory" "both"))))
+ 3 3
+ [(eq_attr "pent_pair" "np")])
+
+(define_function_unit "pent_np" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (and (eq_attr "type" "alu,alu1,ishift")
+ (and (eq_attr "pent_pair" "!np")
+ (eq_attr "memory" "load,store"))))
+ 2 2
+ [(eq_attr "pent_pair" "np")])
+
+(define_function_unit "pent_np" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "pent_pair" "!np"))
+ 1 1
+ [(eq_attr "pent_pair" "np")])
+
+; Floating point instructions usually blocks cycle longer when combined with
+; integer instructions, because of the inpaired fxch instruction.
+(define_function_unit "pent_np" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "type" "fmov,fop,fop1,fsgn,fmul,fpspc,fcmov,fcmp"))
+ 2 2
+ [(eq_attr "type" "!fmov,fop,fop1,fsgn,fmul,fpspc,fcmov,fcmp")])
+
+(define_function_unit "fpu" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "type" "fcmp,fxch,fsgn"))
+ 1 1)
+
+; Addition takes 3 cycles; assume other random cruft does as well.
+; ??? Trivial fp operations such as fabs or fchs takes only one cycle.
+(define_function_unit "fpu" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "type" "fop,fop1"))
+ 3 1)
+
+; Multiplication takes 3 cycles and is only half pipelined.
+(define_function_unit "fpu" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "type" "fmul"))
+ 3 1)
+
+(define_function_unit "pent_mul" 1 1
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "type" "fmul"))
+ 2 2)
-(define_function_unit "fp" 1 0
- (and (eq_attr "type" "idiv") (eq_attr "cpu" "pentiumpro"))
- 10 10)
+; ??? This is correct only for fdiv and sqrt -- sin/cos take 65-100 cycles.
+; They can overlap with integer insns. Only the last two cycles can overlap
+; with other fp insns. Only fsin/fcos can overlap with multiplies.
+; Only last two cycles of fsin/fcos can overlap with other instructions.
+(define_function_unit "fpu" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "type" "fdiv"))
+ 39 37)
+
+(define_function_unit "pent_mul" 1 1
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "type" "fdiv"))
+ 39 39)
+
+(define_function_unit "fpu" 1 0
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "type" "fpspc"))
+ 70 68)
+
+(define_function_unit "pent_mul" 1 1
+ (and (eq_attr "cpu" "pentium")
+ (eq_attr "type" "fpspc"))
+ 70 70)
+\f
+;; Pentium Pro/PII Scheduling
+;;
+;; The PPro has an out-of-order core, but the instruction decoders are
+;; naturally in-order and asymmetric. We get best performance by scheduling
+;; for the decoders, for in doing so we give the oo execution unit the
+;; most choices.
+
+;; Categorize how many uops an ia32 instruction evaluates to:
+;; one -- an instruction with 1 uop can be decoded by any of the
+;; three decoders.
+;; few -- an instruction with 1 to 4 uops can be decoded only by
+;; decoder 0.
+;; many -- a complex instruction may take an unspecified number of
+;; cycles to decode in decoder 0.
+
+(define_attr "ppro_uops" "one,few,many"
+ (cond [(eq_attr "type" "other,multi,call,callv,fpspc")
+ (const_string "many")
+ (eq_attr "type" "icmov,fcmov")
+ (const_string "few")
+ (eq_attr "type" "imov")
+ (if_then_else (eq_attr "memory" "store,both")
+ (const_string "few")
+ (const_string "one"))
+ (eq_attr "memory" "!none")
+ (const_string "few")
+ ]
+ (const_string "one")))
+
+;; Rough readiness numbers. Fine tuning happens in i386.c.
+;;
+;; p0 describes port 0.
+;; p01 describes ports 0 and 1 as a pair; alu insns can issue to either.
+;; p2 describes port 2 for loads.
+;; p34 describes ports 3 and 4 for stores.
+;; fpu describes the fpu accessed via port 0.
+;; ??? It is less than clear if there are separate fadd and fmul units
+;; that could operate in parallel.
+;;
+;; ??? fxch isn't handled; not an issue until sched3 after reg-stack is real.
+
+(define_function_unit "ppro_p0" 1 0
+ (and (eq_attr "cpu" "pentiumpro")
+ (eq_attr "type" "ishift,lea,ibr"))
+ 1 1)
+
+(define_function_unit "ppro_p0" 1 0
+ (and (eq_attr "cpu" "pentiumpro")
+ (eq_attr "type" "imul"))
+ 4 1)
+
+;; ??? Does the divider lock out the pipe while it works,
+;; or is there a disconnected unit?
+(define_function_unit "ppro_p0" 1 0
+ (and (eq_attr "cpu" "pentiumpro")
+ (eq_attr "type" "idiv"))
+ 17 17)
-(define_function_unit "fp" 1 0
- (and (eq_attr "type" "imul") (eq_attr "cpu" "pentiumpro"))
- 6 0)
+(define_function_unit "ppro_p0" 1 0
+ (and (eq_attr "cpu" "pentiumpro")
+ (eq_attr "type" "fop,fop1,fsgn"))
+ 3 1)
+
+(define_function_unit "ppro_p0" 1 0
+ (and (eq_attr "cpu" "pentiumpro")
+ (eq_attr "type" "fcmov"))
+ 2 1)
+
+(define_function_unit "ppro_p0" 1 0
+ (and (eq_attr "cpu" "pentiumpro")
+ (eq_attr "type" "fcmp"))
+ 1 1)
+
+(define_function_unit "ppro_p0" 1 0
+ (and (eq_attr "cpu" "pentiumpro")
+ (eq_attr "type" "fmov"))
+ 1 1)
+
+(define_function_unit "ppro_p0" 1 0
+ (and (eq_attr "cpu" "pentiumpro")
+ (eq_attr "type" "fmul"))
+ 5 1)
+
+(define_function_unit "ppro_p0" 1 0
+ (and (eq_attr "cpu" "pentiumpro")
+ (eq_attr "type" "fdiv,fpspc"))
+ 56 1)
+
+(define_function_unit "ppro_p01" 2 0
+ (and (eq_attr "cpu" "pentiumpro")
+ (eq_attr "type" "!imov,fmov"))
+ 1 1)
+
+(define_function_unit "ppro_p01" 2 0
+ (and (and (eq_attr "cpu" "pentiumpro")
+ (eq_attr "type" "imov,fmov"))
+ (eq_attr "memory" "none"))
+ 1 1)
+
+(define_function_unit "ppro_p2" 1 0
+ (and (eq_attr "cpu" "pentiumpro")
+ (ior (eq_attr "type" "pop")
+ (eq_attr "memory" "load,both")))
+ 3 1)
+
+(define_function_unit "ppro_p34" 1 0
+ (and (eq_attr "cpu" "pentiumpro")
+ (ior (eq_attr "type" "push")
+ (eq_attr "memory" "store,both")))
+ 1 1)
+
+(define_function_unit "fpu" 1 0
+ (and (eq_attr "cpu" "pentiumpro")
+ (eq_attr "type" "fop,fop1,fsgn,fmov,fcmp,fcmov"))
+ 1 1)
+
+(define_function_unit "fpu" 1 0
+ (and (eq_attr "cpu" "pentiumpro")
+ (eq_attr "type" "fmul"))
+ 5 2)
+
+(define_function_unit "fpu" 1 0
+ (and (eq_attr "cpu" "pentiumpro")
+ (eq_attr "type" "fdiv,fpspc"))
+ 56 56)
+
+;; imul uses the fpu. ??? does it have the same throughput as fmul?
+(define_function_unit "fpu" 1 0
+ (and (eq_attr "cpu" "pentiumpro")
+ (eq_attr "type" "imul"))
+ 4 1)
+\f
+;; AMD K6/K6-2 Scheduling
+;;
+;; The K6 has similar architecture to PPro. Important difference is, that
+;; there are only two decoders and they seems to be much slower than execution
+;; units. So we have to pay much more attention to proper decoding for
+;; schedulers. We share most of scheduler code for PPro in i386.c
+;;
+;; The fp unit is not pipelined and do one operation per two cycles including
+;; the FXCH.
+;;
+;; alu describes both ALU units (ALU-X and ALU-Y).
+;; alux describes X alu unit
+;; fpu describes FPU unit
+;; load describes load unit.
+;; branch describes branch unit.
+;; store decsribes store unit. This unit is not modelled completely and only
+;; used to model lea operation. Otherwise it lie outside of the critical
+;; path.
+;;
+;; ??? fxch isn't handled; not an issue until sched3 after reg-stack is real.
-(define_function_unit "fp" 1 0
- (eq_attr "type" "fpdiv")
- 10 10)
+;; The decoder specification is in the PPro section above!
-(define_function_unit "fp" 1 0
- (and (eq_attr "type" "fld") (eq_attr "cpu" "!pentiumpro,k6"))
- 1 0)
+;; Shift instructions and certain arithmetic are issued only to X pipe.
+(define_function_unit "k6_alux" 1 0
+ (and (eq_attr "cpu" "k6")
+ (eq_attr "type" "ishift,alu1,negnot"))
+ 1 1)
-;; K6 FPU is not pipelined.
-(define_function_unit "fp" 1 0
- (and (eq_attr "type" "fpop,fpmul,fcompare") (eq_attr "cpu" "k6"))
- 2 2)
+;; The QI mode arithmetic is issued to X pipe only.
+(define_function_unit "k6_alux" 1 0
+ (and (eq_attr "cpu" "k6")
+ (and (eq_attr "type" "alu,alu1,negnot,icmp,imovx,incdec")
+ (match_operand:QI 0 "general_operand" "")))
+ 1 1)
-;; i386 and i486 have one integer unit, which need not be modeled
+(define_function_unit "k6_alu" 2 0
+ (and (eq_attr "cpu" "k6")
+ (eq_attr "type" "ishift,alu1,negnot,alu,icmp,imovx,incdec,setcc,lea"))
+ 1 1)
-(define_function_unit "integer" 2 0
- (and (eq_attr "type" "integer,binary,test,compare,lea") (eq_attr "cpu" "pentium,pentiumpro"))
- 1 0)
+(define_function_unit "k6_alu" 2 0
+ (and (eq_attr "cpu" "k6")
+ (and (eq_attr "type" "imov")
+ (eq_attr "memory" "none")))
+ 1 1)
-(define_function_unit "integer" 2 0
+(define_function_unit "k6_branch" 1 0
(and (eq_attr "cpu" "k6")
- (and (eq_attr "type" "integer,binary,test,compare")
- (eq_attr "memory" "!load")))
- 1 0)
+ (eq_attr "type" "call,callv,ibr"))
+ 1 1)
-;; Internally, K6 converts REG OP MEM instructions into a load (2 cycles)
-;; and a register operation (1 cycle).
-(define_function_unit "integer" 2 0
+;; Load unit have two cycle latency, but we take care for it in adjust_cost
+(define_function_unit "k6_load" 1 0
(and (eq_attr "cpu" "k6")
- (and (eq_attr "type" "integer,binary,test,compare")
- (eq_attr "memory" "load")))
- 3 0)
+ (ior (eq_attr "type" "pop")
+ (eq_attr "memory" "load,both")))
+ 1 1)
-;; Multiplies use one of the integer units
-(define_function_unit "integer" 2 0
- (and (eq_attr "cpu" "pentium") (eq_attr "type" "imul"))
- 11 11)
+;; Lea have two instructions, so latency is probably 2
+(define_function_unit "k6_store" 1 0
+ (and (eq_attr "cpu" "k6")
+ (eq_attr "type" "lea"))
+ 2 1)
-(define_function_unit "integer" 2 0
- (and (eq_attr "cpu" "k6") (eq_attr "type" "imul"))
- 2 2)
+(define_function_unit "k6_store" 1 0
+ (and (eq_attr "cpu" "k6")
+ (ior (eq_attr "type" "push")
+ (eq_attr "memory" "store,both")))
+ 1 1)
-(define_function_unit "integer" 2 0
- (and (eq_attr "cpu" "pentium") (eq_attr "type" "idiv"))
- 25 25)
+(define_function_unit "k6_fpu" 1 1
+ (and (eq_attr "cpu" "k6")
+ (eq_attr "type" "fop,fop1,fmov,fcmp"))
+ 2 2)
-(define_function_unit "integer" 2 0
- (and (eq_attr "cpu" "k6") (eq_attr "type" "idiv"))
- 17 17)
+(define_function_unit "k6_fpu" 1 1
+ (and (eq_attr "cpu" "k6")
+ (eq_attr "type" "fmul"))
+ 2 2)
-;; Pentium Pro and K6 have a separate load unit.
-(define_function_unit "load" 1 0
- (and (eq_attr "cpu" "pentiumpro") (eq_attr "memory" "load"))
- 3 0)
+;; ??? Guess
+(define_function_unit "k6_fpu" 1 1
+ (and (eq_attr "cpu" "k6")
+ (eq_attr "type" "fdiv,fpspc"))
+ 56 56)
-(define_function_unit "load" 1 0
- (and (eq_attr "cpu" "k6") (eq_attr "memory" "load"))
- 2 0)
+(define_function_unit "k6_alu" 2 0
+ (and (eq_attr "cpu" "k6")
+ (eq_attr "type" "imul"))
+ 2 2)
-;; Pentium Pro and K6 have a separate store unit.
-(define_function_unit "store" 1 0
- (and (eq_attr "cpu" "pentiumpro,k6") (eq_attr "memory" "store"))
- 1 0)
+(define_function_unit "k6_alux" 1 0
+ (and (eq_attr "cpu" "k6")
+ (eq_attr "type" "imul"))
+ 2 2)
-;; lea executes in the K6 store unit with 1 cycle latency
-(define_function_unit "store" 1 0
- (and (eq_attr "cpu" "k6") (eq_attr "type" "lea"))
- 1 0)
+;; ??? Guess
+(define_function_unit "k6_alu" 2 0
+ (and (eq_attr "cpu" "k6")
+ (eq_attr "type" "idiv"))
+ 17 17)
+(define_function_unit "k6_alux" 1 0
+ (and (eq_attr "cpu" "k6")
+ (eq_attr "type" "idiv"))
+ 17 17)
\f
-;; "movl MEM,REG / testl REG,REG" is faster on a 486 than "cmpl $0,MEM".
-;; But restricting MEM here would mean that gcc could not remove a redundant
-;; test in cases like "incl MEM / je TARGET".
-;;
-;; We don't want to allow a constant operand for test insns because
-;; (set (cc0) (const_int foo)) has no mode information. Such insns will
-;; be folded while optimizing anyway.
+;; Compare instructions.
-;; All test insns have expanders that save the operands away without
+;; All compare insns have expanders that save the operands away without
;; actually generating RTL. The bCOND or sCOND (emitted immediately
-;; after the tstM or cmp) will actually emit the tstM or cmpM.
-
-;; Processor type -- this attribute must exactly match the processor_type
-;; enumeration in i386.h.
-
-(define_attr "cpu" "i386,i486,pentium,pentiumpro,k6"
- (const (symbol_ref "ix86_cpu")))
-
-(define_insn "tstsi_1"
- [(set (cc0)
- (match_operand:SI 0 "nonimmediate_operand" "rm"))]
- ""
- "*
-{
- if (REG_P (operands[0]))
- return AS2 (test%L0,%0,%0);
+;; after the cmp) will actually emit the cmpM.
- operands[1] = const0_rtx;
- return AS2 (cmp%L0,%1,%0);
-}"
- [(set_attr "type" "test")])
-
-(define_expand "tstsi"
- [(set (cc0)
- (match_operand:SI 0 "nonimmediate_operand" ""))]
+(define_expand "cmpdi"
+ [(set (reg:CC 17)
+ (compare:CC (match_operand:DI 0 "general_operand" "")
+ (match_operand:DI 1 "general_operand" "")))]
""
"
{
- i386_compare_gen = gen_tstsi_1;
- i386_compare_op0 = operands[0];
- i386_compare_op1 = const0_rtx;
+ if ((GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
+ || (CONSTANT_P (operands[0]) && CONSTANT_P (operands[1])))
+ operands[0] = force_reg (DImode, operands[0]);
+ ix86_compare_op0 = operands[0];
+ ix86_compare_op1 = operands[1];
DONE;
}")
-(define_insn "tsthi_1"
- [(set (cc0)
- (match_operand:HI 0 "nonimmediate_operand" "rm"))]
- ""
- "*
-{
- if (REG_P (operands[0]))
- return AS2 (test%W0,%0,%0);
-
- operands[1] = const0_rtx;
- return AS2 (cmp%W0,%1,%0);
-}"
- [(set_attr "type" "test")])
-
-(define_expand "tsthi"
- [(set (cc0)
- (match_operand:HI 0 "nonimmediate_operand" ""))]
+(define_expand "cmpsi"
+ [(set (reg:CC 17)
+ (compare:CC (match_operand:SI 0 "cmpsi_operand" "")
+ (match_operand:SI 1 "general_operand" "")))]
""
"
{
- i386_compare_gen = gen_tsthi_1;
- i386_compare_op0 = operands[0];
- i386_compare_op1 = const0_rtx;
+ if ((GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
+ || (CONSTANT_P (operands[0]) && CONSTANT_P (operands[1])))
+ operands[0] = force_reg (SImode, operands[0]);
+ ix86_compare_op0 = operands[0];
+ ix86_compare_op1 = operands[1];
DONE;
}")
-(define_insn "tstqi_1"
- [(set (cc0)
- (match_operand:QI 0 "nonimmediate_operand" "qm"))]
- ""
- "*
-{
- if (REG_P (operands[0]))
- return AS2 (test%B0,%0,%0);
-
- operands[1] = const0_rtx;
- return AS2 (cmp%B0,%1,%0);
-}"
- [(set_attr "type" "test")])
-
-(define_expand "tstqi"
- [(set (cc0)
- (match_operand:QI 0 "nonimmediate_operand" ""))]
+(define_expand "cmphi"
+ [(set (reg:CC 17)
+ (compare:CC (match_operand:HI 0 "general_operand" "")
+ (match_operand:HI 1 "general_operand" "")))]
""
"
{
- i386_compare_gen = gen_tstqi_1;
- i386_compare_op0 = operands[0];
- i386_compare_op1 = const0_rtx;
+ if ((GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
+ || (CONSTANT_P (operands[0]) && CONSTANT_P (operands[1])))
+ operands[0] = force_reg (HImode, operands[0]);
+ ix86_compare_op0 = operands[0];
+ ix86_compare_op1 = operands[1];
DONE;
}")
-(define_insn "tstsf_cc"
- [(set (cc0)
- (match_operand:SF 0 "register_operand" "f"))
- (clobber (match_scratch:HI 1 "=a"))]
- "TARGET_80387 && ! TARGET_IEEE_FP"
- "*
-{
- if (! STACK_TOP_P (operands[0]))
- abort ();
-
- output_asm_insn (\"ftst\", operands);
-
- if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
- output_asm_insn (AS1 (fstp,%y0), operands);
-
- return output_fp_cc0_set (insn);
-}"
- [(set_attr "type" "test")])
-
-;; Don't generate tstsf if generating IEEE code, since the `ftst' opcode
-;; isn't IEEE compliant.
-
-(define_expand "tstsf"
- [(parallel [(set (cc0)
- (match_operand:SF 0 "register_operand" ""))
- (clobber (match_scratch:HI 1 ""))])]
- "TARGET_80387 && ! TARGET_IEEE_FP"
+(define_expand "cmpqi"
+ [(set (reg:CC 17)
+ (compare:CC (match_operand:QI 0 "general_operand" "")
+ (match_operand:QI 1 "general_operand" "")))]
+ ""
"
{
- i386_compare_gen = gen_tstsf_cc;
- i386_compare_op0 = operands[0];
- i386_compare_op1 = const0_rtx;
+ if ((GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
+ || (CONSTANT_P (operands[0]) && CONSTANT_P (operands[1])))
+ operands[0] = force_reg (QImode, operands[0]);
+ ix86_compare_op0 = operands[0];
+ ix86_compare_op1 = operands[1];
DONE;
}")
-(define_insn "tstdf_cc"
- [(set (cc0)
- (match_operand:DF 0 "register_operand" "f"))
- (clobber (match_scratch:HI 1 "=a"))]
- "TARGET_80387 && ! TARGET_IEEE_FP"
- "*
-{
- if (! STACK_TOP_P (operands[0]))
- abort ();
+(define_insn "cmpsi_0"
+ [(set (reg:CCNO 17)
+ (compare:CCNO (match_operand:SI 0 "nonimmediate_operand" "r,?mr")
+ (match_operand:SI 1 "const0_operand" "n,n")))]
+ ""
+ "@
+ test{l}\\t{%0, %0|%0, %0}
+ cmp{l}\\t{%1, %0|%0, %1}"
+ [(set_attr "type" "icmp")])
- output_asm_insn (\"ftst\", operands);
+(define_insn "cmpsi_1"
+ [(set (reg:CC 17)
+ (compare:CC (match_operand:SI 0 "nonimmediate_operand" "rm,r")
+ (match_operand:SI 1 "general_operand" "ri,mr")))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "cmp{l}\\t{%1, %0|%0, %1}"
+ [(set_attr "type" "icmp")])
- if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
- output_asm_insn (AS1 (fstp,%y0), operands);
+(define_insn "cmphi_0"
+ [(set (reg:CCNO 17)
+ (compare:CCNO (match_operand:HI 0 "nonimmediate_operand" "r,?mr")
+ (match_operand:HI 1 "const0_operand" "n,n")))]
+ ""
+ "@
+ test{w}\\t{%0, %0|%0, %0}
+ cmp{w}\\t{%1, %0|%0, %1}"
+ [(set_attr "type" "icmp")])
- return output_fp_cc0_set (insn);
-}"
- [(set_attr "type" "test")])
+(define_insn "cmphi_1"
+ [(set (reg:CC 17)
+ (compare:CC (match_operand:HI 0 "nonimmediate_operand" "rm,r")
+ (match_operand:HI 1 "general_operand" "ri,mr")))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "cmp{w}\\t{%1, %0|%0, %1}"
+ [(set_attr "type" "icmp")])
+
+(define_insn "cmpqi_0"
+ [(set (reg:CCNO 17)
+ (compare:CCNO (match_operand:QI 0 "nonimmediate_operand" "q,?mq")
+ (match_operand:QI 1 "const0_operand" "n,n")))]
+ ""
+ "@
+ test{b}\\t{%0, %0|%0, %0}
+ cmp{b}\\t{$0, %0|%0, 0}"
+ [(set_attr "type" "icmp")])
-;; Don't generate tstdf if generating IEEE code, since the `ftst' opcode
-;; isn't IEEE compliant.
+(define_insn "cmpqi_1"
+ [(set (reg:CC 17)
+ (compare:CC (match_operand:QI 0 "nonimmediate_operand" "qm,q")
+ (match_operand:QI 1 "general_operand" "qi,mq")))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "cmp{b}\\t{%1, %0|%0, %1}"
+ [(set_attr "type" "icmp")])
+
+(define_insn "*cmpqi_ext_1"
+ [(set (reg:CC 17)
+ (compare:CC
+ (match_operand:QI 0 "general_operand" "qm")
+ (subreg:QI
+ (zero_extract:SI
+ (match_operand 1 "ext_register_operand" "q")
+ (const_int 8)
+ (const_int 8)) 0)))]
+ ""
+ "cmp{b}\\t{%h1, %0|%0, %h1}"
+ [(set_attr "type" "icmp")])
+
+(define_insn "*cmpqi_ext_2"
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (subreg:QI
+ (zero_extract:SI
+ (match_operand 0 "ext_register_operand" "q")
+ (const_int 8)
+ (const_int 8)) 0)
+ (match_operand:QI 1 "const0_operand" "n")))]
+ ""
+ "test{b}\\t%h0, %h0"
+ [(set_attr "type" "icmp")])
+
+(define_insn "cmpqi_ext_3"
+ [(set (reg:CC 17)
+ (compare:CC
+ (subreg:QI
+ (zero_extract:SI
+ (match_operand 0 "ext_register_operand" "q")
+ (const_int 8)
+ (const_int 8)) 0)
+ (match_operand:QI 1 "general_operand" "qmn")))]
+ ""
+ "cmp{b}\\t{%1, %h0|%h0, %1}"
+ [(set_attr "type" "icmp")])
+
+(define_insn "*cmpqi_ext_4"
+ [(set (reg:CC 17)
+ (compare:CC
+ (subreg:QI
+ (zero_extract:SI
+ (match_operand 0 "ext_register_operand" "q")
+ (const_int 8)
+ (const_int 8)) 0)
+ (subreg:QI
+ (zero_extract:SI
+ (match_operand 1 "ext_register_operand" "q")
+ (const_int 8)
+ (const_int 8)) 0)))]
+ ""
+ "cmp{b}\\t{%h1, %h0|%h0, %h1}"
+ [(set_attr "type" "icmp")])
+
+;; These implement float point compares.
+;; %%% See if we can get away with VOIDmode operands on the actual insns,
+;; which would allow mix and match FP modes on the compares. Which is what
+;; the old patterns did, but with many more of them.
-(define_expand "tstdf"
- [(parallel [(set (cc0)
- (match_operand:DF 0 "register_operand" ""))
- (clobber (match_scratch:HI 1 ""))])]
- "TARGET_80387 && ! TARGET_IEEE_FP"
+(define_expand "cmpxf"
+ [(set (reg:CC 17)
+ (compare:CC (match_operand:XF 0 "cmp_fp_expander_operand" "")
+ (match_operand:XF 1 "cmp_fp_expander_operand" "")))]
+ "TARGET_80387"
"
{
- i386_compare_gen = gen_tstdf_cc;
- i386_compare_op0 = operands[0];
- i386_compare_op1 = const0_rtx;
+ ix86_compare_op0 = operands[0];
+ ix86_compare_op1 = operands[1];
DONE;
}")
-(define_insn "tstxf_cc"
- [(set (cc0)
- (match_operand:XF 0 "register_operand" "f"))
- (clobber (match_scratch:HI 1 "=a"))]
- "TARGET_80387 && ! TARGET_IEEE_FP"
- "*
-{
- if (! STACK_TOP_P (operands[0]))
- abort ();
-
- output_asm_insn (\"ftst\", operands);
-
- if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
- output_asm_insn (AS1 (fstp,%y0), operands);
-
- return output_fp_cc0_set (insn);
-}"
- [(set_attr "type" "test")])
-
-;; Don't generate tstxf if generating IEEE code, since the `ftst' opcode
-;; isn't IEEE compliant.
-
-(define_expand "tstxf"
- [(parallel [(set (cc0)
- (match_operand:XF 0 "register_operand" ""))
- (clobber (match_scratch:HI 1 ""))])]
- "TARGET_80387 && ! TARGET_IEEE_FP"
+(define_expand "cmpdf"
+ [(set (reg:CC 17)
+ (compare:CC (match_operand:DF 0 "cmp_fp_expander_operand" "")
+ (match_operand:DF 1 "cmp_fp_expander_operand" "")))]
+ "TARGET_80387"
"
{
- i386_compare_gen = gen_tstxf_cc;
- i386_compare_op0 = operands[0];
- i386_compare_op1 = const0_rtx;
+ ix86_compare_op0 = operands[0];
+ ix86_compare_op1 = operands[1];
DONE;
}")
-\f
-;;- compare instructions. See comments above tstM patterns about
-;; expansion of these insns.
-
-(define_insn "cmpsi_1"
- [(set (cc0)
- (compare (match_operand:SI 0 "nonimmediate_operand" "mr,r")
- (match_operand:SI 1 "general_operand" "ri,mr")))]
- "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
- "* return AS2 (cmp%L0,%1,%0);"
- [(set_attr "type" "compare")])
-(define_expand "cmpsi"
- [(set (cc0)
- (compare (match_operand:SI 0 "nonimmediate_operand" "")
- (match_operand:SI 1 "general_operand" "")))]
- ""
+(define_expand "cmpsf"
+ [(set (reg:CC 17)
+ (compare:CC (match_operand:SF 0 "cmp_fp_expander_operand" "")
+ (match_operand:SF 1 "cmp_fp_expander_operand" "")))]
+ "TARGET_80387"
"
{
- if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
- operands[0] = force_reg (SImode, operands[0]);
-
- i386_compare_gen = gen_cmpsi_1;
- i386_compare_op0 = operands[0];
- i386_compare_op1 = operands[1];
+ ix86_compare_op0 = operands[0];
+ ix86_compare_op1 = operands[1];
DONE;
}")
-(define_insn "cmphi_1"
- [(set (cc0)
- (compare (match_operand:HI 0 "nonimmediate_operand" "mr,r")
- (match_operand:HI 1 "general_operand" "ri,mr")))]
- "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
- "* return AS2 (cmp%W0,%1,%0);"
- [(set_attr "type" "compare")])
-
-(define_expand "cmphi"
- [(set (cc0)
- (compare (match_operand:HI 0 "nonimmediate_operand" "")
- (match_operand:HI 1 "general_operand" "")))]
- ""
- "
-{
- if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
- operands[0] = force_reg (HImode, operands[0]);
+;; FP compares, step 1:
+;; Set the FP condition codes.
+;;
+;; CCFPmode compare with exceptions
+;; CCFPUmode compare with no exceptions
- i386_compare_gen = gen_cmphi_1;
- i386_compare_op0 = operands[0];
- i386_compare_op1 = operands[1];
- DONE;
-}")
+;; %%% It is an unfortunate fact that ftst has no non-popping variant,
+;; and that fp moves clobber the condition codes, and that there is
+;; currently no way to describe this fact to reg-stack. So there are
+;; no splitters yet for this.
-(define_insn "cmpqi_1"
- [(set (cc0)
- (compare (match_operand:QI 0 "nonimmediate_operand" "q,mq")
- (match_operand:QI 1 "general_operand" "qm,nq")))]
- "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
- "* return AS2 (cmp%B0,%1,%0);"
- [(set_attr "type" "compare")])
+;; %%% YIKES! This scheme does not retain a strong connection between
+;; the real compare and the ultimate cc0 user, so CC_REVERSE does not
+;; work! Only allow tos/mem with tos in op 0.
+;;
+;; Hmm, of course, this is what the actual _hardware_ does. Perhaps
+;; things aren't as bad as they sound...
-(define_expand "cmpqi"
- [(set (cc0)
- (compare (match_operand:QI 0 "nonimmediate_operand" "")
- (match_operand:QI 1 "general_operand" "")))]
- ""
- "
+(define_insn "*cmpfp_0"
+ [(set (match_operand:HI 0 "register_operand" "=a")
+ (unspec:HI
+ [(compare:CCFP (match_operand 1 "register_operand" "f")
+ (match_operand 2 "const0_operand" "X"))] 9))]
+ "TARGET_80387
+ && FLOAT_MODE_P (GET_MODE (operands[1]))
+ && GET_MODE (operands[1]) == GET_MODE (operands[2])"
+ "*
{
- if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
- operands[0] = force_reg (QImode, operands[0]);
-
- i386_compare_gen = gen_cmpqi_1;
- i386_compare_op0 = operands[0];
- i386_compare_op1 = operands[1];
- DONE;
-}")
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"ftst\;fnstsw\\t%0\;fstp\\t%y0\";
+ else
+ return \"ftst\;fnstsw\\t%0\";
+}"
+ [(set_attr "type" "multi")])
-;; These implement float point compares. For each of DFmode and
-;; SFmode, there is the normal insn, and an insn where the second operand
-;; is converted to the desired mode.
+;; We may not use "#" to split and emit these, since the REG_DEAD notes
+;; used to manage the reg stack popping would not be preserved.
-(define_insn ""
- [(set (cc0)
- (match_operator 2 "VOIDmode_compare_op"
- [(match_operand:XF 0 "register_operand" "f")
- (match_operand:XF 1 "register_operand" "f")]))
- (clobber (match_scratch:HI 3 "=a"))]
+(define_insn "*cmpfp_2_sf"
+ [(set (reg:CCFP 18)
+ (compare:CCFP
+ (match_operand:SF 0 "register_operand" "f")
+ (match_operand:SF 1 "nonimmediate_operand" "fm")))]
"TARGET_80387"
- "* return output_float_compare (insn, operands);"
- [(set_attr "type" "fcompare")])
+ "* return output_fp_compare (insn, operands, 0, 0);"
+ [(set_attr "type" "fcmp")])
(define_insn ""
- [(set (cc0)
- (match_operator 2 "VOIDmode_compare_op"
- [(match_operand:XF 0 "register_operand" "f")
- (float_extend:XF
- (match_operand:DF 1 "nonimmediate_operand" "fm"))]))
- (clobber (match_scratch:HI 3 "=a"))]
+ [(set (match_operand:HI 0 "register_operand" "=a")
+ (unspec:HI
+ [(compare:CCFP
+ (match_operand:SF 1 "register_operand" "f")
+ (match_operand:SF 2 "nonimmediate_operand" "fm"))] 9))]
"TARGET_80387"
- "* return output_float_compare (insn, operands);"
- [(set_attr "type" "fcompare")])
-
-(define_insn ""
- [(set (cc0)
- (match_operator 2 "VOIDmode_compare_op"
- [(float_extend:XF
- (match_operand:DF 0 "nonimmediate_operand" "fm"))
- (match_operand:XF 1 "register_operand" "f")]))
- (clobber (match_scratch:HI 3 "=a"))]
+ "* return output_fp_compare (insn, operands, 2, 0);"
+ [(set_attr "type" "fcmp")])
+
+(define_insn "*cmpfp_2_df"
+ [(set (reg:CCFP 18)
+ (compare:CCFP
+ (match_operand:DF 0 "register_operand" "f")
+ (match_operand:DF 1 "nonimmediate_operand" "fm")))]
"TARGET_80387"
- "* return output_float_compare (insn, operands);"
- [(set_attr "type" "fcompare")])
+ "* return output_fp_compare (insn, operands, 0, 0);"
+ [(set_attr "type" "fcmp")])
(define_insn ""
- [(set (cc0)
- (match_operator 2 "VOIDmode_compare_op"
- [(match_operand:XF 0 "register_operand" "f")
- (float_extend:XF
- (match_operand:SF 1 "nonimmediate_operand" "fm"))]))
- (clobber (match_scratch:HI 3 "=a"))]
+ [(set (match_operand:HI 0 "register_operand" "=a")
+ (unspec:HI
+ [(compare:CCFP
+ (match_operand:DF 1 "register_operand" "f")
+ (match_operand:DF 2 "nonimmediate_operand" "fm"))] 9))]
"TARGET_80387"
- "* return output_float_compare (insn, operands);"
- [(set_attr "type" "fcompare")])
-
-(define_insn ""
- [(set (cc0)
- (match_operator 2 "VOIDmode_compare_op"
- [(float_extend:XF
- (match_operand:SF 0 "nonimmediate_operand" "fm"))
- (match_operand:XF 1 "register_operand" "f")]))
- (clobber (match_scratch:HI 3 "=a"))]
+ "* return output_fp_compare (insn, operands, 2, 0);"
+ [(set_attr "type" "multi")])
+
+(define_insn "*cmpfp_2_xf"
+ [(set (reg:CCFP 18)
+ (compare:CCFP
+ (match_operand:XF 0 "register_operand" "f")
+ (match_operand:XF 1 "register_operand" "f")))]
"TARGET_80387"
- "* return output_float_compare (insn, operands);"
- [(set_attr "type" "fcompare")])
+ "* return output_fp_compare (insn, operands, 0, 0);"
+ [(set_attr "type" "fcmp")])
(define_insn ""
- [(set (cc0)
- (compare:CCFPEQ (match_operand:XF 0 "register_operand" "f")
- (match_operand:XF 1 "register_operand" "f")))
- (clobber (match_scratch:HI 2 "=a"))]
+ [(set (match_operand:HI 0 "register_operand" "=a")
+ (unspec:HI
+ [(compare:CCFP
+ (match_operand:XF 1 "register_operand" "f")
+ (match_operand:XF 2 "register_operand" "f"))] 9))]
"TARGET_80387"
- "* return output_float_compare (insn, operands);"
- [(set_attr "type" "fcompare")])
+ "* return output_fp_compare (insn, operands, 2, 0);"
+ [(set_attr "type" "multi")])
+
+(define_insn "*cmpfp_2u"
+ [(set (reg:CCFPU 18)
+ (compare:CCFPU
+ (match_operand 0 "register_operand" "f")
+ (match_operand 1 "register_operand" "f")))]
+ "TARGET_80387
+ && FLOAT_MODE_P (GET_MODE (operands[0]))
+ && GET_MODE (operands[0]) == GET_MODE (operands[1])"
+ "* return output_fp_compare (insn, operands, 0, 1);"
+ [(set_attr "type" "fcmp")])
(define_insn ""
- [(set (cc0)
- (match_operator 2 "VOIDmode_compare_op"
- [(match_operand:DF 0 "nonimmediate_operand" "f,fm")
- (match_operand:DF 1 "nonimmediate_operand" "fm,f")]))
- (clobber (match_scratch:HI 3 "=a,a"))]
+ [(set (match_operand:HI 0 "register_operand" "=a")
+ (unspec:HI
+ [(compare:CCFPU
+ (match_operand 1 "register_operand" "f")
+ (match_operand 2 "register_operand" "f"))] 9))]
"TARGET_80387
- && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)"
- "* return output_float_compare (insn, operands);"
- [(set_attr "type" "fcompare")])
+ && FLOAT_MODE_P (GET_MODE (operands[1]))
+ && GET_MODE (operands[1]) == GET_MODE (operands[2])"
+ "* return output_fp_compare (insn, operands, 2, 1);"
+ [(set_attr "type" "multi")])
-(define_insn ""
- [(set (cc0)
- (match_operator 2 "VOIDmode_compare_op"
- [(match_operand:DF 0 "register_operand" "f")
- (float_extend:DF
- (match_operand:SF 1 "nonimmediate_operand" "fm"))]))
- (clobber (match_scratch:HI 3 "=a"))]
- "TARGET_80387"
- "* return output_float_compare (insn, operands);"
- [(set_attr "type" "fcompare")])
+;; Patterns to match the SImode-in-memory ficom instructions.
+;;
+;; %%% Play games with accepting gp registers, as otherwise we have to
+;; force them to memory during rtl generation, which is no good. We
+;; can get rid of this once we teach reload to do memory input reloads
+;; via pushes.
+
+(define_insn ""
+ [(set (reg:CCFP 18)
+ (compare:CCFP
+ (match_operand 0 "register_operand" "f,f")
+ (float (match_operand:SI 1 "nonimmediate_operand" "m,?r"))))]
+ "0 && TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[0]))
+ && GET_MODE (XEXP (SET_SRC (PATTERN (insn)), 1)) == GET_MODE (operands[0])"
+ "#")
-(define_insn ""
- [(set (cc0)
- (match_operator 2 "VOIDmode_compare_op"
- [(float_extend:DF
- (match_operand:SF 0 "nonimmediate_operand" "fm"))
- (match_operand:DF 1 "register_operand" "f")]))
- (clobber (match_scratch:HI 3 "=a"))]
- "TARGET_80387"
- "* return output_float_compare (insn, operands);"
- [(set_attr "type" "fcompare")])
+;; Split the not-really-implemented gp register case into a
+;; push-op-pop sequence.
+;;
+;; %%% This is most efficient, but am I gonna get in trouble
+;; for separating cc0_setter and cc0_user?
-(define_insn ""
- [(set (cc0)
- (match_operator 2 "VOIDmode_compare_op"
- [(float_extend:DF
- (match_operand:SF 0 "register_operand" "f"))
- (match_operand:DF 1 "nonimmediate_operand" "fm")]))
- (clobber (match_scratch:HI 3 "=a"))]
+(define_split
+ [(set (reg:CCFP 18)
+ (compare:CCFP
+ (match_operand:SF 0 "register_operand" "")
+ (float (match_operand:SI 1 "register_operand" ""))))]
+ "0 && TARGET_80387 && reload_completed"
+ [(set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 1))
+ (set (reg:CCFP 18) (compare:CCFP (match_dup 0) (match_dup 2)))
+ (parallel [(set (match_dup 1) (mem:SI (reg:SI 7)))
+ (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4)))])]
+ "operands[2] = gen_rtx_MEM (Pmode, stack_pointer_rtx);
+ operands[2] = gen_rtx_FLOAT (GET_MODE (operands[0]), operands[2]);")
+
+;; FP compares, step 2
+;; Move the fpsw to ax.
+
+(define_insn "x86_fnstsw_1"
+ [(set (match_operand:HI 0 "register_operand" "=a")
+ (unspec:HI [(reg 18)] 9))]
"TARGET_80387"
- "* return output_float_compare (insn, operands);"
- [(set_attr "type" "fcompare")])
+ "fnstsw\\t%0"
+ [(set_attr "length" "2")
+ (set_attr "ppro_uops" "few")])
+
+;; FP compares, step 3
+;; Get ax into flags, general case.
+
+(define_insn "x86_sahf_1"
+ [(set (reg:CC 17)
+ (unspec:CC [(match_operand:HI 0 "register_operand" "a")] 10))]
+ ""
+ "sahf"
+ [(set_attr "length" "1")
+ (set_attr "ppro_uops" "one")])
+
+;; Pentium Pro can do steps 1 through 3 in one go.
+
+(define_insn "*cmpfp_i"
+ [(set (reg:CCFP 17)
+ (compare:CCFP (match_operand 0 "register_operand" "f")
+ (match_operand 1 "register_operand" "f")))]
+ "TARGET_80387 && TARGET_CMOVE
+ && FLOAT_MODE_P (GET_MODE (operands[0]))
+ && GET_MODE (operands[0]) == GET_MODE (operands[0])"
+ "* return output_fp_compare (insn, operands, 1, 0);"
+ [(set_attr "type" "fcmp")])
+
+(define_insn "*cmpfp_iu"
+ [(set (reg:CCFPU 17)
+ (compare:CCFPU (match_operand 0 "register_operand" "f")
+ (match_operand 1 "register_operand" "f")))]
+ "TARGET_80387 && TARGET_CMOVE
+ && FLOAT_MODE_P (GET_MODE (operands[0]))
+ && GET_MODE (operands[0]) == GET_MODE (operands[1])"
+ "* return output_fp_compare (insn, operands, 1, 1);"
+ [(set_attr "type" "fcmp")])
+\f
+;; Move instructions.
-(define_insn ""
- [(set (cc0)
- (compare:CCFPEQ (match_operand:DF 0 "register_operand" "f")
- (match_operand:DF 1 "register_operand" "f")))
- (clobber (match_scratch:HI 2 "=a"))]
- "TARGET_80387"
- "* return output_float_compare (insn, operands);"
- [(set_attr "type" "fcompare")])
+;; General case of fullword move.
-;; These two insns will never be generated by combine due to the mode of
-;; the COMPARE.
-;(define_insn ""
-; [(set (cc0)
-; (compare:CCFPEQ (match_operand:DF 0 "register_operand" "f")
-; (float_extend:DF
-; (match_operand:SF 1 "register_operand" "f"))))
-; (clobber (match_scratch:HI 2 "=a"))]
-; "TARGET_80387"
-; "* return output_float_compare (insn, operands);")
-;
-;(define_insn ""
-; [(set (cc0)
-; (compare:CCFPEQ (float_extend:DF
-; (match_operand:SF 0 "register_operand" "f"))
-; (match_operand:DF 1 "register_operand" "f")))
-; (clobber (match_scratch:HI 2 "=a"))]
-; "TARGET_80387"
-; "* return output_float_compare (insn, operands);")
-
-(define_insn "*cmpsf_cc_1"
- [(set (cc0)
- (match_operator 2 "VOIDmode_compare_op"
- [(match_operand:SF 0 "nonimmediate_operand" "f,fm")
- (match_operand:SF 1 "nonimmediate_operand" "fm,f")]))
- (clobber (match_scratch:HI 3 "=a,a"))]
- "TARGET_80387
- && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)"
- "* return output_float_compare (insn, operands);"
- [(set_attr "type" "fcompare")])
+(define_expand "movsi"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "")
+ (match_operand:SI 1 "general_operand" ""))]
+ ""
+ "ix86_expand_move (SImode, operands); DONE;")
-(define_insn ""
- [(set (cc0)
- (compare:CCFPEQ (match_operand:SF 0 "register_operand" "f")
- (match_operand:SF 1 "register_operand" "f")))
- (clobber (match_scratch:HI 2 "=a"))]
- "TARGET_80387"
- "* return output_float_compare (insn, operands);"
- [(set_attr "type" "fcompare")])
+;; Push/pop instructions. They are separate since autoinc/dec is not a
+;; general_operand.
+;;
+;; %%% We don't use a post-inc memory reference because x86 is not a
+;; general AUTO_INC_DEC host, which impacts how it is treated in flow.
+;; Changing this impacts compiler performance on other non-AUTO_INC_DEC
+;; targets without our curiosities, and it is just as easy to represent
+;; this differently.
-(define_expand "cmpxf"
- [(set (cc0)
- (compare (match_operand:XF 0 "register_operand" "")
- (match_operand:XF 1 "register_operand" "")))]
- "TARGET_80387"
- "
-{
- i386_compare_gen = gen_cmpxf_cc;
- i386_compare_gen_eq = gen_cmpxf_ccfpeq;
- i386_compare_op0 = operands[0];
- i386_compare_op1 = operands[1];
- DONE;
-}")
+(define_insn "pushsi2"
+ [(set (match_operand:SI 0 "push_operand" "=<")
+ (match_operand:SI 1 "general_operand" "ri*m"))]
+ ""
+ "push{l}\\t%1"
+ [(set_attr "type" "push")])
-(define_expand "cmpdf"
- [(set (cc0)
- (compare (match_operand:DF 0 "register_operand" "")
- (match_operand:DF 1 "general_operand" "")))]
- "TARGET_80387"
- "
-{
- i386_compare_gen = gen_cmpdf_cc;
- i386_compare_gen_eq = gen_cmpdf_ccfpeq;
- i386_compare_op0 = operands[0];
- i386_compare_op1 = (immediate_operand (operands[1], DFmode))
- ? copy_to_mode_reg (DFmode, operands[1]) : operands[1];
- DONE;
-}")
+(define_insn "popsi1"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=r*m")
+ (mem:SI (reg:SI 7)))
+ (set (reg:SI 7)
+ (plus:SI (reg:SI 7) (const_int 4)))]
+ ""
+ "pop{l}\\t%0"
+ [(set_attr "type" "pop")])
-(define_expand "cmpsf"
- [(set (cc0)
- (compare (match_operand:SF 0 "register_operand" "")
- (match_operand:SF 1 "general_operand" "")))]
- "TARGET_80387"
- "
+(define_insn "*movsi_1"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=r,m")
+ (match_operand:SI 1 "general_operand" "rim,ri"))
+ (clobber (reg:CC 17))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "*
{
- i386_compare_gen = gen_cmpsf_cc;
- i386_compare_gen_eq = gen_cmpsf_ccfpeq;
- i386_compare_op0 = operands[0];
- i386_compare_op1 = (immediate_operand (operands[1], SFmode))
- ? copy_to_mode_reg (SFmode, operands[1]) : operands[1];
- DONE;
-}")
-
-(define_expand "cmpxf_cc"
- [(parallel [(set (cc0)
- (compare (match_operand:XF 0 "register_operand" "")
- (match_operand:XF 1 "register_operand" "")))
- (clobber (match_scratch:HI 2 ""))])]
- "TARGET_80387"
- "")
-
-(define_expand "cmpxf_ccfpeq"
- [(parallel [(set (cc0)
- (compare:CCFPEQ (match_operand:XF 0 "register_operand" "")
- (match_operand:XF 1 "register_operand" "")))
- (clobber (match_scratch:HI 2 ""))])]
- "TARGET_80387"
- "")
-
-(define_expand "cmpdf_cc"
- [(parallel [(set (cc0)
- (compare (match_operand:DF 0 "register_operand" "")
- (match_operand:DF 1 "register_operand" "")))
- (clobber (match_scratch:HI 2 ""))])]
- "TARGET_80387"
- "")
-
-(define_expand "cmpdf_ccfpeq"
- [(parallel [(set (cc0)
- (compare:CCFPEQ (match_operand:DF 0 "register_operand" "")
- (match_operand:DF 1 "register_operand" "")))
- (clobber (match_scratch:HI 2 ""))])]
- "TARGET_80387"
- "
-{
- if (! register_operand (operands[1], DFmode))
- operands[1] = copy_to_mode_reg (DFmode, operands[1]);
-}")
-
-(define_expand "cmpsf_cc"
- [(parallel [(set (cc0)
- (compare (match_operand:SF 0 "register_operand" "")
- (match_operand:SF 1 "register_operand" "")))
- (clobber (match_scratch:HI 2 ""))])]
- "TARGET_80387"
- "")
-
-(define_expand "cmpsf_ccfpeq"
- [(parallel [(set (cc0)
- (compare:CCFPEQ (match_operand:SF 0 "register_operand" "")
- (match_operand:SF 1 "register_operand" "")))
- (clobber (match_scratch:HI 2 ""))])]
- "TARGET_80387"
- "
-{
- if (! register_operand (operands[1], SFmode))
- operands[1] = copy_to_mode_reg (SFmode, operands[1]);
-}")
-\f
-;; logical compare
-
-(define_insn ""
- [(set (cc0)
- (and:SI (match_operand:SI 0 "general_operand" "%ro")
- (match_operand:SI 1 "nonmemory_operand" "ri")))]
- ""
- "*
-{
- /* For small integers, we may actually use testb. */
- if (GET_CODE (operands[1]) == CONST_INT
- && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))
- && (! REG_P (operands[0]) || QI_REG_P (operands[0]))
- /* A Pentium test is pairable only with eax. Not with ah or al. */
- && (! REG_P (operands[0]) || REGNO (operands[0]) || !TARGET_PENTIUM
- || optimize_size))
+ switch (get_attr_type (insn))
{
- /* We may set the sign bit spuriously. */
-
- if ((INTVAL (operands[1]) & ~0xff) == 0)
- {
- cc_status.flags |= CC_NOT_NEGATIVE;
- return AS2 (test%B0,%1,%b0);
- }
-
- if ((INTVAL (operands[1]) & ~0xff00) == 0)
- {
- cc_status.flags |= CC_NOT_NEGATIVE;
- operands[1] = GEN_INT (INTVAL (operands[1]) >> 8);
-
- if (QI_REG_P (operands[0]))
- return AS2 (test%B0,%1,%h0);
- else
- {
- operands[0] = adj_offsettable_operand (operands[0], 1);
- return AS2 (test%B0,%1,%b0);
- }
- }
-
- if (GET_CODE (operands[0]) == MEM
- && (INTVAL (operands[1]) & ~0xff0000) == 0)
- {
- cc_status.flags |= CC_NOT_NEGATIVE;
- operands[1] = GEN_INT (INTVAL (operands[1]) >> 16);
- operands[0] = adj_offsettable_operand (operands[0], 2);
- return AS2 (test%B0,%1,%b0);
- }
-
- if (GET_CODE (operands[0]) == MEM
- && (INTVAL (operands[1]) & ~0xff000000) == 0)
- {
- operands[1] = GEN_INT ((INTVAL (operands[1]) >> 24) & 0xff);
- operands[0] = adj_offsettable_operand (operands[0], 3);
- return AS2 (test%B0,%1,%b0);
- }
+ case TYPE_ALU1:
+ if (operands[1] != const0_rtx)
+ abort();
+ return \"xor{l}\\t{%0, %0|%0, %0}\";
+ case TYPE_LEA:
+ return \"lea{l}\\t{%1, %0|%0, %1}\";
+ default:
+ if (flag_pic && SYMBOLIC_CONST (operands[1]))
+ abort();
+ return \"mov{l}\\t{%1, %0|%0, %1}\";
}
-
- if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
- return AS2 (test%L0,%1,%0);
-
- return AS2 (test%L1,%0,%1);
}"
- [(set_attr "type" "compare")])
-
-(define_insn ""
- [(set (cc0)
- (and:HI (match_operand:HI 0 "general_operand" "%ro")
- (match_operand:HI 1 "nonmemory_operand" "ri")))]
- ""
+ [(set (attr "type")
+ (cond [(and (match_operand:SI 1 "const0_operand" "")
+ (and (match_operand:SI 0 "register_operand" "")
+ (ne (symbol_ref "TARGET_USE_MOV0") (const_int 0))))
+ (const_string "alu1")
+ (and (ne (symbol_ref "flag_pic") (const_int 0))
+ (match_operand:SI 1 "symbolic_operand" ""))
+ (const_string "lea")
+ ]
+ (const_string "imov")))])
+
+(define_insn "*movsi_2"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=r,m")
+ (match_operand:SI 1 "general_operand" "rinm,rin"))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
"*
{
- if (GET_CODE (operands[1]) == CONST_INT
- && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))
- && (! REG_P (operands[0]) || QI_REG_P (operands[0])))
+ switch (get_attr_type (insn))
{
- if ((INTVAL (operands[1]) & 0xff00) == 0)
- {
- /* ??? This might not be necessary. */
- if (INTVAL (operands[1]) & 0xffff0000)
- operands[1] = GEN_INT (INTVAL (operands[1]) & 0xff);
-
- /* We may set the sign bit spuriously. */
- cc_status.flags |= CC_NOT_NEGATIVE;
- return AS2 (test%B0,%1,%b0);
- }
-
- if ((INTVAL (operands[1]) & 0xff) == 0)
- {
- operands[1] = GEN_INT ((INTVAL (operands[1]) >> 8) & 0xff);
-
- if (QI_REG_P (operands[0]))
- return AS2 (test%B0,%1,%h0);
- else
- {
- operands[0] = adj_offsettable_operand (operands[0], 1);
- return AS2 (test%B0,%1,%b0);
- }
- }
+ case TYPE_LEA:
+ return \"lea{l}\\t{%1, %0|%0, %1}\";
+ default:
+ if (flag_pic && SYMBOLIC_CONST (operands[1]))
+ abort();
+ return \"mov{l}\\t{%1, %0|%0, %1}\";
}
-
- /* use 32-bit test instruction if there are no sign issues */
- if (GET_CODE (operands[1]) == CONST_INT
- && !(INTVAL (operands[1]) & ~0x7fff)
- && i386_aligned_p (operands[0]))
- return AS2 (test%L0,%1,%k0);
-
- if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
- return AS2 (test%W0,%1,%0);
-
- return AS2 (test%W1,%0,%1);
-}"
- [(set_attr "type" "compare")])
-
-(define_insn ""
- [(set (cc0)
- (and:QI (match_operand:QI 0 "nonimmediate_operand" "%qm")
- (match_operand:QI 1 "nonmemory_operand" "qi")))]
- ""
- "*
-{
- if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
- return AS2 (test%B0,%1,%0);
-
- return AS2 (test%B1,%0,%1);
}"
- [(set_attr "type" "compare")])
-\f
-;; move instructions.
-;; There is one for each machine mode,
-;; and each is preceded by a corresponding push-insn pattern
-;; (since pushes are not general_operands on the 386).
-
-(define_insn ""
- [(set (match_operand:SI 0 "push_operand" "=<")
- (match_operand:SI 1 "nonmemory_operand" "rn"))]
- "flag_pic"
- "* return AS1 (push%L0,%1);"
- [(set_attr "memory" "store")])
-
-(define_insn ""
- [(set (match_operand:SI 0 "push_operand" "=<")
- (match_operand:SI 1 "nonmemory_operand" "ri"))]
- "!flag_pic"
- "* return AS1 (push%L0,%1);"
- [(set_attr "memory" "store")])
-
-;; On a 386, it is faster to push MEM directly.
-
-(define_insn ""
- [(set (match_operand:SI 0 "push_operand" "=<")
- (match_operand:SI 1 "memory_operand" "m"))]
- "TARGET_PUSH_MEMORY"
- "* return AS1 (push%L0,%1);"
- [(set_attr "type" "memory")
- (set_attr "memory" "load")])
-
-;; General case of fullword move.
-
-;; If generating PIC code and operands[1] is a symbolic CONST, emit a
-;; move to get the address of the symbolic object from the GOT.
-
-(define_expand "movsi"
- [(set (match_operand:SI 0 "general_operand" "")
- (match_operand:SI 1 "general_operand" ""))]
+ [(set (attr "type")
+ (cond [(and (ne (symbol_ref "flag_pic") (const_int 0))
+ (match_operand:SI 1 "symbolic_operand" ""))
+ (const_string "lea")
+ ]
+ (const_string "imov")))])
+
+(define_insn "*swapsi"
+ [(set (match_operand:SI 0 "register_operand" "+r")
+ (match_operand:SI 1 "register_operand" "+r"))
+ (set (match_dup 1)
+ (match_dup 0))]
""
- "
-{
- extern int flag_pic;
-
- if (flag_pic && SYMBOLIC_CONST (operands[1]))
- emit_pic_move (operands, SImode);
-
- /* Don't generate memory->memory moves, go through a register */
- else if (TARGET_MOVE
- && no_new_pseudos == 0
- && GET_CODE (operands[0]) == MEM
- && GET_CODE (operands[1]) == MEM)
- {
- operands[1] = force_reg (SImode, operands[1]);
- }
-}")
-
-;; On i486, incl reg is faster than movl $1,reg.
-
-(define_insn ""
- [(set (match_operand:SI 0 "general_operand" "=g,r,r")
- (match_operand:SI 1 "general_operand" "rn,i,m"))]
- "((!TARGET_MOVE || GET_CODE (operands[0]) != MEM)
- || (GET_CODE (operands[1]) != MEM))
- && flag_pic"
- "*
-{
- rtx link;
-
- /* K6: mov reg,0 is slightly faster than xor reg,reg but is 3 bytes
- longer. */
- if ((ix86_cpu != PROCESSOR_K6 || optimize_size)
- && operands[1] == const0_rtx && REG_P (operands[0]))
- return AS2 (xor%L0,%0,%0);
-
- if (operands[1] == const1_rtx
- /* PPRO and K6 prefer mov to inc to reduce dependencies. */
- && (optimize_size || (int)ix86_cpu < (int)PROCESSOR_PENTIUMPRO)
- && (link = find_reg_note (insn, REG_WAS_0, 0))
- /* Make sure the insn that stored the 0 is still present. */
- && ! INSN_DELETED_P (XEXP (link, 0))
- && GET_CODE (XEXP (link, 0)) != NOTE
- /* Make sure cross jumping didn't happen here. */
- && no_labels_between_p (XEXP (link, 0), insn)
- /* Make sure the reg hasn't been clobbered. */
- && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
- /* Fastest way to change a 0 to a 1. */
- return AS1 (inc%L0,%0);
-
- if (SYMBOLIC_CONST (operands[1]))
- return AS2 (lea%L0,%a1,%0);
-
- return AS2 (mov%L0,%1,%0);
-}"
- [(set_attr "type" "integer,integer,memory")
- (set_attr "memory" "*,*,load")])
+ "xchg{l}\\t%1, %0"
+ [(set_attr "type" "imov")
+ (set_attr "pent_pair" "np")
+ (set_attr "ppro_uops" "few")])
-(define_insn ""
- [(set (match_operand:SI 0 "general_operand" "=g,r")
- (match_operand:SI 1 "general_operand" "ri,m"))]
- "((!TARGET_MOVE || GET_CODE (operands[0]) != MEM)
- || (GET_CODE (operands[1]) != MEM))
- && !flag_pic"
- "*
-{
- rtx link;
-
- /* Use of xor was disabled for AMD K6 as recommended by the Optimization
- Manual. My test shows, that this generally hurts the performance, because
- mov is longer and takes longer to decode and decoding is the main
- bottleneck of K6 when executing GCC code. */
-
- if (operands[1] == const0_rtx && REG_P (operands[0]))
- return AS2 (xor%L0,%0,%0);
-
- if (operands[1] == const1_rtx
- /* PPRO and K6 prefer mov to inc to reduce dependencies. */
- && (optimize_size || (int)ix86_cpu < (int)PROCESSOR_PENTIUMPRO)
- && (link = find_reg_note (insn, REG_WAS_0, 0))
- /* Make sure the insn that stored the 0 is still present. */
- && ! INSN_DELETED_P (XEXP (link, 0))
- && GET_CODE (XEXP (link, 0)) != NOTE
- /* Make sure cross jumping didn't happen here. */
- && no_labels_between_p (XEXP (link, 0), insn)
- /* Make sure the reg hasn't been clobbered. */
- && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
- /* Fastest way to change a 0 to a 1. */
- return AS1 (inc%L0,%0);
-
- return AS2 (mov%L0,%1,%0);
-}"
- [(set_attr "type" "integer,memory")
- (set_attr "memory" "*,load")])
-
-(define_insn ""
- [(set (match_operand:HI 0 "push_operand" "=<")
- (match_operand:HI 1 "nonmemory_operand" "ri"))]
+(define_expand "movhi"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "")
+ (match_operand:HI 1 "general_operand" ""))]
""
- "* return AS1 (push%W0,%1);"
- [(set_attr "type" "memory")
- (set_attr "memory" "store")])
+ "ix86_expand_move (HImode, operands); DONE;")
-(define_insn ""
- [(set (match_operand:HI 0 "push_operand" "=<")
- (match_operand:HI 1 "memory_operand" "m"))]
- "TARGET_PUSH_MEMORY"
- "* return AS1 (push%W0,%1);"
- [(set_attr "type" "memory")
- (set_attr "memory" "load")])
-
-;; On i486, an incl and movl are both faster than incw and movw.
-
-(define_expand "movhi"
- [(set (match_operand:HI 0 "general_operand" "")
- (match_operand:HI 1 "general_operand" ""))]
+(define_insn "pushhi2"
+ [(set (match_operand:HI 0 "push_operand" "=<,<")
+ (match_operand:HI 1 "general_operand" "n,r*m"))]
""
- "
+ "@
+ push{w}\\t{|WORD PTR }%1
+ push{w}\\t%1"
+ [(set_attr "type" "push")])
+
+(define_insn "pophi1"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=r*m")
+ (mem:HI (reg:SI 7)))
+ (set (reg:SI 7)
+ (plus:SI (reg:SI 7) (const_int 2)))]
+ ""
+ "pop{w}\\t%0"
+ [(set_attr "type" "pop")])
+
+(define_insn "*movhi_1"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,m")
+ (match_operand:HI 1 "general_operand" "rn,rm,rn"))
+ (clobber (reg:CC 17))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "*
{
- /* Don't generate memory->memory moves, go through a register */
- if (TARGET_MOVE
- && no_new_pseudos == 0
- && GET_CODE (operands[0]) == MEM
- && GET_CODE (operands[1]) == MEM)
+ switch (get_attr_type (insn))
{
- operands[1] = force_reg (HImode, operands[1]);
+ case TYPE_ALU1:
+ /* Clear the whole register -- smaller, faster, better. */
+ if (operands[1] != const0_rtx)
+ abort();
+ return \"xor{l}\\t%k0, %k0\";
+ case TYPE_IMOVX:
+ return \"movz{wl|x}\\t{%1, %k0|%k0, %1}\";
+ default:
+ if (get_attr_length_prefix (insn) == 0)
+ return \"mov{l}\\t{%k1, %k0|%k0, %k1}\";
+ else
+ return \"mov{w}\\t{%1, %0|%0, %1}\";
}
-}")
-
-(define_insn ""
- [(set (match_operand:HI 0 "general_operand" "=g,r")
- (match_operand:HI 1 "general_operand" "ri,m"))]
- "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM) || (GET_CODE (operands[1]) != MEM)"
+}"
+ [(set (attr "type")
+ (cond [(and (match_operand:HI 1 "const0_operand" "")
+ (and (match_operand:HI 0 "register_operand" "")
+ (ne (symbol_ref "TARGET_USE_MOV0") (const_int 0))))
+ (const_string "alu1")
+ (and (ne (symbol_ref "TARGET_MOVX")
+ (const_int 0))
+ (eq_attr "alternative" "1"))
+ (const_string "imovx")
+ ]
+ (const_string "imov")))
+ (set (attr "length_prefix")
+ (cond [(eq_attr "type" "imovx")
+ (const_string "0")
+ (and (eq_attr "alternative" "0")
+ (eq (symbol_ref "TARGET_PARTIAL_REG_STALL")
+ (const_int 0)))
+ (const_string "0")
+ ]
+ (const_string "1")))
+ ; There's no place to override just the immediate length
+ (set (attr "length")
+ (cond [(and (eq_attr "type" "imov")
+ (and (eq_attr "length_prefix" "0")
+ (match_operand:HI 1 "immediate_operand" "")))
+ (const_string "5")
+ ]
+ (const_string "*")))])
+
+(define_insn "*movhi_2"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,m")
+ (match_operand:HI 1 "general_operand" "rn,rm,rn"))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
"*
{
- rtx link;
- if (REG_P (operands[0]) && operands[1] == const0_rtx)
- return AS2 (xor%L0,%k0,%k0);
-
- if (REG_P (operands[0]) && operands[1] == const1_rtx
- /* PPRO and K6 prefer mov to inc to reduce dependencies. */
- && (optimize_size || (int)ix86_cpu < (int)PROCESSOR_PENTIUMPRO)
- && (link = find_reg_note (insn, REG_WAS_0, 0))
- /* Make sure the insn that stored the 0 is still present. */
- && ! INSN_DELETED_P (XEXP (link, 0))
- && GET_CODE (XEXP (link, 0)) != NOTE
- /* Make sure cross jumping didn't happen here. */
- && no_labels_between_p (XEXP (link, 0), insn)
- /* Make sure the reg hasn't been clobbered. */
- && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
- /* Fastest way to change a 0 to a 1. */
- return AS1 (inc%L0,%k0);
-
- if (REG_P (operands[0]))
+ switch (get_attr_type (insn))
{
- if (i386_aligned_p (operands[1]))
- {
- operands[1] = i386_sext16_if_const (operands[1]);
- return AS2 (mov%L0,%k1,%k0);
- }
- if (! TARGET_ZERO_EXTEND_WITH_AND)
- {
- /* movzwl is faster than movw on the Pentium Pro,
- * although not as fast as an aligned movl. */
-#ifdef INTEL_SYNTAX
- return AS2 (movzx,%1,%k0);
-#else
- return AS2 (movz%W0%L0,%1,%k0);
-#endif
- }
+ case TYPE_IMOVX:
+ /* movzwl is faster than movw on p2 due to partial word stalls,
+ though not as fast as an aligned movl. */
+ return \"movz{wl|x}\\t{%1, %k0|%k0, %1}\";
+ default:
+ if (get_attr_length_prefix (insn) == 0)
+ return \"mov{l}\\t{%k1, %k0|%k0, %k1}\";
+ else
+ return \"mov{w}\\t{%1, %0|%0, %1}\";
}
-
- return AS2 (mov%W0,%1,%0);
}"
- [(set_attr "type" "integer,memory")
- (set_attr "memory" "*,load")])
+ [(set (attr "type")
+ (cond [(eq_attr "alternative" "0")
+ (const_string "imov")
+ (and (ne (symbol_ref "TARGET_MOVX")
+ (const_int 0))
+ (eq_attr "alternative" "1"))
+ (const_string "imovx")
+ ]
+ (const_string "imov")))
+ (set (attr "length_prefix")
+ (cond [(eq_attr "type" "imovx")
+ (const_string "0")
+ (and (eq_attr "alternative" "0")
+ (eq (symbol_ref "TARGET_PARTIAL_REG_STALL")
+ (const_int 0)))
+ (const_string "0")
+ ]
+ (const_string "1")))
+ ; There's no place to override just the immediate length
+ (set (attr "length")
+ (cond [(and (eq_attr "type" "imov")
+ (and (eq_attr "length_prefix" "0")
+ (match_operand:HI 1 "immediate_operand" "")))
+ (const_string "5")
+ ]
+ (const_string "*")))])
+
+(define_insn "*swaphi_1"
+ [(set (match_operand:HI 0 "register_operand" "+r")
+ (match_operand:HI 1 "register_operand" "+r"))
+ (set (match_dup 1)
+ (match_dup 0))]
+ "TARGET_PARTIAL_REG_STALL"
+ "xchg{w}\\t%1, %0"
+ [(set_attr "type" "imov")
+ (set_attr "pent_pair" "np")
+ (set_attr "ppro_uops" "few")])
+
+(define_insn "*swaphi_2"
+ [(set (match_operand:HI 0 "register_operand" "+r")
+ (match_operand:HI 1 "register_operand" "+r"))
+ (set (match_dup 1)
+ (match_dup 0))]
+ "! TARGET_PARTIAL_REG_STALL"
+ "xchg{l}\\t%k1, %k0"
+ [(set_attr "type" "imov")
+ (set_attr "length_prefix" "0")
+ (set_attr "pent_pair" "np")
+ (set_attr "ppro_uops" "few")])
(define_expand "movstricthi"
- [(set (strict_low_part (match_operand:HI 0 "general_operand" ""))
+ [(set (strict_low_part (match_operand:HI 0 "nonimmediate_operand" ""))
(match_operand:HI 1 "general_operand" ""))]
- ""
+ "! TARGET_PARTIAL_REG_STALL"
"
{
/* Don't generate memory->memory moves, go through a register */
- if (TARGET_MOVE
- && no_new_pseudos == 0
- && GET_CODE (operands[0]) == MEM
- && GET_CODE (operands[1]) == MEM)
- {
- operands[1] = force_reg (HImode, operands[1]);
- }
+ if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
+ operands[1] = force_reg (HImode, operands[1]);
}")
-(define_insn ""
+(define_insn "*movstricthi_1"
[(set (strict_low_part (match_operand:HI 0 "general_operand" "+g,r"))
- (match_operand:HI 1 "general_operand" "ri,m"))]
- "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM) || (GET_CODE (operands[1]) != MEM)"
- "*
-{
- rtx link;
-
- /* Use of xor was disabled for AMD K6 as recommended by the Optimization
- Manual. My test shows, that this generally hurts the performance, because
- mov is longer and takes longer to decode and decoding is the main
- bottleneck of K6 when executing GCC code. */
-
- if (operands[1] == const0_rtx && REG_P (operands[0]))
- return AS2 (xor%W0,%0,%0);
-
- if (operands[1] == const1_rtx
- /* PPRO and K6 prefer mov to inc to reduce dependencies. */
- && (optimize_size || (int)ix86_cpu < (int)PROCESSOR_PENTIUMPRO)
- && (link = find_reg_note (insn, REG_WAS_0, 0))
- /* Make sure the insn that stored the 0 is still present. */
- && ! INSN_DELETED_P (XEXP (link, 0))
- && GET_CODE (XEXP (link, 0)) != NOTE
- /* Make sure cross jumping didn't happen here. */
- && no_labels_between_p (XEXP (link, 0), insn)
- /* Make sure the reg hasn't been clobbered. */
- && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
- /* Fastest way to change a 0 to a 1. */
- return AS1 (inc%W0,%0);
-
- return AS2 (mov%W0,%1,%0);
-}"
- [(set_attr "type" "integer,memory")])
-
-;; emit_push_insn when it calls move_by_pieces
-;; requires an insn to "push a byte".
-;; But actually we use pushw, which has the effect of rounding
-;; the amount pushed up to a halfword.
-(define_insn ""
- [(set (match_operand:QI 0 "push_operand" "=<")
- (match_operand:QI 1 "const_int_operand" "n"))]
- ""
- "* return AS1(push%W0,%1);")
-
-(define_insn ""
- [(set (match_operand:QI 0 "push_operand" "=<")
- (match_operand:QI 1 "register_operand" "q"))]
- ""
- "*
-{
- operands[1] = gen_rtx_REG (HImode, REGNO (operands[1]));
- return AS1 (push%W0,%1);
-}")
-
-;; On i486, incb reg is faster than movb $1,reg.
-
-;; ??? Do a recognizer for zero_extract that looks just like this, but reads
-;; or writes %ah, %bh, %ch, %dh.
+ (match_operand:HI 1 "general_operand" "rn,m"))]
+ "! TARGET_PARTIAL_REG_STALL
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)"
+ "mov{w}\\t{%1, %0|%0, %1}"
+ [(set_attr "type" "imov")])
(define_expand "movqi"
[(set (match_operand:QI 0 "general_operand" "")
(match_operand:QI 1 "general_operand" ""))]
""
- "
+ "ix86_expand_move (QImode, operands); DONE;")
+
+;; emit_push_insn when it calls move_by_pieces requires an insn to
+;; "push a byte". But actually we use pushw, which has the effect
+;; of rounding the amount pushed up to a halfword.
+
+(define_insn "pushqi2"
+ [(set (match_operand:QI 0 "push_operand" "=<,<")
+ (match_operand:QI 1 "nonmemory_operand" "n,r"))]
+ ""
+ "@
+ push{w}\\t{|word ptr }%1
+ push{w}\\t%w1"
+ [(set_attr "type" "push")
+ (set_attr "length_prefix" "1")
+ ; There's no place to override just the immediate length
+ (set (attr "length")
+ (if_then_else (eq_attr "length_prefix" "0")
+ (const_string "4")
+ (const_string "*")))])
+
+(define_insn "popqi1"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=r*m")
+ (mem:QI (reg:SI 7)))
+ (set (reg:SI 7)
+ (plus:SI (reg:SI 7) (const_int 2)))]
+ ""
+ "pop{w}\\t%0"
+ [(set_attr "type" "pop")
+ (set_attr "length_prefix" "1")])
+
+(define_insn "*movqi_1"
+ [(set (match_operand:QI 0 "general_operand" "=q,q,*r,*r,m")
+ (match_operand:QI 1 "general_operand" "qn,qm,*rn,qm,qn"))
+ (clobber (reg:CC 17))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "*
{
- /* Don't generate memory->memory moves, go through a register */
- if (TARGET_MOVE
- && no_new_pseudos == 0
- && GET_CODE (operands[0]) == MEM
- && GET_CODE (operands[1]) == MEM)
+ switch (get_attr_type (insn))
{
- operands[1] = force_reg (QImode, operands[1]);
+ case TYPE_ALU1:
+ /* Clear the whole register -- smaller, faster, better. */
+ if (operands[1] != const0_rtx)
+ abort();
+ return \"xor{l}\\t%k0, %k0\";
+ case TYPE_IMOVX:
+ if (!QI_REG_P (operands[1]) && GET_CODE (operands[1]) != MEM)
+ abort ();
+ return \"movz{bl|x}\\t{%1, %k0|%k0, %1}\";
+ default:
+ if (which_alternative == 2)
+ return \"mov{l}\\t{%k1, %k0|%k0, %k1}\";
+ else
+ return \"mov{b}\\t{%1, %0|%0, %1}\";
}
-}")
-
-(define_insn ""
- [(set (match_operand:QI 0 "nonimmediate_operand" "=q,*r,qm")
- (match_operand:QI 1 "general_operand" "*g,*rn,qn"))]
- "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM) || (GET_CODE (operands[1]) != MEM)"
+}"
+ [(set (attr "type")
+ (cond [(and (match_operand:QI 1 "const0_operand" "")
+ (and (match_operand:QI 0 "register_operand" "")
+ (ne (symbol_ref "TARGET_USE_MOV0") (const_int 0))))
+ (const_string "alu1")
+ (eq_attr "alternative" "3")
+ (const_string "imovx")
+ (and (ne (symbol_ref "TARGET_MOVX")
+ (const_int 0))
+ (eq_attr "alternative" "1"))
+ (const_string "imovx")
+ ]
+ (const_string "imov")))
+ ; There's no place to override just the immediate length
+ (set (attr "length")
+ (cond [(and (eq_attr "type" "imov")
+ (and (eq_attr "alternative" "2")
+ (match_operand:HI 1 "immediate_operand" "")))
+ (const_string "5")
+ ]
+ (const_string "*")))])
+
+(define_insn "*movqi_2"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=q,q,*r,*r,m")
+ (match_operand:QI 1 "general_operand" "qn,qm,*rn,qm,qn"))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
"*
{
- rtx link;
-
- /* movb $0,reg8 is 2 bytes, the same as xorl reg8,reg8.
- It is at least as fast as xor on any processor except a Pentium. */
-
- if (operands[1] == const1_rtx
- && TARGET_PENTIUM
- && (link = find_reg_note (insn, REG_WAS_0, 0))
- /* Make sure the insn that stored the 0 is still present. */
- && ! INSN_DELETED_P (XEXP (link, 0))
- && GET_CODE (XEXP (link, 0)) != NOTE
- /* Make sure cross jumping didn't happen here. */
- && no_labels_between_p (XEXP (link, 0), insn)
- /* Make sure the reg hasn't been clobbered. */
- && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
+ switch (get_attr_type (insn))
{
- /* Fastest way to change a 0 to a 1.
- If inc%B0 isn't allowed, use inc%L0. */
- if (NON_QI_REG_P (operands[0]))
- return AS1 (inc%L0,%k0);
+ case TYPE_IMOVX:
+ if (!QI_REG_P (operands[1]) && GET_CODE (operands[1]) != MEM)
+ abort ();
+ return \"movz{bl|x}\\t{%1, %k0|%k0, %1}\";
+ default:
+ if (which_alternative == 2)
+ return \"mov{l}\\t{%k1, %k0|%k0, %k1}\";
else
- return AS1 (inc%B0,%0);
+ return \"mov{b}\\t{%1, %0|%0, %1}\";
}
+}"
+ [(set (attr "type")
+ (cond [(eq_attr "alternative" "3")
+ (const_string "imovx")
+ (and (ne (symbol_ref "TARGET_MOVX")
+ (const_int 0))
+ (eq_attr "alternative" "1"))
+ (const_string "imovx")
+ ]
+ (const_string "imov")))
+ ; There's no place to override just the immediate length
+ (set (attr "length")
+ (cond [(and (eq_attr "type" "imov")
+ (and (eq_attr "alternative" "2")
+ (match_operand:HI 1 "immediate_operand" "")))
+ (const_string "5")
+ ]
+ (const_string "*")))])
+
+(define_expand "reload_outqi"
+ [(parallel [(match_operand:QI 0 "" "=m")
+ (match_operand:QI 1 "register_operand" "r")
+ (match_operand:QI 2 "register_operand" "=&q")])]
+ ""
+ "
+{
+ rtx op0, op1, op2;
+ op0 = operands[0]; op1 = operands[1]; op2 = operands[2];
- /* If mov%B0 isn't allowed for one of these regs, use mov%L0. */
- if (NON_QI_REG_P (operands[0]) || NON_QI_REG_P (operands[1]))
- return (AS2 (mov%L0,%k1,%k0));
-
- return (AS2 (mov%B0,%1,%0));
+ if (reg_overlap_mentioned_p (op2, op0))
+ abort ();
+ if (! q_regs_operand (op1, QImode))
+ {
+ emit_insn (gen_movqi (op2, op1));
+ op1 = op2;
+ }
+ emit_insn (gen_movqi (op0, op1));
+ DONE;
}")
-;; If it becomes necessary to support movstrictqi into %esi or %edi,
-;; use the insn sequence:
-;;
-;; shrdl $8,srcreg,dstreg
-;; rorl $24,dstreg
-;;
-;; If operands[1] is a constant, then an andl/orl sequence would be
-;; faster.
+(define_insn "*swapqi"
+ [(set (match_operand:QI 0 "register_operand" "+r")
+ (match_operand:QI 1 "register_operand" "+r"))
+ (set (match_dup 1)
+ (match_dup 0))]
+ ""
+ "xchg{b}\\t%1, %0"
+ [(set_attr "type" "imov")
+ (set_attr "pent_pair" "np")
+ (set_attr "ppro_uops" "few")])
(define_expand "movstrictqi"
[(set (strict_low_part (match_operand:QI 0 "general_operand" ""))
(match_operand:QI 1 "general_operand" ""))]
- ""
+ "! TARGET_PARTIAL_REG_STALL"
"
{
/* Don't generate memory->memory moves, go through a register */
- if (TARGET_MOVE
- && no_new_pseudos == 0
- && GET_CODE (operands[0]) == MEM
- && GET_CODE (operands[1]) == MEM)
- {
- operands[1] = force_reg (QImode, operands[1]);
- }
+ if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
+ operands[1] = force_reg (QImode, operands[1]);
}")
-(define_insn ""
+(define_insn "*movstrictqi_1"
[(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm,q"))
(match_operand:QI 1 "general_operand" "*qn,m"))]
- "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM) || (GET_CODE (operands[1]) != MEM)"
+ "! TARGET_PARTIAL_REG_STALL
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)"
+ "mov{b}\\t{%1, %0|%0, %1}"
+ [(set_attr "type" "imov")])
+
+(define_insn "*movsi_extv_1"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (sign_extract:SI (match_operand:SI 1 "register_operand" "q")
+ (const_int 8)
+ (const_int 8)))]
+ ""
+ "movs{bl|x}\\t{%h1, %0|%0, %h1}"
+ [(set_attr "type" "imovx")])
+
+(define_insn "*movhi_extv_1"
+ [(set (match_operand:HI 0 "register_operand" "=r")
+ (sign_extract:HI (match_operand:SI 1 "register_operand" "q")
+ (const_int 8)
+ (const_int 8)))]
+ ""
+ "movs{bl|x}\\t{%h1, %k0|%k0, %h1}"
+ [(set_attr "type" "imovx")])
+
+(define_insn "*movqi_extv_1"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=qm*r")
+ (sign_extract:QI (match_operand:SI 1 "register_operand" "q")
+ (const_int 8)
+ (const_int 8)))]
+ ""
"*
{
- rtx link;
-
- /* movb $0,reg8 is 2 bytes, the same as xorl reg8,reg8. */
-
- if (operands[1] == const1_rtx
- && TARGET_PENTIUM
- && ! NON_QI_REG_P (operands[0])
- && (link = find_reg_note (insn, REG_WAS_0, 0))
- /* Make sure the insn that stored the 0 is still present. */
- && ! INSN_DELETED_P (XEXP (link, 0))
- && GET_CODE (XEXP (link, 0)) != NOTE
- /* Make sure cross jumping didn't happen here. */
- && no_labels_between_p (XEXP (link, 0), insn)
- /* Make sure the reg hasn't been clobbered. */
- && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
- /* Fastest way to change a 0 to a 1. */
- return AS1 (inc%B0,%0);
-
- /* If mov%B0 isn't allowed for one of these regs, use mov%L0. */
- if (NON_QI_REG_P (operands[0]) || NON_QI_REG_P (operands[1]))
+ switch (get_attr_type (insn))
{
- abort ();
- return (AS2 (mov%L0,%k1,%k0));
+ case TYPE_IMOVX:
+ return \"movs{bl|x}\\t{%h1, %k0|%k0, %h1}\";
+ default:
+ return \"mov{b}\\t{%h1, %0|%0, %h1}\";
}
+}"
+ [(set (attr "type")
+ (if_then_else (and (match_operand:QI 0 "register_operand" "")
+ (ior (not (match_operand:QI 0 "q_regs_operand" ""))
+ (ne (symbol_ref "TARGET_MOVX")
+ (const_int 0))))
+ (const_string "imovx")
+ (const_string "imov")))])
+
+(define_insn "*movsi_extzv_1"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (zero_extract:SI (match_operand 1 "ext_register_operand" "q")
+ (const_int 8)
+ (const_int 8)))]
+ ""
+ "movz{bl|x}\\t{%h1, %0|%0, %h1}"
+ [(set_attr "type" "imovx")])
- return AS2 (mov%B0,%1,%0);
-}")
-
-(define_insn "movsf_push"
- [(set (match_operand:SF 0 "push_operand" "=<,<")
- (match_operand:SF 1 "general_operand" "*rfF,m"))]
- "TARGET_PUSH_MEMORY || GET_CODE (operands[1]) != MEM
- || reload_in_progress || reload_completed"
+(define_insn "*movqi_extzv_1"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=qm*r")
+ (subreg:QI (zero_extract:SI (match_operand 1 "ext_register_operand" "q")
+ (const_int 8)
+ (const_int 8)) 0))]
+ ""
"*
{
- if (STACK_REG_P (operands[1]))
+ switch (get_attr_type (insn))
{
- rtx xops[3];
-
- if (! STACK_TOP_P (operands[1]))
- abort ();
+ case TYPE_IMOVX:
+ return \"movz{bl|x}\\t{%h1, %k0|%k0, %h1}\";
+ default:
+ return \"mov{b}\\t{%h1, %0|%0, %h1}\";
+ }
+}"
+ [(set (attr "type")
+ (if_then_else (and (match_operand:QI 0 "register_operand" "")
+ (ior (not (match_operand:QI 0 "q_regs_operand" ""))
+ (ne (symbol_ref "TARGET_MOVX")
+ (const_int 0))))
+ (const_string "imovx")
+ (const_string "imov")))])
+
+(define_insn "*movsi_insv_1"
+ [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "+q")
+ (const_int 8)
+ (const_int 8))
+ (match_operand:SI 1 "nonimmediate_operand" "qm"))]
+ ""
+ "mov{b}\\t{%b1, %h0|%h0, %b1}"
+ [(set_attr "type" "imov")])
+
+(define_insn "*movqi_insv_2"
+ [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "+q")
+ (const_int 8)
+ (const_int 8))
+ (and:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" "q")
+ (const_int 8))
+ (const_int 255)))]
+ ""
+ "mov{b}\\t{%h1, %h0|%h0, %h1}"
+ [(set_attr "type" "imov")])
- xops[0] = AT_SP (SFmode);
- xops[1] = GEN_INT (4);
- xops[2] = stack_pointer_rtx;
+(define_expand "movdi"
+ [(set (match_operand:DI 0 "general_operand" "")
+ (match_operand:DI 1 "general_operand" ""))]
+ ""
+ "ix86_expand_move (DImode, operands); DONE;")
- output_asm_insn (AS2 (sub%L2,%1,%2), xops);
+(define_insn "*pushdi"
+ [(set (match_operand:DI 0 "push_operand" "=<")
+ (match_operand:DI 1 "nonmemory_operand" "riF"))]
+ ""
+ "#")
- if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
- output_asm_insn (AS1 (fstp%S0,%0), xops);
- else
- output_asm_insn (AS1 (fst%S0,%0), xops);
+(define_insn "*movdi_1"
+ [(set (match_operand:DI 0 "general_operand" "=r,o")
+ (match_operand:DI 1 "general_operand" "riFo,riF"))
+ (clobber (reg:CC 17))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "#")
- RET;
- }
+(define_insn "*movdi_2"
+ [(set (match_operand:DI 0 "general_operand" "=r,o")
+ (match_operand:DI 1 "general_operand" "riFo,riF"))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "#")
- return AS1 (push%L0,%1);
+(define_split
+ [(set (match_operand:DI 0 "push_operand" "")
+ (match_operand:DI 1 "general_operand" ""))]
+ ""
+ [(set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 3))
+ (set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 2))]
+ "
+{
+ split_di (operands+1, 1, operands+2, operands+3);
+ /* Compensate for the fact that we're changing stack offsets in
+ the middle of this operation. */
+ if (reg_mentioned_p (stack_pointer_rtx, operands[2]))
+ operands[2] = adj_offsettable_operand (operands[2], 4);
}")
+;; %%% This multiword shite has got to go.
(define_split
- [(set (match_operand:SF 0 "push_operand" "")
- (match_operand:SF 1 "general_operand" ""))]
- "reload_completed && STACK_REG_P (operands[1])"
- [(set (reg:SI 7)
- (minus:SI (reg:SI 7) (const_int 4)))
- (set (mem:SF (reg:SI 7))
- (match_dup 1))]
- "")
-
+ [(set (match_operand:DI 0 "general_operand" "")
+ (match_operand:DI 1 "general_operand" ""))
+ (clobber (reg:CC 17))]
+ "reload_completed"
+ [(parallel [(set (match_dup 2) (match_dup 4))
+ (clobber (reg:CC 17))])
+ (parallel [(set (match_dup 3) (match_dup 5))
+ (clobber (reg:CC 17))])]
+ "if (ix86_split_movdi (operands)) DONE;")
+
+(define_split
+ [(set (match_operand:DI 0 "general_operand" "")
+ (match_operand:DI 1 "general_operand" ""))]
+ "reload_completed"
+ [(set (match_dup 2) (match_dup 4))
+ (set (match_dup 3) (match_dup 5))]
+ "if (ix86_split_movdi (operands)) DONE;")
+
(define_expand "movsf"
[(set (match_operand:SF 0 "general_operand" "")
(match_operand:SF 1 "general_operand" ""))]
""
- "
+ "ix86_expand_move (SFmode, operands); DONE;")
+
+(define_insn "*pushsf"
+ [(set (match_operand:SF 0 "push_operand" "=<,<")
+ (match_operand:SF 1 "general_operand" "f,Ffm*r"))]
+ ""
+ "*
{
- /* Don't generate memory->memory moves, go through a register */
- if (TARGET_MOVE
- && no_new_pseudos == 0
- && GET_CODE (operands[0]) == MEM
- && GET_CODE (operands[1]) == MEM)
+ switch (which_alternative)
{
- operands[1] = force_reg (SFmode, operands[1]);
- }
+ case 0:
+ /* %%% We loose REG_DEAD notes for controling pops if we split late. */
+ operands[0] = gen_rtx_MEM (SFmode, stack_pointer_rtx);
+ operands[2] = stack_pointer_rtx;
+ operands[3] = GEN_INT (4);
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"sub{l}\\t{%3, %2|%2, %3}\;fstp%z0\\t%0\";
+ else
+ return \"sub{l}\\t{%3, %2|%2, %3}\;fst%z0\\t%0\";
- /* If we are loading a floating point constant that isn't 0 or 1
- into a register, force the value to memory now, since we'll
- get better code out the back end. */
- else if ((reload_in_progress | reload_completed) == 0
- && GET_CODE (operands[0]) != MEM
- && GET_CODE (operands[1]) == CONST_DOUBLE
- && !standard_80387_constant_p (operands[1]))
- {
- operands[1] = validize_mem (force_const_mem (SFmode, operands[1]));
+ case 1:
+ return \"push{l}\\t%1\";
+
+ default:
+ abort ();
}
-}")
+}"
+ [(set_attr "type" "multi,push")])
-;; For the purposes of regclass, prefer FLOAT_REGS.
-(define_insn ""
- [(set (match_operand:SF 0 "nonimmediate_operand" "=f,m,!*r,!m")
- (match_operand:SF 1 "general_operand" "fmG,f,*rmF,*rF"))]
- "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM) || (GET_CODE (operands[1]) != MEM)"
+;; %%% Kill this when call knows how to work this out.
+(define_split
+ [(set (match_operand:SF 0 "push_operand" "")
+ (match_operand:SF 1 "register_operand" ""))]
+ "FP_REGNO_P (REGNO (operands[1]))"
+ [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int -4)))
+ (set (mem:SF (reg:SI 7)) (match_dup 1))])
+
+(define_insn "*movsf_1"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=f,m,f,*r,m,*r")
+ (match_operand:SF 1 "general_operand" "fm,f,G,*rm,*r,G"))]
+ ""
"*
{
- int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
-
- /* First handle a `pop' insn or a `fld %st(0)' */
-
- if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1]))
+ switch (which_alternative)
{
- if (stack_top_dies)
- return AS1 (fstp,%y0);
+ case 0:
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp\\t%0\";
+ else if (STACK_TOP_P (operands[0]))
+ return \"fld%z1\\t%1\";
else
- return AS1 (fld,%y0);
- }
-
- /* Handle other kinds of writes from the 387 */
+ return \"fst\\t%0\";
- if (STACK_TOP_P (operands[1]))
- {
- if (stack_top_dies)
- return AS1 (fstp%z0,%y0);
+ case 1:
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp%z0\\t%0\";
else
- return AS1 (fst%z0,%y0);
- }
+ return \"fst%z0\\t%0\";
- /* Handle other kinds of reads to the 387 */
-
- if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE)
- return output_move_const_single (operands);
-
- if (STACK_TOP_P (operands[0]))
- return AS1 (fld%z1,%y1);
+ case 2:
+ switch (standard_80387_constant_p (operands[1]))
+ {
+ case 1:
+ return \"fldz\";
+ case 2:
+ return \"fld1\";
+ }
+ abort();
- /* Handle all SFmode moves not involving the 387 */
+ case 3:
+ case 4:
+ case 5:
+ return \"mov{l}\\t{%1, %0|%0, %1}\";
- return singlemove_string (operands);
+ default:
+ abort();
+ }
}"
- [(set_attr "type" "fld")])
-
+ [(set_attr "type" "fmov,fmov,fmov,imov,imov,imov")])
(define_insn "swapsf"
- [(set (match_operand:SF 0 "register_operand" "f")
- (match_operand:SF 1 "register_operand" "f"))
+ [(set (match_operand:SF 0 "register_operand" "+f")
+ (match_operand:SF 1 "register_operand" "+f"))
(set (match_dup 1)
(match_dup 0))]
""
"*
{
if (STACK_TOP_P (operands[0]))
- return AS1 (fxch,%1);
+ return \"fxch\\t%1\";
else
- return AS1 (fxch,%0);
-}")
+ return \"fxch\\t%0\";
+}"
+ [(set_attr "type" "fxch")])
+(define_expand "movdf"
+ [(set (match_operand:DF 0 "general_operand" "")
+ (match_operand:DF 1 "general_operand" ""))]
+ ""
+ "ix86_expand_move (DFmode, operands); DONE;")
-(define_insn "movdf_push"
+(define_insn "*pushdf"
[(set (match_operand:DF 0 "push_operand" "=<,<")
- (match_operand:DF 1 "general_operand" "*rfF,o"))]
- "TARGET_PUSH_MEMORY || GET_CODE (operands[1]) != MEM
- || reload_in_progress || reload_completed"
+ (match_operand:DF 1 "general_operand" "f,ofF*r"))]
+ ""
"*
{
- if (STACK_REG_P (operands[1]))
+ switch (which_alternative)
{
- rtx xops[3];
-
- xops[0] = AT_SP (DFmode);
- xops[1] = GEN_INT (8);
- xops[2] = stack_pointer_rtx;
-
- output_asm_insn (AS2 (sub%L2,%1,%2), xops);
-
- if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
- output_asm_insn (AS1 (fstp%Q0,%0), xops);
+ case 0:
+ /* %%% We loose REG_DEAD notes for controling pops if we split late. */
+ operands[0] = gen_rtx_MEM (DFmode, stack_pointer_rtx);
+ operands[2] = stack_pointer_rtx;
+ operands[3] = GEN_INT (8);
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"sub{l}\\t{%3, %2|%2, %3}\;fstp%z0\\t%0\";
else
- output_asm_insn (AS1 (fst%Q0,%0), xops);
-
- RET;
- }
+ return \"sub{l}\\t{%3, %2|%2, %3}\;fst%z0\\t%0\";
- if (which_alternative == 1)
- return output_move_pushmem (operands, insn, GET_MODE_SIZE (DFmode), 0, 0);
+ case 1:
+ return \"#\";
- return output_move_double (operands);
-}")
+ default:
+ abort ();
+ }
+}"
+ [(set_attr "type" "multi")])
+;; %%% Kill this when call knows how to work this out.
(define_split
[(set (match_operand:DF 0 "push_operand" "")
- (match_operand:DF 1 "register_operand" ""))]
- "reload_completed && STACK_REG_P (operands[1])"
- [(set (reg:SI 7)
- (minus:SI (reg:SI 7) (const_int 8)))
- (set (mem:DF (reg:SI 7))
- (match_dup 1))]
+ (match_operand:DF 1 "register_operand" ""))]
+ "reload_completed && FP_REGNO_P (REGNO (operands[1]))"
+ [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int -8)))
+ (set (mem:DF (reg:SI 7)) (match_dup 1))]
"")
-(define_expand "movdf"
- [(set (match_operand:DF 0 "general_operand" "")
+(define_split
+ [(set (match_operand:DF 0 "push_operand" "")
(match_operand:DF 1 "general_operand" ""))]
- ""
+ "reload_completed"
+ [(set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 1))
+ (set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 0))]
"
{
- /* Don't generate memory->memory moves, go through a register */
- if (TARGET_MOVE
- && no_new_pseudos == 0
- && GET_CODE (operands[0]) == MEM
- && GET_CODE (operands[1]) == MEM)
- {
- operands[1] = force_reg (DFmode, operands[1]);
- }
-
- /* If we are loading a floating point constant that isn't 0 or 1 into a
- register, indicate we need the pic register loaded. This could be
- optimized into stores of constants if the target eventually moves to
- memory, but better safe than sorry. */
- else if ((reload_in_progress | reload_completed) == 0
- && GET_CODE (operands[0]) != MEM
- && GET_CODE (operands[1]) == CONST_DOUBLE
- && !standard_80387_constant_p (operands[1]))
- {
- operands[1] = validize_mem (force_const_mem (DFmode, operands[1]));
- }
+ split_di (operands+1, 1, operands+0, operands+1);
+ /* Compensate for the fact that we're changing stack offsets in
+ the middle of this operation. */
+ if (reg_mentioned_p (stack_pointer_rtx, operands[0]))
+ operands[0] = adj_offsettable_operand (operands[0], 4);
}")
-;; For the purposes of regclass, prefer FLOAT_REGS.
-(define_insn ""
- [(set (match_operand:DF 0 "nonimmediate_operand" "=f,m,!*r,!o")
- (match_operand:DF 1 "general_operand" "fmG,f,*roF,*rF"))]
- "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM)
- || (GET_CODE (operands[1]) != MEM)"
+(define_insn "*movdf_1"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "=f,m,f,*r,m,*r")
+ (match_operand:DF 1 "general_operand" "fm,f,G,*rm,*r,G"))]
+ ""
"*
{
- int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
-
- /* First handle a `pop' insn or a `fld %st(0)' */
-
- if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1]))
+ switch (which_alternative)
{
- if (stack_top_dies)
- return AS1 (fstp,%y0);
+ case 0:
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp\\t%0\";
+ else if (STACK_TOP_P (operands[0]))
+ return \"fld%z1\\t%1\";
else
- return AS1 (fld,%y0);
- }
+ return \"fst\\t%0\";
- /* Handle other kinds of writes from the 387 */
-
- if (STACK_TOP_P (operands[1]))
- {
- if (stack_top_dies)
- return AS1 (fstp%z0,%y0);
+ case 1:
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp%z0\\t%0\";
else
- return AS1 (fst%z0,%y0);
- }
-
- /* Handle other kinds of reads to the 387 */
+ return \"fst%z0\\t%0\";
- if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE)
- return output_move_const_single (operands);
-
- if (STACK_TOP_P (operands[0]))
- return AS1 (fld%z1,%y1);
+ case 2:
+ switch (standard_80387_constant_p (operands[1]))
+ {
+ case 1:
+ return \"fldz\";
+ case 2:
+ return \"fld1\";
+ }
+ abort();
- /* Handle all DFmode moves not involving the 387 */
+ case 3:
+ case 4:
+ case 5:
+ return \"#\";
- return output_move_double (operands);
+ default:
+ abort();
+ }
}"
- [(set_attr "type" "fld")])
-
+ [(set_attr "type" "fmov,fmov,fmov,multi,multi,multi")])
+(define_split
+ [(set (match_operand:DF 0 "nonimmediate_operand" "")
+ (match_operand:DF 1 "general_operand" ""))]
+ "reload_completed
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
+ && ! (FP_REG_P (operands[0]) ||
+ (GET_CODE (operands[0]) == SUBREG
+ && FP_REG_P (SUBREG_REG (operands[0]))))
+ && ! (FP_REG_P (operands[1]) ||
+ (GET_CODE (operands[1]) == SUBREG
+ && FP_REG_P (SUBREG_REG (operands[1]))))"
+ [(set (match_dup 0) (match_dup 2))
+ (set (match_dup 1) (match_dup 3))]
+ "split_di (operands+1, 1, operands+2, operands+3);
+ split_di (operands+0, 1, operands+0, operands+1);")
(define_insn "swapdf"
- [(set (match_operand:DF 0 "register_operand" "f")
- (match_operand:DF 1 "register_operand" "f"))
+ [(set (match_operand:DF 0 "register_operand" "+f")
+ (match_operand:DF 1 "register_operand" "+f"))
(set (match_dup 1)
(match_dup 0))]
""
"*
{
if (STACK_TOP_P (operands[0]))
- return AS1 (fxch,%1);
+ return \"fxch\\t%1\";
else
- return AS1 (fxch,%0);
-}")
+ return \"fxch\\t%0\";
+}"
+ [(set_attr "type" "fxch")])
+
+(define_expand "movxf"
+ [(set (match_operand:XF 0 "general_operand" "")
+ (match_operand:XF 1 "general_operand" ""))]
+ ""
+ "ix86_expand_move (XFmode, operands); DONE;")
-(define_insn "movxf_push"
- [(set (match_operand:XF 0 "push_operand" "=<,<")
- (match_operand:XF 1 "general_operand" "*rfF,o"))]
- "TARGET_PUSH_MEMORY || GET_CODE (operands[1]) != MEM
- || reload_in_progress || reload_completed"
+(define_insn "*pushxf"
+ [(set (match_operand:XF 0 "push_operand" "=<")
+ (match_operand:XF 1 "register_operand" "f"))]
+ ""
"*
{
- if (STACK_REG_P (operands[1]))
- {
- rtx xops[3];
-
- xops[0] = AT_SP (XFmode);
- xops[1] = GEN_INT (12);
- xops[2] = stack_pointer_rtx;
-
- output_asm_insn (AS2 (sub%L2,%1,%2), xops);
-
- output_asm_insn (AS1 (fstp%T0,%0), xops);
- if (! find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
- output_asm_insn (AS1 (fld%T0,%0), xops);
-
- RET;
- }
-
- if (which_alternative == 1)
- return output_move_pushmem (operands, insn, GET_MODE_SIZE (XFmode), 0, 0);
-
- return output_move_double (operands);
- }")
+ /* %%% We loose REG_DEAD notes for controling pops if we split late. */
+ operands[0] = gen_rtx_MEM (XFmode, stack_pointer_rtx);
+ operands[2] = stack_pointer_rtx;
+ operands[3] = GEN_INT (12);
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"sub{l}\\t{%3, %2|%2, %3}\;fstp%z0\\t%0\";
+ else
+ return \"sub{l}\\t{%3, %2|%2, %3}\;fst%z0\\t%0\";
+}"
+ [(set_attr "type" "multi")])
(define_split
[(set (match_operand:XF 0 "push_operand" "")
- (match_operand:XF 1 "register_operand" ""))]
- "reload_completed && STACK_REG_P (operands[1])"
- [(set (reg:SI 7)
- (minus:SI (reg:SI 7) (const_int 12)))
- (set (mem:XF (reg:SI 7))
- (match_dup 1))]
- "")
+ (match_operand:XF 1 "register_operand" ""))]
+ "FP_REGNO_P (REGNO (operands[1]))"
+ [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int -12)))
+ (set (mem:XF (reg:SI 7)) (match_dup 1))])
-(define_expand "movxf"
- [(set (match_operand:XF 0 "general_operand" "")
- (match_operand:XF 1 "general_operand" ""))]
- ""
+(define_split
+ [(set (match_operand:DF 0 "push_operand" "")
+ (match_operand:DF 1 "memory_operand" ""))]
+ "reload_completed"
+ [(set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 2))
+ (set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 1))
+ (set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 0))]
"
{
- /* Don't generate memory->memory moves, go through a register */
- if (TARGET_MOVE
- && no_new_pseudos == 0
- && GET_CODE (operands[0]) == MEM
- && GET_CODE (operands[1]) == MEM)
- {
- operands[1] = force_reg (XFmode, operands[1]);
- }
-
- /* If we are loading a floating point constant that isn't 0 or 1
- into a register, indicate we need the pic register loaded. This could
- be optimized into stores of constants if the target eventually moves
- to memory, but better safe than sorry. */
- else if ((reload_in_progress | reload_completed) == 0
- && GET_CODE (operands[0]) != MEM
- && GET_CODE (operands[1]) == CONST_DOUBLE
- && !standard_80387_constant_p (operands[1]))
- {
- operands[1] = validize_mem (force_const_mem (XFmode, operands[1]));
- }
+ operands[0] = change_address (operands[1], SImode, NULL_RTX);
+ operands[1] = adj_offsettable_operand (operands[0], 4);
+ operands[2] = adj_offsettable_operand (operands[0], 8);
+ /* Compensate for the fact that we're changing stack offsets in
+ the middle of this operation. */
+ if (reg_mentioned_p (stack_pointer_rtx, operands[1]))
+ operands[1] = adj_offsettable_operand (operands[1], 4);
+ if (reg_mentioned_p (stack_pointer_rtx, operands[0]))
+ operands[0] = adj_offsettable_operand (operands[0], 8);
}")
-
-(define_insn ""
- [(set (match_operand:XF 0 "nonimmediate_operand" "=f,m,!*r,!o")
- (match_operand:XF 1 "general_operand" "fmG,f,*roF,*rF"))]
- "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM)
- || (GET_CODE (operands[1]) != MEM)"
+(define_insn "*movxf_1"
+ [(set (match_operand:XF 0 "nonimmediate_operand" "=f,m,f")
+ (match_operand:XF 1 "general_operand" "fm,f,G"))]
+ ""
"*
{
- int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
-
- /* First handle a `pop' insn or a `fld %st(0)' */
-
- if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1]))
+ switch (which_alternative)
{
- if (stack_top_dies)
- return AS1 (fstp,%y0);
+ case 0:
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp\\t%0\";
+ else if (STACK_TOP_P (operands[0]))
+ return \"fld%z1\\t%1\";
else
- return AS1 (fld,%y0);
- }
-
- /* Handle other kinds of writes from the 387 */
+ return \"fst\\t%0\";
- if (STACK_TOP_P (operands[1]))
- {
- output_asm_insn (AS1 (fstp%z0,%y0), operands);
- if (! stack_top_dies)
- return AS1 (fld%z0,%y0);
+ case 1:
+ /* There is no non-popping store to memory for XFmode. So if
+ we need one, follow the store with a load. */
+ if (! find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp%z0\\t%0\;fld%z0\\t%0\";
+ else
+ return \"fstp%z0\\t%0\";
- RET;
+ case 2:
+ switch (standard_80387_constant_p (operands[1]))
+ {
+ case 1:
+ return \"fldz\";
+ case 2:
+ return \"fld1\";
+ }
+ break;
}
+ abort();
+}"
+ [(set_attr "type" "fmov")])
- /* Handle other kinds of reads to the 387 */
-
- if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE)
- return output_move_const_single (operands);
-
- if (STACK_TOP_P (operands[0]))
- return AS1 (fld%z1,%y1);
-
- /* Handle all XFmode moves not involving the 387 */
-
- return output_move_double (operands);
-}")
-
-(define_insn "swapxf"
- [(set (match_operand:XF 0 "register_operand" "f")
- (match_operand:XF 1 "register_operand" "f"))
+(define_insn "swapxf"
+ [(set (match_operand:XF 0 "register_operand" "+f")
+ (match_operand:XF 1 "register_operand" "+f"))
(set (match_dup 1)
(match_dup 0))]
""
"*
{
if (STACK_TOP_P (operands[0]))
- return AS1 (fxch,%1);
+ return \"fxch\\t%1\";
else
- return AS1 (fxch,%0);
-}")
-
-(define_insn ""
- [(set (match_operand:DI 0 "push_operand" "=<")
- (match_operand:DI 1 "general_operand" "riF"))]
- ""
- "* return output_move_double (operands);")
-
-(define_insn ""
- [(set (match_operand:DI 0 "push_operand" "=<")
- (match_operand:DI 1 "memory_operand" "o"))]
- "TARGET_PUSH_MEMORY"
- "* return output_move_pushmem (operands, insn, GET_MODE_SIZE (DImode),0,0);")
-
-(define_expand "movdi"
- [(set (match_operand:DI 0 "general_operand" "")
- (match_operand:DI 1 "general_operand" ""))]
- ""
- "
-{
- /* Don't generate memory->memory moves, go through a register */
- if (TARGET_MOVE
- && no_new_pseudos == 0
- && GET_CODE (operands[0]) == MEM
- && GET_CODE (operands[1]) == MEM)
- {
- operands[1] = force_reg (DImode, operands[1]);
- }
-}")
-
-(define_insn ""
- [(set (match_operand:DI 0 "general_operand" "=g,r")
- (match_operand:DI 1 "general_operand" "riF,m"))]
- "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM)
- || (GET_CODE (operands[1]) != MEM)"
- "* return output_move_double (operands);"
- [(set_attr "type" "integer,memory")
- (set_attr "memory" "*,load")])
-
-(define_split
- [(set (match_operand:DI 0 "nonimmediate_operand" "")
- (match_operand:DI 1 "general_operand" ""))]
- "reload_completed
- && (offsettable_memref_p (operands[0])
- || nonmemory_operand (operands[0], DImode))
- && (offsettable_memref_p (operands[1])
- || nonmemory_operand (operands[1], DImode))
- && (! reg_overlap_mentioned_p (gen_lowpart (SImode, operands[0]),
- operands[1])
- || ! reg_overlap_mentioned_p (gen_highpart (SImode, operands[0]),
- operands[1]))"
- [(set (match_dup 2)
- (match_dup 4))
- (set (match_dup 3)
- (match_dup 5))]
- "
-{
- split_di (&operands[0], 1, &operands[2], &operands[3]);
- split_di (&operands[1], 1, &operands[4], &operands[5]);
-
- if (reg_overlap_mentioned_p (operands[2], operands[1]))
- {
- rtx tmp;
-
- tmp = operands[2];
- operands[2] = operands[3];
- operands[3] = tmp;
-
- tmp = operands[4];
- operands[4] = operands[5];
- operands[5] = tmp;
- }
-}")
+ return \"fxch\\t%0\";
+}"
+ [(set_attr "type" "fxch")])
\f
-;;- conversion instructions
-;;- NONE
+;; Zero extension instructions
-;;- zero extension instructions
-;; See comments by `andsi' for when andl is faster than movzx.
-
-(define_expand "zero_extendhisi2"
- [(set (match_operand:SI 0 "register_operand" "")
- (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))]
+(define_insn "zero_extendhisi2"
+ [(set (match_operand:SI 0 "register_operand" "=r,?r")
+ (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0,rm")))
+ (clobber (reg:CC 17))]
""
- "")
-
-;; When optimizing for the PPro/PII or code size, always use movzwl.
-;; We want to use a different pattern so we can use different constraints
-;; than the generic pattern.
-(define_insn ""
- [(set (match_operand:SI 0 "register_operand" "=r")
- (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "rm")))]
- "(optimize_size || (int)ix86_cpu == (int)PROCESSOR_PENTIUMPRO)"
- "* return AS2 (movz%W0%L0,%1,%0);")
-
-(define_insn ""
- [(set (match_operand:SI 0 "register_operand" "=r,&r,?r")
- (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0,rm,rm")))]
- "! (optimize_size || (int)ix86_cpu == (int)PROCESSOR_PENTIUMPRO)"
"*
- {
- rtx xops[2];
-
- if ((TARGET_ZERO_EXTEND_WITH_AND || REGNO (operands[0]) == 0)
- && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1]))
- {
- xops[0] = operands[0];
- xops[1] = GEN_INT (0xffff);
- output_asm_insn (AS2 (and%L0,%1,%k0), xops);
- RET;
- }
- if (TARGET_ZERO_EXTEND_WITH_AND && !reg_overlap_mentioned_p (operands[0], operands[1]))
- {
- output_asm_insn (AS2 (xor%L0,%0,%0),operands);
- output_asm_insn (AS2 (mov%W0,%1,%w0),operands);
- RET;
- }
-
- if (TARGET_ZERO_EXTEND_WITH_AND)
+{
+ switch (get_attr_type (insn))
{
- xops[0] = operands[0];
- xops[1] = GEN_INT (0xffff);
- if (i386_aligned_p (operands[1]))
- output_asm_insn (AS2 (mov%L0,%k1,%k0),operands);
- else
- output_asm_insn (AS2 (mov%W0,%1,%w0),operands);
- output_asm_insn (AS2 (and%L0,%1,%k0), xops);
- RET;
+ case TYPE_ALU1:
+ if (!REG_P (operands[1]) || REGNO (operands[0]) != REGNO (operands[1]))
+ abort ();
+ operands[1] = GEN_INT (0xffff);
+ return \"and{l}\\t{%1, %0|%0, %1}\";
+ default:
+ return \"movz{wl|x}\\t{%1, %0|%0, %1}\";
}
-
-#ifdef INTEL_SYNTAX
- return AS2 (movzx,%1,%0);
-#else
- return AS2 (movz%W0%L0,%1,%0);
-#endif
-}")
+}"
+ [(set (attr "type")
+ (if_then_else (and (eq_attr "alternative" "0")
+ (ne (symbol_ref "TARGET_ZERO_EXTEND_WITH_AND")
+ (const_int 0)))
+ (const_string "alu1")
+ (const_string "imovx")))])
(define_split
[(set (match_operand:SI 0 "register_operand" "")
- (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))]
- "reload_completed && TARGET_ZERO_EXTEND_WITH_AND && !reg_overlap_mentioned_p (operands[0], operands[1])"
- [(set (match_dup 0)
- (const_int 0))
- (set (strict_low_part (match_dup 2))
- (match_dup 1))]
- "operands[2] = gen_rtx_REG (HImode, true_regnum (operands[0]));")
-
+ (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))
+ (clobber (reg:CC 17))]
+ "reload_completed
+ && TARGET_ZERO_EXTEND_WITH_AND
+ && !reg_overlap_mentioned_p (operands[0], operands[1])"
+ [(parallel [(set (match_dup 0) (const_int 0))
+ (clobber (reg:CC 17))])
+ (set (strict_low_part (subreg:HI (match_dup 0) 0)) (match_dup 1))]
+ "")
(define_split
[(set (match_operand:SI 0 "register_operand" "")
- (zero_extend:SI (match_operand:HI 1 "memory_operand" "")))]
- "reload_completed && TARGET_ZERO_EXTEND_WITH_AND && reg_overlap_mentioned_p (operands[0], operands[1])"
- [(set (strict_low_part (match_dup 2))
- (match_dup 1))
- (set (match_dup 0)
- (and:SI (match_dup 0)
- (const_int 65535)))]
- "operands[2] = gen_rtx_REG (HImode, true_regnum (operands[0]));")
-
-(define_expand "zero_extendqihi2"
- [(set (match_operand:HI 0 "register_operand" "")
- (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")))]
- ""
+ (zero_extend:SI (match_operand:HI 1 "memory_operand" "")))
+ (clobber (reg:CC 17))]
+ "reload_completed
+ && TARGET_ZERO_EXTEND_WITH_AND
+ && reg_overlap_mentioned_p (operands[0], operands[1])"
+ [(set (strict_low_part (subreg:HI (match_dup 0) 0)) (match_dup 1))
+ (parallel [(set (match_dup 0) (and:SI (match_dup 0) (const_int 65535)))
+ (clobber (reg:CC 17))])]
"")
-(define_insn ""
- [(set (match_operand:HI 0 "register_operand" "=r")
- (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "qm")))]
- "optimize_size || (int)ix86_cpu == (int)PROCESSOR_PENTIUMPRO"
-
- "* return AS2 (movz%B0%W0,%1,%0);")
-
-(define_insn ""
- [(set (match_operand:HI 0 "register_operand" "=q,&q,?r")
- (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "0,qm,qm")))]
- "! (optimize_size || (int)ix86_cpu == (int)PROCESSOR_PENTIUMPRO)"
+(define_insn "zero_extendqihi2"
+ [(set (match_operand:HI 0 "register_operand" "=q,r,r")
+ (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "0,0,qm")))
+ (clobber (reg:CC 17))]
+ ""
"*
- {
- rtx xops[2];
-
- if ((TARGET_ZERO_EXTEND_WITH_AND || REGNO (operands[0]) == 0)
- && REG_P (operands[1])
- && REGNO (operands[0]) == REGNO (operands[1]))
- {
- xops[0] = operands[0];
- xops[1] = GEN_INT (0xff);
- output_asm_insn (AS2 (and%L0,%1,%k0), xops);
- RET;
- }
- if (TARGET_ZERO_EXTEND_WITH_AND && QI_REG_P (operands[0]))
+{
+ switch (get_attr_type (insn))
{
- if(!reg_overlap_mentioned_p(operands[0],operands[1]))
- {
- output_asm_insn (AS2 (xor%L0,%k0,%k0), operands);
- output_asm_insn (AS2 (mov%B0,%1,%b0), operands);
- }
- else
- {
- xops[0] = operands[0];
- xops[1] = GEN_INT (0xff);
- output_asm_insn (AS2 (mov%B0,%1,%b0),operands);
- output_asm_insn (AS2 (and%L0,%1,%k0), xops);
- }
- RET;
+ case TYPE_ALU1:
+ if (!REG_P (operands[1]) || REGNO (operands[0]) != REGNO (operands[1]))
+ abort ();
+ operands[1] = GEN_INT (0xff);
+ return \"and{l}\\t{%1, %0|%0, %1}\";
+ default:
+ return \"movz{bw|x}\\t{%1, %0|%0, %1}\";
}
-
-#ifdef INTEL_SYNTAX
- return AS2 (movzx,%1,%0);
-#else
- return AS2 (movz%B0%W0,%1,%0);
-#endif
-}")
+}"
+ [(set (attr "type")
+ (cond [(and (eq_attr "alternative" "0")
+ (ne (symbol_ref "TARGET_ZERO_EXTEND_WITH_AND")
+ (const_int 0)))
+ (const_string "alu1")
+ (eq_attr "alternative" "1")
+ (const_string "alu1")
+ ]
+ (const_string "imovx")))])
(define_split
[(set (match_operand:HI 0 "register_operand" "")
- (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")))]
- "reload_completed && QI_REG_P (operands[0]) && TARGET_ZERO_EXTEND_WITH_AND
- && !reg_overlap_mentioned_p (operands[0], operands[1])"
- [(set (match_dup 0)
- (const_int 0))
- (set (strict_low_part (match_dup 2))
- (match_dup 1))]
- "operands[2] = gen_rtx_REG (QImode, REGNO (operands[0]));")
-
+ (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")))
+ (clobber (reg:CC 17))]
+ "reload_completed
+ && QI_REG_P (operands[0])
+ && TARGET_ZERO_EXTEND_WITH_AND
+ && !reg_overlap_mentioned_p (operands[0], operands[1])"
+ [(parallel [(set (match_dup 0) (const_int 0))
+ (clobber (reg:CC 17))])
+ (set (strict_low_part (subreg:QI (match_dup 0) 0)) (match_dup 1))]
+ "")
(define_split
[(set (match_operand:HI 0 "register_operand" "")
- (zero_extend:HI (match_operand:QI 1 "memory_operand" "")))]
- "reload_completed && QI_REG_P (operands[0]) && TARGET_ZERO_EXTEND_WITH_AND
- && reg_overlap_mentioned_p (operands[0], operands[1])"
- [(set (strict_low_part (match_dup 2))
- (match_dup 1))
- (set (match_dup 0)
- (and:HI (match_dup 0)
- (const_int 255)))]
- "operands[2] = gen_rtx_REG (QImode, REGNO (operands[0]));")
+ (zero_extend:HI (match_operand:QI 1 "memory_operand" "")))
+ (clobber (reg:CC 17))]
+ "reload_completed
+ && QI_REG_P (operands[0])
+ && TARGET_ZERO_EXTEND_WITH_AND
+ && reg_overlap_mentioned_p (operands[0], operands[1])"
+ [(set (strict_low_part (subreg:QI (match_dup 0) 0)) (match_dup 1))
+ (parallel [(set (match_dup 0) (and:HI (match_dup 0) (const_int 255)))
+ (clobber (reg:CC 17))])]
+ "")
(define_split
[(set (match_operand:HI 0 "register_operand" "")
- (zero_extend:HI (match_operand:QI 1 "register_operand" "")))]
- "reload_completed && TARGET_ZERO_EXTEND_WITH_AND"
- [(set (match_dup 0)
- (match_dup 2))
- (set (match_dup 0)
- (and:HI (match_dup 0)
- (const_int 255)))]
- "if (GET_CODE (operands[1]) == SUBREG && SUBREG_WORD (operands[1]) == 0)
- operands[1] = SUBREG_REG (operands[1]);
- if (GET_CODE (operands[0]) != REG || GET_CODE (operands[1]) != REG
- || REGNO (operands[0]) == REGNO (operands[1]))
- FAIL;
- operands[2] = gen_rtx_REG (HImode, REGNO (operands[1]));")
-
-(define_expand "zero_extendqisi2"
- [(set (match_operand:SI 0 "register_operand" "")
- (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
- ""
+ (zero_extend:HI (match_operand:QI 1 "register_operand" "")))
+ (clobber (reg:CC 17))]
+ "reload_completed
+ && TARGET_ZERO_EXTEND_WITH_AND
+ && ! reg_overlap_mentioned_p (operands[0], operands[1])"
+ [(set (match_dup 0) (subreg:HI (match_dup 1) 0))
+ (parallel [(set (match_dup 0) (and:HI (match_dup 0) (const_int 255)))
+ (clobber (reg:CC 17))])]
"")
-(define_insn ""
- [(set (match_operand:SI 0 "register_operand" "=r")
- (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "qm")))]
- "optimize_size || (int)ix86_cpu == (int)PROCESSOR_PENTIUMPRO"
- "* return AS2 (movz%B0%L0,%1,%0);")
-
-(define_insn ""
- [(set (match_operand:SI 0 "register_operand" "=q,&q,?r")
- (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "0,qm,qm")))]
- "! (optimize_size || (int)ix86_cpu == (int)PROCESSOR_PENTIUMPRO)"
+(define_insn "zero_extendqisi2"
+ [(set (match_operand:SI 0 "register_operand" "=q,r,r")
+ (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "0,0,qm")))
+ (clobber (reg:CC 17))]
+ ""
"*
- {
- rtx xops[2];
-
- if ((TARGET_ZERO_EXTEND_WITH_AND || REGNO (operands[0]) == 0)
- && REG_P (operands[1])
- && REGNO (operands[0]) == REGNO (operands[1]))
- {
- xops[0] = operands[0];
- xops[1] = GEN_INT (0xff);
- output_asm_insn (AS2 (and%L0,%1,%k0), xops);
- RET;
- }
- if (TARGET_ZERO_EXTEND_WITH_AND && QI_REG_P (operands[0]))
- {
- if(!reg_overlap_mentioned_p (operands[0], operands[1]))
- {
- output_asm_insn (AS2 (xor%L0,%0,%0),operands);
- output_asm_insn (AS2 (mov%B0,%1,%b0),operands);
- }
- else
- {
- xops[0] = operands[0];
- xops[1] = GEN_INT (0xff);
- output_asm_insn (AS2 (mov%B0,%1,%b0), operands);
- output_asm_insn (AS2 (and%L0,%1,%k0), xops);
- }
- RET;
- }
-
- if (TARGET_ZERO_EXTEND_WITH_AND && GET_CODE (operands[1]) == REG)
+{
+ switch (get_attr_type (insn))
{
- xops[0] = operands[0];
- xops[1] = GEN_INT (0xff);
- operands[1] = gen_rtx_REG (SImode, REGNO (operands[1]));
- output_asm_insn (AS2 (mov%L0,%1,%0), operands);
- output_asm_insn (AS2 (and%L0,%1,%k0), xops);
- RET;
+ case TYPE_ALU1:
+ if (!REG_P (operands[1]) || REGNO (operands[0]) != REGNO (operands[1]))
+ abort ();
+ operands[1] = GEN_INT (0xff);
+ return \"and{l}\\t{%1, %0|%0, %1}\";
+ default:
+ return \"movz{bl|x}\\t{%1, %0|%0, %1}\";
}
-
-#ifdef INTEL_SYNTAX
- return AS2 (movzx,%1,%0);
-#else
- return AS2 (movz%B0%L0,%1,%0);
-#endif
-}")
-
-(define_split
- [(set (match_operand:SI 0 "register_operand" "")
- (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
- "reload_completed && QI_REG_P (operands[0]) && TARGET_ZERO_EXTEND_WITH_AND
- && !reg_overlap_mentioned_p (operands[0], operands[1])"
- [(set (match_dup 0)
- (const_int 0))
- (set (strict_low_part (match_dup 2))
- (match_dup 1))]
- "operands[2] = gen_rtx_REG (QImode, REGNO (operands[0]));")
-
+}"
+ [(set (attr "type")
+ (cond [(and (eq_attr "alternative" "0")
+ (ne (symbol_ref "TARGET_ZERO_EXTEND_WITH_AND")
+ (const_int 0)))
+ (const_string "alu1")
+ (eq_attr "alternative" "1")
+ (const_string "alu1")
+ ]
+ (const_string "imovx")))])
(define_split
[(set (match_operand:SI 0 "register_operand" "")
- (zero_extend:SI (match_operand:QI 1 "memory_operand" "")))]
- "reload_completed && QI_REG_P (operands[0]) && TARGET_ZERO_EXTEND_WITH_AND
- && reg_overlap_mentioned_p (operands[0], operands[1])"
- [(set (strict_low_part (match_dup 2))
- (match_dup 1))
- (set (match_dup 0)
- (and:SI (match_dup 0)
- (const_int 255)))]
- "operands[2] = gen_rtx_REG (QImode, REGNO (operands[0]));")
+ (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))
+ (clobber (reg:CC 17))]
+ "reload_completed
+ && TARGET_ZERO_EXTEND_WITH_AND
+ && QI_REG_P (operands[0])
+ && (GET_CODE (operands[1]) == MEM || QI_REG_P (operands[1]))
+ && !reg_overlap_mentioned_p (operands[0], operands[1])"
+ [(parallel [(set (match_dup 0) (const_int 0))
+ (clobber (reg:CC 17))])
+ (set (strict_low_part (subreg:QI (match_dup 0) 0)) (match_dup 1))]
+ "")
(define_split
[(set (match_operand:SI 0 "register_operand" "")
- (zero_extend:SI (match_operand:QI 1 "register_operand" "")))]
- "reload_completed && TARGET_ZERO_EXTEND_WITH_AND
- && ! reg_overlap_mentioned_p (operands[0], operands[1])"
- [(set (match_dup 0)
- (match_dup 2))
- (set (match_dup 0)
- (and:SI (match_dup 0)
- (const_int 255)))]
- "operands[2] = gen_rtx_REG (SImode, true_regnum (operands[1]));")
+ (zero_extend:SI (match_operand:QI 1 "register_operand" "")))
+ (clobber (reg:CC 17))]
+ "reload_completed
+ && TARGET_ZERO_EXTEND_WITH_AND
+ && ! reg_overlap_mentioned_p (operands[0], operands[1])"
+ [(set (match_dup 0) (subreg:SI (match_dup 1) 0))
+ (parallel [(set (match_dup 0) (and:SI (match_dup 0) (const_int 255)))
+ (clobber (reg:CC 17))])]
+ "")
+;; %%% Kill me once multi-word ops are sane.
(define_insn "zero_extendsidi2"
[(set (match_operand:DI 0 "nonimmediate_operand" "=r,?r,?*o")
- (zero_extend:DI (match_operand:SI 1 "general_operand" "0,rm,r")))]
+ (zero_extend:DI (match_operand:SI 1 "general_operand" "0,rm,r")))
+ (clobber (reg:CC 17))]
""
"#")
(define_split
[(set (match_operand:DI 0 "register_operand" "")
- (zero_extend:DI (match_operand:SI 1 "register_operand" "")))]
+ (zero_extend:DI (match_operand:SI 1 "register_operand" "")))
+ (clobber (reg:CC 17))]
"reload_completed && true_regnum (operands[0]) == true_regnum (operands[1])"
- [(set (match_dup 4) (const_int 0))]
+ [(parallel [(set (match_dup 4) (const_int 0))
+ (clobber (reg:CC 17))])]
"split_di (&operands[0], 1, &operands[3], &operands[4]);")
(define_split
[(set (match_operand:DI 0 "nonimmediate_operand" "")
- (zero_extend:DI (match_operand:SI 1 "general_operand" "")))]
+ (zero_extend:DI (match_operand:SI 1 "general_operand" "")))
+ (clobber (reg:CC 17))]
"reload_completed"
[(set (match_dup 3) (match_dup 1))
- (set (match_dup 4) (const_int 0))]
+ (parallel [(set (match_dup 4) (const_int 0))
+ (clobber (reg:CC 17))])]
"split_di (&operands[0], 1, &operands[3], &operands[4]);")
\f
-;;- sign extension instructions
+;; Sign extension instructions
(define_insn "extendsidi2"
- [(set (match_operand:DI 0 "nonimmediate_operand" "=A,?r,?Ar,*o")
- (sign_extend:DI (match_operand:SI 1 "register_operand" "0,0,r,*r")))
- (clobber (match_scratch:SI 2 "=X,X,X,&r"))]
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=*A,r,?r,?*o")
+ (sign_extend:DI (match_operand:SI 1 "register_operand" "0,0,r,r")))
+ (clobber (match_scratch:SI 2 "=X,X,X,&r"))
+ (clobber (reg:CC 17))]
""
"#")
(define_split
[(set (match_operand:DI 0 "memory_operand" "")
(sign_extend:DI (match_operand:SI 1 "register_operand" "")))
- (clobber (match_operand:SI 2 "register_operand" ""))]
+ (clobber (match_operand:SI 2 "register_operand" ""))
+ (clobber (reg:CC 17))]
"(flow2_completed
&& dead_or_set_p (insn, operands[1])
&& !reg_mentioned_p (operands[1], operands[0]))"
[(set (match_dup 3) (match_dup 1))
- (set (match_dup 1) (ashiftrt:SI (match_dup 1) (const_int 31)))
+ (parallel [(set (match_dup 1) (ashiftrt:SI (match_dup 1) (const_int 31)))
+ (clobber (reg:CC 17))])
(set (match_dup 4) (match_dup 1))]
"split_di (&operands[0], 1, &operands[3], &operands[4]);")
(define_split
[(set (match_operand:DI 0 "memory_operand" "")
(sign_extend:DI (match_operand:SI 1 "register_operand" "")))
- (clobber (match_operand:SI 2 "register_operand" ""))]
+ (clobber (match_operand:SI 2 "register_operand" ""))
+ (clobber (reg:CC 17))]
"flow2_completed"
[(const_int 0)]
"
/* Generate a cltd if possible and doing so it profitable. */
if (true_regnum (operands[1]) == 0
&& true_regnum (operands[2]) == 1
- && (optimize_size || !TARGET_PENTIUM))
+ && (optimize_size || TARGET_USE_CLTD))
{
- emit_insn (gen_ashrsi3_31 (operands[2], operands[1]));
+ emit_insn (gen_ashrsi3_31 (operands[2], operands[1], GEN_INT (31)));
}
else
{
emit_move_insn (operands[2], operands[1]);
- emit_insn (gen_ashrsi3_31 (operands[2], operands[2]));
+ emit_insn (gen_ashrsi3_31 (operands[2], operands[2], GEN_INT (31)));
}
emit_move_insn (operands[4], operands[2]);
DONE;
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(sign_extend:DI (match_operand:SI 1 "register_operand" "")))
- (clobber (match_scratch:SI 2 ""))]
+ (clobber (match_scratch:SI 2 ""))
+ (clobber (reg:CC 17))]
"reload_completed"
[(const_int 0)]
"
/* Generate a cltd if possible and doing so it profitable. */
if (true_regnum (operands[3]) == 0
- && (optimize_size || !TARGET_PENTIUM))
+ && (optimize_size || TARGET_USE_CLTD))
{
- emit_insn (gen_ashrsi3_31 (operands[4], operands[3]));
+ emit_insn (gen_ashrsi3_31 (operands[4], operands[3], GEN_INT (31)));
DONE;
}
if (true_regnum (operands[4]) != true_regnum (operands[1]))
emit_move_insn (operands[4], operands[1]);
- emit_insn (gen_ashrsi3_31 (operands[4], operands[4]));
+ emit_insn (gen_ashrsi3_31 (operands[4], operands[4], GEN_INT (31)));
DONE;
}")
-;; Note that the i386 programmers' manual says that the opcodes
-;; are named movsx..., but the assembler on Unix does not accept that.
-;; We use what the Unix assembler expects.
-
(define_insn "extendhisi2"
- [(set (match_operand:SI 0 "register_operand" "=r")
- (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "rm")))]
+ [(set (match_operand:SI 0 "register_operand" "=*a,r")
+ (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "*0,rm")))]
""
"*
{
- if (REGNO (operands[0]) == 0
- && REG_P (operands[1]) && REGNO (operands[1]) == 0
- && (optimize_size || ix86_cpu != PROCESSOR_K6))
-#ifdef INTEL_SYNTAX
- return \"cwde\";
-#else
- return \"cwtl\";
-#endif
-
-#ifdef INTEL_SYNTAX
- return AS2 (movsx,%1,%0);
-#else
- return AS2 (movs%W0%L0,%1,%0);
-#endif
-}")
+ switch (get_attr_length (insn))
+ {
+ case 1:
+ return \"{cwtl|cwde}\";
+ default:
+ return \"movs{wl|x}\\t{%1,%0|%0, %1}\";
+ }
+}"
+ [(set_attr "type" "imovx")
+ (set (attr "length")
+ ;; movsx is short decodable while cwtl is vector decoded.
+ (cond [(and (eq_attr "cpu" "!k6")
+ (eq_attr "alternative" "0"))
+ (const_string "1")
+ ]
+ (const_string "*")))])
(define_insn "extendqihi2"
- [(set (match_operand:HI 0 "register_operand" "=r")
- (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "qm")))]
+ [(set (match_operand:HI 0 "register_operand" "=*a,r")
+ (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "*0,qm")))]
""
"*
{
- if (REGNO (operands[0]) == 0
- && REG_P (operands[1]) && REGNO (operands[1]) == 0
- && (optimize_size || ix86_cpu != PROCESSOR_K6))
- return \"cbtw\";
-
-#ifdef INTEL_SYNTAX
- return AS2 (movsx,%1,%0);
-#else
- return AS2 (movs%B0%W0,%1,%0);
-#endif
-}")
+ switch (get_attr_length (insn))
+ {
+ case 1:
+ return \"{cbtw|cbw}\";
+ default:
+ return \"movs{bw|x}\\t{%1,%0|%0, %1}\";
+ }
+}"
+ [(set_attr "type" "imovx")
+ (set (attr "length")
+ ;; movsx is short decodable while cwtl is vector decoded.
+ (cond [(and (eq_attr "cpu" "!k6")
+ (eq_attr "alternative" "0"))
+ (const_string "1")
+ ]
+ (const_string "*")))])
(define_insn "extendqisi2"
[(set (match_operand:SI 0 "register_operand" "=r")
(sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "qm")))]
""
- "*
-{
-#ifdef INTEL_SYNTAX
- return AS2 (movsx,%1,%0);
-#else
- return AS2 (movs%B0%L0,%1,%0);
-#endif
-}")
-
-\f
-;; Truncation of long long -> 32 bit
-
-(define_expand "truncdisi2"
- [(set (match_operand:SI 0 "nonimmediate_operand" "=r,m")
- (truncate:SI (match_operand:DI 1 "nonimmediate_operand" "ro,r")))]
- ""
- "
-{
- /* Don't generate memory->memory moves, go through a register */
- if (TARGET_MOVE
- && (reload_in_progress | reload_completed) == 0
- && GET_CODE (operands[0]) == MEM
- && GET_CODE (operands[1]) == MEM)
- {
- rtx target = gen_reg_rtx (SImode);
- emit_insn (gen_truncdisi2 (target, operands[1]));
- emit_move_insn (operands[0], target);
- DONE;
- }
-}")
-
-(define_insn ""
- [(set (match_operand:SI 0 "nonimmediate_operand" "=r,m")
- (truncate:SI (match_operand:DI 1 "nonimmediate_operand" "ro,r")))]
- "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM) || (GET_CODE (operands[1]) != MEM)"
- "*
-{
- rtx low[2], high[2], xops[2];
-
- split_di (&operands[1], 1, low, high);
- xops[0] = operands[0];
- xops[1] = low[0];
- if (!rtx_equal_p (xops[0], xops[1]))
- output_asm_insn (AS2 (mov%L0,%1,%0), xops);
-
- RET;
-}")
-
-(define_insn ""
- [(set (match_operand:SI 0 "nonimmediate_operand" "=r,m")
- (truncate:SI (lshiftrt:DI (match_operand:DI 1 "nonimmediate_operand" "ro,r")
- (const_int 32))))]
- "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM) || (GET_CODE (operands[1]) != MEM)"
- "*
-{
- rtx low[2], high[2], xops[2];
-
- split_di (&operands[1], 1, low, high);
- xops[0] = operands[0];
- xops[1] = high[0];
- if (!rtx_equal_p (xops[0], xops[1]))
- output_asm_insn (AS2 (mov%L0,%1,%0), xops);
-
- RET;
-}")
-
-
+ "movs{bl|x}\\t{%1,%0|%0, %1}"
+ [(set_attr "type" "imovx")])
\f
;; Conversions between float and double.
-(define_expand "extendsfdf2"
- [(parallel [(set (match_operand:DF 0 "nonimmediate_operand" "")
- (float_extend:DF
- (match_operand:SF 1 "nonimmediate_operand" "")))
- (clobber (match_dup 2))
- (clobber (match_dup 3))])]
- "TARGET_80387"
- "
-{
- if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
- operands[1] = force_reg (SFmode, operands[1]);
-
- operands[2] = assign_386_stack_local (SFmode, 0);
- operands[3] = assign_386_stack_local (DFmode, 0);
-}")
-
-(define_insn ""
- [(set (match_operand:DF 0 "nonimmediate_operand" "=f,m,!f,!*r")
- (float_extend:DF
- (match_operand:SF 1 "nonimmediate_operand" "fm,f,*r,f")))
- (clobber (match_operand:SF 2 "memory_operand" "m,m,m,m"))
- (clobber (match_operand:DF 3 "memory_operand" "m,m,m,o"))]
- "TARGET_80387 && (GET_CODE (operands[0]) != MEM
- || GET_CODE (operands[1]) != MEM)"
- "*
-{
- output_float_extend (insn, operands);
- return \"\";
-}"
- [(set_attr "type" "fld,fpop,fld,fpop")])
-
-(define_split
- [(set (match_operand:DF 0 "register_operand" "")
- (float_extend:DF (match_operand:SF 1 "register_operand" "")))
- (clobber (match_operand:SF 2 "memory_operand" ""))
- (clobber (match_operand:DF 3 "memory_operand" ""))]
- "TARGET_80387 && reload_completed && NON_STACK_REG_P (operands[1])"
- [(set (match_dup 2)
- (match_dup 1))
- (set (match_dup 0)
- (float_extend:DF (match_dup 2)))]
- "")
-
-(define_split
- [(set (match_operand:DF 0 "register_operand" "")
- (float_extend:DF (match_operand:SF 1 "register_operand" "")))
- (clobber (match_operand:SF 2 "memory_operand" ""))
- (clobber (match_operand:DF 3 "memory_operand" ""))]
- "TARGET_80387 && reload_completed && NON_STACK_REG_P (operands[0])"
- [(set (match_dup 3)
- (float_extend:DF (match_dup 1)))
- (set (match_dup 0)
- (match_dup 3))]
- "")
-
-(define_split
- [(set (match_operand:DF 0 "nonimmediate_operand" "")
- (float_extend:DF (match_operand:SF 1 "nonimmediate_operand" "")))
- (clobber (match_operand:SF 2 "memory_operand" ""))
- (clobber (match_operand:DF 3 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 0)
- (float_extend:DF (match_dup 1)))]
- "")
+;; These are all no-ops in the model used for the 80387. So just
+;; emit moves.
+;; %%% Kill these when call knows how to work out a DFmode push earlier.
(define_insn ""
- [(set (match_operand:DF 0 "nonimmediate_operand" "=f,m")
- (float_extend:DF (match_operand:SF 1 "nonimmediate_operand" "fm,f")))]
- "TARGET_80387 && (GET_CODE (operands[0]) != MEM
- || GET_CODE (operands[1]) != MEM)"
- "*
-{
- output_float_extend (insn, operands);
- return \"\";
-}"
- [(set_attr "type" "fld,fpop")])
-
-(define_expand "extenddfxf2"
- [(parallel [(set (match_operand:XF 0 "nonimmediate_operand" "")
- (float_extend:XF
- (match_operand:DF 1 "nonimmediate_operand" "")))
- (clobber (match_dup 2))
- (clobber (match_dup 3))])]
- "TARGET_80387"
- "
-{
- if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
- operands[1] = force_reg (DFmode, operands[1]);
-
- operands[2] = assign_386_stack_local (DFmode, 0);
- operands[3] = assign_386_stack_local (XFmode, 0);
-}")
-
-(define_insn ""
- [(set (match_operand:XF 0 "nonimmediate_operand" "=f,m,!f,!*r")
- (float_extend:XF
- (match_operand:DF 1 "nonimmediate_operand" "fm,f,*r,f")))
- (clobber (match_operand:DF 2 "memory_operand" "m,m,o,m"))
- (clobber (match_operand:XF 3 "memory_operand" "m,m,m,o"))]
- "TARGET_80387 && (GET_CODE (operands[0]) != MEM
- || GET_CODE (operands[1]) != MEM)"
- "*
-{
- output_float_extend (insn, operands);
- return \"\";
-}"
- [(set_attr "type" "fld,fpop,fld,fpop")])
-
-(define_split
- [(set (match_operand:XF 0 "register_operand" "")
- (float_extend:XF (match_operand:DF 1 "register_operand" "")))
- (clobber (match_operand:DF 2 "memory_operand" ""))
- (clobber (match_operand:XF 3 "memory_operand" ""))]
- "TARGET_80387 && reload_completed && NON_STACK_REG_P (operands[1])"
- [(set (match_dup 2)
- (match_dup 1))
- (set (match_dup 0)
- (float_extend:XF (match_dup 2)))]
- "")
-
-(define_split
- [(set (match_operand:XF 0 "register_operand" "")
- (float_extend:XF (match_operand:DF 1 "register_operand" "")))
- (clobber (match_operand:DF 2 "memory_operand" ""))
- (clobber (match_operand:XF 3 "memory_operand" ""))]
- "TARGET_80387 && reload_completed && NON_STACK_REG_P (operands[0])"
- [(set (match_dup 3)
- (float_extend:XF (match_dup 1)))
- (set (match_dup 0)
- (match_dup 3))]
- "")
-
-(define_split
- [(set (match_operand:XF 0 "nonimmediate_operand" "")
- (float_extend:XF (match_operand:DF 1 "nonimmediate_operand" "")))
- (clobber (match_operand:DF 2 "memory_operand" ""))
- (clobber (match_operand:XF 3 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 0)
- (float_extend:XF (match_dup 1)))]
- "")
-
-(define_insn ""
- [(set (match_operand:XF 0 "nonimmediate_operand" "=f,m")
- (float_extend:XF (match_operand:DF 1 "nonimmediate_operand" "fm,f")))]
- "TARGET_80387 && (GET_CODE (operands[0]) != MEM
- || GET_CODE (operands[1]) != MEM)"
- "*
-{
- output_float_extend (insn, operands);
- return \"\";
-}"
- [(set_attr "type" "fld,fpop")])
-
-(define_expand "extendsfxf2"
- [(parallel [(set (match_operand:XF 0 "nonimmediate_operand" "")
- (float_extend:XF
- (match_operand:SF 1 "nonimmediate_operand" "")))
- (clobber (match_dup 2))
- (clobber (match_dup 3))])]
- "TARGET_80387"
- "
-{
- if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
- operands[1] = force_reg (SFmode, operands[1]);
-
- operands[2] = assign_386_stack_local (SFmode, 0);
- operands[3] = assign_386_stack_local (XFmode, 0);
-}")
-
-(define_insn ""
- [(set (match_operand:XF 0 "nonimmediate_operand" "=f,m,!f,!*r")
- (float_extend:XF
- (match_operand:SF 1 "nonimmediate_operand" "fm,f,*r,f")))
- (clobber (match_operand:SF 2 "memory_operand" "m,m,m,m"))
- (clobber (match_operand:XF 3 "memory_operand" "m,m,m,o"))]
- "TARGET_80387 && (GET_CODE (operands[0]) != MEM
- || GET_CODE (operands[1]) != MEM)"
- "*
-{
- output_float_extend (insn, operands);
- return \"\";
-}"
- [(set_attr "type" "fld,fpop,fld,fpop")])
-
-(define_split
- [(set (match_operand:XF 0 "register_operand" "")
- (float_extend:XF (match_operand:SF 1 "register_operand" "")))
- (clobber (match_operand:SF 2 "memory_operand" ""))
- (clobber (match_operand:XF 3 "memory_operand" ""))]
- "TARGET_80387 && reload_completed && NON_STACK_REG_P (operands[1])"
- [(set (match_dup 2)
- (match_dup 1))
- (set (match_dup 0)
- (float_extend:XF (match_dup 2)))]
- "")
-
-(define_split
- [(set (match_operand:XF 0 "register_operand" "")
- (float_extend:XF (match_operand:SF 1 "register_operand" "")))
- (clobber (match_operand:SF 2 "memory_operand" ""))
- (clobber (match_operand:XF 3 "memory_operand" ""))]
- "TARGET_80387 && reload_completed && NON_STACK_REG_P (operands[0])"
- [(set (match_dup 3)
- (float_extend:XF (match_dup 1)))
- (set (match_dup 0)
- (match_dup 3))]
- "")
+ [(set (match_operand:DF 0 "push_operand" "=<")
+ (float_extend:DF (match_operand:SF 1 "nonimmediate_operand" "f")))]
+ "0"
+ "#")
(define_split
- [(set (match_operand:XF 0 "nonimmediate_operand" "")
- (float_extend:XF (match_operand:SF 1 "nonimmediate_operand" "")))
- (clobber (match_operand:SF 2 "memory_operand" ""))
- (clobber (match_operand:XF 3 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 0)
- (float_extend:XF (match_dup 1)))]
- "")
-
-(define_insn ""
- [(set (match_operand:XF 0 "nonimmediate_operand" "=f,m")
- (float_extend:XF
- (match_operand:SF 1 "nonimmediate_operand" "fm,f")))]
- "TARGET_80387 && (GET_CODE (operands[0]) != MEM
- || GET_CODE (operands[1]) != MEM)"
- "*
-{
- output_float_extend (insn, operands);
- return \"\";
-}"
- [(set_attr "type" "fld,fpop")])
-
-(define_expand "truncdfsf2"
- [(parallel [(set (match_operand:SF 0 "nonimmediate_operand" "")
- (float_truncate:SF
- (match_operand:DF 1 "register_operand" "")))
- (clobber (match_dup 2))])]
- "TARGET_80387"
- "
-{
- operands[2] = (rtx) assign_386_stack_local (SFmode, 0);
-}")
+ [(set (match_operand:DF 0 "push_operand" "")
+ (float_extend:DF (match_operand:SF 1 "register_operand" "")))]
+ "FP_REGNO_P (REGNO (operands[1]))"
+ [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int -8)))
+ (set (mem:DF (reg:SI 7)) (float_extend:DF (match_dup 1)))])
(define_insn ""
- [(set (match_operand:SF 0 "nonimmediate_operand" "=f,m,!*r")
- (float_truncate:SF
- (match_operand:DF 1 "register_operand" "0,f,f")))
- (clobber (match_operand:SF 2 "memory_operand" "m,m,m"))]
- "TARGET_80387"
- "*
-{
- int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
- rtx xops[1];
-
- xops[0] = GET_CODE (operands[0]) == MEM ? operands[0] : operands[2];
-
- if (stack_top_dies || STACK_REG_P (operands[0]))
- output_asm_insn (AS1 (fstp%z0,%0), xops);
- else
- output_asm_insn (AS1 (fst%z0,%0), xops);
-
- if (STACK_REG_P (operands[0]))
- return AS1 (fld%z2,%2);
- else if (NON_STACK_REG_P (operands[0]))
- return AS2 (mov%L0,%2,%0);
-
- return \"\";
-}"
- [(set_attr "type" "fpop")])
-
-(define_split
- [(set (match_operand:SF 0 "register_operand" "")
- (float_truncate:SF (match_operand:DF 1 "register_operand" "")))
- (clobber (match_operand:SF 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 2)
- (float_truncate:SF (match_dup 1)))
- (set (match_dup 0)
- (match_dup 2))]
- "")
+ [(set (match_operand:XF 0 "push_operand" "=<")
+ (float_extend:XF (match_operand:SF 1 "nonimmediate_operand" "f")))]
+ "0"
+ "#")
(define_split
- [(set (match_operand:SF 0 "memory_operand" "")
- (float_truncate:SF (match_operand:DF 1 "register_operand" "")))
- (clobber (match_operand:SF 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 0)
- (float_truncate:SF (match_dup 1)))]
- "")
-
-;; This cannot output into an f-reg because there is no way to be sure
-;; of truncating in that case.
-
-(define_insn ""
- [(set (match_operand:SF 0 "memory_operand" "=m")
- (float_truncate:SF (match_operand:DF 1 "register_operand" "f")))]
- "TARGET_80387"
- "*
-{
- int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
-
- if (stack_top_dies)
- return AS1 (fstp%z0,%0);
- else
- return AS1 (fst%z0,%0);
-}"
- [(set_attr "type" "fpop")])
-
-(define_expand "truncxfsf2"
- [(parallel [(set (match_operand:SF 0 "nonimmediate_operand" "")
- (float_truncate:SF
- (match_operand:XF 1 "register_operand" "")))
- (clobber (match_dup 2))])]
- "TARGET_80387"
- "
-{
- operands[2] = (rtx) assign_386_stack_local (SFmode, 0);
-}")
+ [(set (match_operand:XF 0 "push_operand" "")
+ (float_extend:XF (match_operand:SF 1 "register_operand" "")))]
+ "FP_REGNO_P (REGNO (operands[1]))"
+ [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int -12)))
+ (set (mem:DF (reg:SI 7)) (float_extend:XF (match_dup 1)))])
(define_insn ""
- [(set (match_operand:SF 0 "nonimmediate_operand" "=f,m,!*r")
- (float_truncate:SF
- (match_operand:XF 1 "register_operand" "0,f,f")))
- (clobber (match_operand:SF 2 "memory_operand" "m,m,m"))]
- "TARGET_80387"
- "*
-{
- int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
- rtx xops[1];
-
- xops[0] = GET_CODE (operands[0]) == MEM ? operands[0] : operands[2];
-
- if (stack_top_dies || STACK_REG_P (operands[0]))
- output_asm_insn (AS1 (fstp%z0,%0), xops);
- else
- output_asm_insn (AS1 (fst%z0,%0), xops);
-
- if (STACK_REG_P (operands[0]))
- return AS1 (fld%z2,%2);
- else if (NON_STACK_REG_P (operands[0]))
- return AS2 (mov%L0,%2,%0);
-
- return \"\";
-}"
- [(set_attr "type" "fpop")])
-
-(define_split
- [(set (match_operand:SF 0 "register_operand" "")
- (float_truncate:SF (match_operand:XF 1 "register_operand" "")))
- (clobber (match_operand:SF 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 2)
- (float_truncate:SF (match_dup 1)))
- (set (match_dup 0)
- (match_dup 2))]
- "")
+ [(set (match_operand:XF 0 "push_operand" "=<")
+ (float_extend:XF (match_operand:DF 1 "nonimmediate_operand" "f")))]
+ "0"
+ "#")
(define_split
- [(set (match_operand:SF 0 "memory_operand" "")
- (float_truncate:SF (match_operand:XF 1 "register_operand" "")))
- (clobber (match_operand:SF 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 0)
- (float_truncate:SF (match_dup 1)))]
- "")
-
-(define_insn ""
- [(set (match_operand:SF 0 "memory_operand" "=m")
- (float_truncate:SF (match_operand:XF 1 "register_operand" "f")))]
- "TARGET_80387"
- "*
-{
- int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
-
- if (stack_top_dies)
- return AS1 (fstp%z0,%0);
- else
- return AS1 (fst%z0,%0);
-}"
- [(set_attr "type" "fpop")])
-
-(define_expand "truncxfdf2"
- [(parallel [(set (match_operand:DF 0 "nonimmediate_operand" "")
- (float_truncate:DF
- (match_operand:XF 1 "register_operand" "")))
- (clobber (match_dup 2))])]
- "TARGET_80387"
- "
-{
- operands[2] = (rtx) assign_386_stack_local (DFmode, 0);
-}")
+ [(set (match_operand:XF 0 "push_operand" "")
+ (float_extend:XF (match_operand:DF 1 "register_operand" "")))]
+ "FP_REGNO_P (REGNO (operands[1]))"
+ [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int -12)))
+ (set (mem:DF (reg:SI 7)) (float_extend:XF (match_dup 1)))])
-(define_insn ""
- [(set (match_operand:DF 0 "nonimmediate_operand" "=f,m,!*r")
- (float_truncate:DF
- (match_operand:XF 1 "register_operand" "0,f,f")))
- (clobber (match_operand:DF 2 "memory_operand" "m,m,o"))]
+(define_insn "extendsfdf2"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "=f,m")
+ (float_extend:DF (match_operand:SF 1 "nonimmediate_operand" "fm,f")))]
"TARGET_80387"
"*
{
- int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
- rtx xops[2];
-
- xops[0] = GET_CODE (operands[0]) == MEM ? operands[0] : operands[2];
-
- if (stack_top_dies || STACK_REG_P (operands[0]))
- output_asm_insn (AS1 (fstp%z0,%0), xops);
- else
- output_asm_insn (AS1 (fst%z0,%0), xops);
-
- if (STACK_REG_P (operands[0]))
- return AS1 (fld%z2,%2);
- else if (NON_STACK_REG_P (operands[0]))
+ switch (which_alternative)
{
- xops[0] = operands[0];
- xops[1] = operands[2];
- output_asm_insn (output_move_double (xops), xops);
- }
-
- return \"\";
-}"
- [(set_attr "type" "fpop")])
-
-(define_split
- [(set (match_operand:DF 0 "register_operand" "")
- (float_truncate:DF (match_operand:XF 1 "register_operand" "")))
- (clobber (match_operand:DF 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 2)
- (float_truncate:DF (match_dup 1)))
- (set (match_dup 0)
- (match_dup 2))]
- "")
-
-(define_split
- [(set (match_operand:DF 0 "memory_operand" "")
- (float_truncate:DF (match_operand:XF 1 "register_operand" "")))
- (clobber (match_operand:DF 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 0)
- (float_truncate:DF (match_dup 1)))]
- "")
-
-(define_insn ""
- [(set (match_operand:DF 0 "memory_operand" "=m")
- (float_truncate:DF (match_operand:XF 1 "register_operand" "f")))]
- "TARGET_80387"
- "*
-{
- int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
-
- if (stack_top_dies)
- return AS1 (fstp%z0,%0);
- else
- return AS1 (fst%z0,%0);
-}"
- [(set_attr "type" "fpop")])
-\f
-;; Conversions between floating point and fix point.
-
-(define_expand "fix_truncsfsi2"
- [(parallel [(set (match_operand:SI 0 "nonimmediate_operand" "")
- (fix:SI (fix:SF (match_operand:SF 1 "register_operand" ""))))
- (clobber (match_dup 2))
- (clobber (match_dup 3))
- (clobber (match_dup 4))
- (clobber (match_scratch:HI 5 ""))])]
- "TARGET_80387"
- "
-{
- operands[2] = (rtx) assign_386_stack_local (HImode, 0);
- operands[3] = (rtx) assign_386_stack_local (HImode, 1);
- operands[4] = (rtx) assign_386_stack_local (SImode, 0);
-}")
-
-;; We have to earlyclobber the register output to prevent it from overlapping
-;; with the address for one of the scratch memory operands.
-(define_insn ""
- [(set (match_operand:SI 0 "nonimmediate_operand" "=m,!&r")
- (fix:SI (fix:SF (match_operand:SF 1 "register_operand" "f,f"))))
- (clobber (match_operand:HI 2 "memory_operand" "m,m"))
- (clobber (match_operand:HI 3 "memory_operand" "m,m"))
- (clobber (match_operand:SI 4 "memory_operand" "m,m"))
- (clobber (match_scratch:HI 5 "=&r,&r"))]
- "TARGET_80387"
- "* return output_fix_trunc (insn, operands);"
- [(set_attr "type" "fpop")])
-
-(define_expand "fix_truncsfdi2"
- [(parallel [(set (match_operand:DI 0 "nonimmediate_operand" "")
- (fix:DI (fix:SF (match_operand:SF 1 "register_operand" ""))))
- (clobber (match_dup 1))
- (clobber (match_dup 2))
- (clobber (match_dup 3))
- (clobber (match_dup 4))
- (clobber (match_scratch:HI 5 ""))])]
- "TARGET_80387"
- "
-{
- operands[1] = copy_to_mode_reg (SFmode, operands[1]);
- operands[2] = (rtx) assign_386_stack_local (HImode, 0);
- operands[3] = (rtx) assign_386_stack_local (HImode, 1);
- operands[4] = (rtx) assign_386_stack_local (DImode, 0);
-}")
-
-;; We have to earlyclobber the register output to prevent it from overlapping
-;; with the address for one of the scratch memory operands.
-(define_insn ""
- [(set (match_operand:DI 0 "nonimmediate_operand" "=m,!&r")
- (fix:DI (fix:SF (match_operand:SF 1 "register_operand" "f,f"))))
- (clobber (match_dup 1))
- (clobber (match_operand:HI 2 "memory_operand" "m,m"))
- (clobber (match_operand:HI 3 "memory_operand" "m,m"))
- (clobber (match_operand:DI 4 "memory_operand" "m,o"))
- (clobber (match_scratch:HI 5 "=&r,&r"))]
- "TARGET_80387"
- "* return output_fix_trunc (insn, operands);"
- [(set_attr "type" "fpop")])
-
-(define_expand "fix_truncdfsi2"
- [(parallel [(set (match_operand:SI 0 "nonimmediate_operand" "")
- (fix:SI (fix:DF (match_operand:DF 1 "register_operand" ""))))
- (clobber (match_dup 2))
- (clobber (match_dup 3))
- (clobber (match_dup 4))
- (clobber (match_scratch:HI 5 ""))])]
- "TARGET_80387"
- "
-{
- operands[2] = (rtx) assign_386_stack_local (HImode, 0);
- operands[3] = (rtx) assign_386_stack_local (HImode, 1);
- operands[4] = (rtx) assign_386_stack_local (SImode, 0);
-}")
-
-;; We have to earlyclobber the register output to prevent it from overlapping
-;; with the address for one of the scratch memory operands.
-(define_insn ""
- [(set (match_operand:SI 0 "nonimmediate_operand" "=m,!&r")
- (fix:SI (fix:DF (match_operand:DF 1 "register_operand" "f,f"))))
- (clobber (match_operand:HI 2 "memory_operand" "m,m"))
- (clobber (match_operand:HI 3 "memory_operand" "m,m"))
- (clobber (match_operand:SI 4 "memory_operand" "m,m"))
- (clobber (match_scratch:HI 5 "=&r,&r"))]
- "TARGET_80387"
- "* return output_fix_trunc (insn, operands);"
- [(set_attr "type" "fpop")])
-
-(define_expand "fix_truncdfdi2"
- [(parallel [(set (match_operand:DI 0 "nonimmediate_operand" "")
- (fix:DI (fix:DF (match_operand:DF 1 "register_operand" ""))))
- (clobber (match_dup 1))
- (clobber (match_dup 2))
- (clobber (match_dup 3))
- (clobber (match_dup 4))
- (clobber (match_scratch:HI 5 ""))])]
- "TARGET_80387"
- "
-{
- operands[1] = copy_to_mode_reg (DFmode, operands[1]);
- operands[2] = (rtx) assign_386_stack_local (HImode, 0);
- operands[3] = (rtx) assign_386_stack_local (HImode, 1);
- operands[4] = (rtx) assign_386_stack_local (DImode, 0);
-}")
+ case 0:
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp\\t%0\";
+ else if (STACK_TOP_P (operands[0]))
+ return \"fld%z1\\t%1\";
+ else
+ return \"fst\\t%0\";
-;; We have to earlyclobber the register output to prevent it from overlapping
-;; with the address for one of the scratch memory operands.
-(define_insn ""
- [(set (match_operand:DI 0 "nonimmediate_operand" "=m,!&r")
- (fix:DI (fix:DF (match_operand:DF 1 "register_operand" "f,f"))))
- (clobber (match_dup 1))
- (clobber (match_operand:HI 2 "memory_operand" "m,m"))
- (clobber (match_operand:HI 3 "memory_operand" "m,m"))
- (clobber (match_operand:DI 4 "memory_operand" "m,o"))
- (clobber (match_scratch:HI 5 "=&r,&r"))]
- "TARGET_80387"
- "* return output_fix_trunc (insn, operands);"
- [(set_attr "type" "fpop")])
+ case 1:
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp%z0\\t%0\";
-(define_expand "fix_truncxfsi2"
- [(parallel [(set (match_operand:SI 0 "nonimmediate_operand" "")
- (fix:SI (fix:XF (match_operand:XF 1 "register_operand" ""))))
- (clobber (match_dup 2))
- (clobber (match_dup 3))
- (clobber (match_dup 4))
- (clobber (match_scratch:HI 5 ""))])]
- "TARGET_80387"
- "
-{
- operands[2] = (rtx) assign_386_stack_local (HImode, 0);
- operands[3] = (rtx) assign_386_stack_local (HImode, 1);
- operands[4] = (rtx) assign_386_stack_local (SImode, 0);
-}")
-
-;; We have to earlyclobber the register output to prevent it from overlapping
-;; with the address for one of the scratch memory operands.
-(define_insn ""
- [(set (match_operand:SI 0 "nonimmediate_operand" "=m,!&r")
- (fix:SI (fix:XF (match_operand:XF 1 "register_operand" "f,f"))))
- (clobber (match_operand:HI 2 "memory_operand" "m,m"))
- (clobber (match_operand:HI 3 "memory_operand" "m,m"))
- (clobber (match_operand:SI 4 "memory_operand" "m,m"))
- (clobber (match_scratch:HI 5 "=&r,&r"))]
- "TARGET_80387"
- "* return output_fix_trunc (insn, operands);"
- [(set_attr "type" "fpop")])
+ else
+ return \"fst%z0\\t%0\";
-(define_expand "fix_truncxfdi2"
- [(parallel [(set (match_operand:DI 0 "nonimmediate_operand" "")
- (fix:DI (fix:XF (match_operand:XF 1 "register_operand" ""))))
- (clobber (match_dup 1))
- (clobber (match_dup 2))
- (clobber (match_dup 3))
- (clobber (match_dup 4))
- (clobber (match_scratch:HI 5 ""))])]
+ default:
+ abort ();
+ }
+}"
+ [(set_attr "type" "fmov")])
+
+(define_insn "extendsfxf2"
+ [(set (match_operand:XF 0 "nonimmediate_operand" "=f,m")
+ (float_extend:XF (match_operand:SF 1 "nonimmediate_operand" "fm,f")))]
"TARGET_80387"
- "
+ "*
{
- operands[1] = copy_to_mode_reg (XFmode, operands[1]);
- operands[2] = (rtx) assign_386_stack_local (HImode, 0);
- operands[3] = (rtx) assign_386_stack_local (HImode, 1);
- operands[4] = (rtx) assign_386_stack_local (DImode, 0);
-}")
+ switch (which_alternative)
+ {
+ case 0:
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp\\t%0\";
+ else if (STACK_TOP_P (operands[0]))
+ return \"fld%z1\\t%1\";
+ else
+ return \"fst\\t%0\";
-;; We have to earlyclobber the register output to prevent it from overlapping
-;; with the address for one of the scratch memory operands.
-(define_insn ""
- [(set (match_operand:DI 0 "nonimmediate_operand" "=m,!&r")
- (fix:DI (fix:XF (match_operand:XF 1 "register_operand" "f,f"))))
- (clobber (match_dup 1))
- (clobber (match_operand:HI 2 "memory_operand" "m,m"))
- (clobber (match_operand:HI 3 "memory_operand" "m,m"))
- (clobber (match_operand:DI 4 "memory_operand" "m,o"))
- (clobber (match_scratch:HI 5 "=&r,&r"))]
- "TARGET_80387"
- "* return output_fix_trunc (insn, operands);"
- [(set_attr "type" "fpop")])
-\f
-;; Conversion between fixed point and floating point.
+ case 1:
+ /* There is no non-popping store to memory for XFmode. So if
+ we need one, follow the store with a load. */
+ if (! find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp%z0\\t%0\\n\\tfld%z0\\t%0\";
+ else
+ return \"fstp%z0\\t%0\";
-;; ??? Possibly represent floatunssidf2 here in gcc2.
+ default:
+ abort ();
+ }
+}"
+ [(set_attr "type" "fmov")])
-(define_expand "floatsisf2"
- [(parallel [(set (match_operand:SF 0 "register_operand" "")
- (float:SF (match_operand:SI 1 "nonimmediate_operand" "")))
- (clobber (match_dup 2))])]
+(define_insn "extenddfxf2"
+ [(set (match_operand:XF 0 "nonimmediate_operand" "=f,m")
+ (float_extend:XF (match_operand:DF 1 "nonimmediate_operand" "fm,f")))]
"TARGET_80387"
- "operands[2] = assign_386_stack_local (SImode, 0);")
+ "*
+{
+ switch (which_alternative)
+ {
+ case 0:
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp\\t%0\";
+ else if (STACK_TOP_P (operands[0]))
+ return \"fld%z1\\t%1\";
+ else
+ return \"fst\\t%0\";
-(define_insn ""
- [(set (match_operand:SF 0 "register_operand" "=f,f")
- (float:SF (match_operand:SI 1 "nonimmediate_operand" "m,!r")))
- (clobber (match_operand:SI 2 "memory_operand" "m,m"))]
- "TARGET_80387"
- "#")
+ case 1:
+ /* There is no non-popping store to memory for XFmode. So if
+ we need one, follow the store with a load. */
+ if (! find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp%z0\\t%0\\n\\tfld%z0\\t%0\";
+ else
+ return \"fstp%z0\\t%0\";
-(define_split
- [(set (match_operand:SF 0 "register_operand" "")
- (float:SF (match_operand:SI 1 "memory_operand" "")))
- (clobber (match_operand:SI 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 0)
- (float:SF (match_dup 1)))]
- "")
+ default:
+ abort ();
+ }
+}"
+ [(set_attr "type" "fmov")])
-(define_split
- [(set (match_operand:SF 0 "register_operand" "")
- (float:SF (match_operand:SI 1 "register_operand" "")))
- (clobber (match_operand:SI 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 2)
- (match_dup 1))
- (set (match_dup 0)
- (float:SF (match_dup 2)))]
- "")
+;; %%% This seems bad bad news.
+;; This cannot output into an f-reg because there is no way to be sure
+;; of truncating in that case. Otherwise this is just like a simple move
+;; insn. So we pretend we can output to a reg in order to get better
+;; register preferencing, but we really use a stack slot.
-(define_insn ""
- [(set (match_operand:SF 0 "register_operand" "=f")
- (float:SF (match_operand:SI 1 "memory_operand" "m")))]
+(define_expand "truncdfsf2"
+ [(parallel [(set (match_operand:SF 0 "nonimmediate_operand" "")
+ (float_truncate:SF
+ (match_operand:DF 1 "register_operand" "")))
+ (clobber (match_dup 2))])]
"TARGET_80387"
- "* return AS1 (fild%z1,%1);"
- [(set_attr "type" "fpop")])
+ "operands[2] = assign_386_stack_local (SFmode, 0);")
-(define_expand "floathisf2"
- [(parallel [(set (match_operand:SF 0 "register_operand" "")
- (float:SF (match_operand:HI 1 "nonimmediate_operand" "")))
- (clobber (match_dup 2))])]
+(define_insn "*truncdfsf2_1"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=m,f")
+ (float_truncate:SF
+ (match_operand:DF 1 "register_operand" "f,0")))
+ (clobber (match_operand:SF 2 "memory_operand" "m,m"))]
"TARGET_80387"
- "operands[2] = assign_386_stack_local (HImode, 0);")
+ "*
+{
+ switch (which_alternative)
+ {
+ case 0:
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp%z0\\t%0\";
+ else
+ return \"fst%z0\\t%0\";
+ case 1:
+ return \"fstp%z2\\t%2\;fld%z2\\t%2\";
+ }
+ abort ();
+}"
+ [(set_attr "type" "fmov,multi")])
-(define_insn ""
- [(set (match_operand:SF 0 "register_operand" "=f,f")
- (float:SF (match_operand:HI 1 "nonimmediate_operand" "m,!r")))
- (clobber (match_operand:HI 2 "memory_operand" "m,m"))]
+(define_insn "*truncdfsf2_2"
+ [(set (match_operand:SF 0 "memory_operand" "=m")
+ (float_truncate:SF
+ (match_operand:DF 1 "register_operand" "f")))]
"TARGET_80387"
- "#")
+ "*
+{
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp%z0\\t%0\";
+ else
+ return \"fst%z0\\t%0\";
+}"
+ [(set_attr "type" "fmov")])
(define_split
- [(set (match_operand:SF 0 "register_operand" "")
- (float:SF (match_operand:HI 1 "memory_operand" "")))
- (clobber (match_operand:HI 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 0)
- (float:SF (match_dup 1)))]
+ [(set (match_operand:SF 0 "memory_operand" "")
+ (float_truncate:SF
+ (match_operand:DF 1 "register_operand" "")))
+ (clobber (match_operand:SF 2 "memory_operand" ""))]
+ "TARGET_80387"
+ [(set (match_dup 0) (float_truncate:SF (match_dup 1)))]
"")
(define_split
[(set (match_operand:SF 0 "register_operand" "")
- (float:SF (match_operand:HI 1 "register_operand" "")))
- (clobber (match_operand:HI 2 "memory_operand" ""))]
+ (float_truncate:SF
+ (match_operand:DF 1 "register_operand" "")))
+ (clobber (match_operand:SF 2 "memory_operand" ""))]
"TARGET_80387 && reload_completed"
- [(set (match_dup 2)
- (match_dup 1))
- (set (match_dup 0)
- (float:SF (match_dup 2)))]
+ [(set (match_dup 2) (float_truncate:SF (match_dup 1)))
+ (set (match_dup 0) (match_dup 2))]
"")
-(define_insn ""
- [(set (match_operand:SF 0 "register_operand" "=f")
- (float:SF (match_operand:HI 1 "memory_operand" "m")))]
+(define_expand "truncxfsf2"
+ [(parallel [(set (match_operand:SF 0 "nonimmediate_operand" "")
+ (float_truncate:SF
+ (match_operand:XF 1 "register_operand" "")))
+ (clobber (match_dup 2))])]
"TARGET_80387"
- "* return AS1 (fild%z1,%1);"
- [(set_attr "type" "fpop")])
+ "operands[2] = assign_386_stack_local (SFmode, 0);")
-(define_expand "floatdisf2"
- [(parallel [(set (match_operand:SF 0 "register_operand" "")
- (float:SF (match_operand:DI 1 "nonimmediate_operand" "")))
- (clobber (match_dup 2))])]
+(define_insn "*truncxfsf2_1"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=m,f")
+ (float_truncate:SF
+ (match_operand:XF 1 "register_operand" "f,0")))
+ (clobber (match_operand:SF 2 "memory_operand" "m,m"))]
"TARGET_80387"
- "operands[2] = assign_386_stack_local (DImode, 0);")
+ "*
+{
+ switch (which_alternative)
+ {
+ case 0:
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp%z0\\t%0\";
+ else
+ return \"fst%z0\\t%0\";
+ case 1:
+ return \"fstp%z2\\t%2\;fld%z2\\t%2\";
+ }
+ abort ();
+}"
+ [(set_attr "type" "fmov,multi")])
-(define_insn ""
- [(set (match_operand:SF 0 "register_operand" "=f,f")
- (float:SF (match_operand:DI 1 "nonimmediate_operand" "m,!r")))
- (clobber (match_operand:DI 2 "memory_operand" "m,o"))]
+(define_insn "*truncxfsf2_2"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=m")
+ (float_truncate:SF
+ (match_operand:XF 1 "register_operand" "f")))]
"TARGET_80387"
- "#")
+ "*
+{
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp%z0\\t%0\";
+ else
+ return \"fst%z0\\t%0\";
+}"
+ [(set_attr "type" "fmov")])
(define_split
- [(set (match_operand:SF 0 "register_operand" "")
- (float:SF (match_operand:DI 1 "memory_operand" "")))
- (clobber (match_operand:DI 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 0)
- (float:SF (match_dup 1)))]
+ [(set (match_operand:SF 0 "memory_operand" "")
+ (float_truncate:SF
+ (match_operand:XF 1 "register_operand" "")))
+ (clobber (match_operand:SF 2 "memory_operand" ""))]
+ "TARGET_80387"
+ [(set (match_dup 0) (float_truncate:SF (match_dup 1)))]
"")
(define_split
[(set (match_operand:SF 0 "register_operand" "")
- (float:SF (match_operand:DI 1 "register_operand" "")))
- (clobber (match_operand:DI 2 "memory_operand" ""))]
+ (float_truncate:SF
+ (match_operand:XF 1 "register_operand" "")))
+ (clobber (match_operand:SF 2 "memory_operand" ""))]
"TARGET_80387 && reload_completed"
- [(set (match_dup 2)
- (match_dup 1))
- (set (match_dup 0)
- (float:SF (match_dup 2)))]
+ [(set (match_dup 2) (float_truncate:SF (match_dup 1)))
+ (set (match_dup 0) (match_dup 2))]
"")
-(define_insn ""
- [(set (match_operand:SF 0 "register_operand" "=f")
- (float:SF (match_operand:DI 1 "memory_operand" "m")))]
+(define_expand "truncxfdf2"
+ [(parallel [(set (match_operand:DF 0 "nonimmediate_operand" "")
+ (float_truncate:DF
+ (match_operand:XF 1 "register_operand" "")))
+ (clobber (match_dup 2))])]
"TARGET_80387"
- "* return AS1 (fild%z1,%1);"
- [(set_attr "type" "fpop")])
+ "operands[2] = assign_386_stack_local (DFmode, 0);")
-(define_expand "floatsidf2"
- [(parallel [(set (match_operand:DF 0 "register_operand" "")
- (float:DF (match_operand:SI 1 "nonimmediate_operand" "")))
- (clobber (match_dup 2))])]
+(define_insn "*truncxfdf2_1"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "=m,f")
+ (float_truncate:DF
+ (match_operand:XF 1 "register_operand" "f,0")))
+ (clobber (match_operand:DF 2 "memory_operand" "m,m"))]
"TARGET_80387"
- "operands[2] = assign_386_stack_local (SImode, 0);")
+ "*
+{
+ switch (which_alternative)
+ {
+ case 0:
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp%z0\\t%0\";
+ else
+ return \"fst%z0\\t%0\";
+ case 1:
+ return \"fstp%z2\\t%2\;fld%z2\\t%2\";
+ }
+ abort ();
+}"
+ [(set_attr "type" "fmov,multi")])
-(define_insn ""
- [(set (match_operand:DF 0 "register_operand" "=f,f")
- (float:DF (match_operand:SI 1 "nonimmediate_operand" "m,!r")))
- (clobber (match_operand:SI 2 "memory_operand" "m,m"))]
+(define_insn "*truncxfdf2_2"
+ [(set (match_operand:DF 0 "memory_operand" "=m")
+ (float_truncate:DF
+ (match_operand:XF 1 "register_operand" "f")))]
"TARGET_80387"
- "#")
+ "*
+{
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return \"fstp%z0\\t%0\";
+ else
+ return \"fst%z0\\t%0\";
+}"
+ [(set_attr "type" "fmov")])
(define_split
- [(set (match_operand:DF 0 "register_operand" "")
- (float:DF (match_operand:SI 1 "memory_operand" "")))
- (clobber (match_operand:SI 2 "memory_operand" ""))]
+ [(set (match_operand:DF 0 "memory_operand" "")
+ (float_truncate:DF
+ (match_operand:XF 1 "register_operand" "")))
+ (clobber (match_operand:DF 2 "memory_operand" ""))]
"TARGET_80387 && reload_completed"
- [(set (match_dup 0)
- (float:DF (match_dup 1)))]
+ [(set (match_dup 0) (subreg:DF (match_dup 1) 0))]
"")
(define_split
[(set (match_operand:DF 0 "register_operand" "")
- (float:DF (match_operand:SI 1 "register_operand" "")))
- (clobber (match_operand:SI 2 "memory_operand" ""))]
+ (float_truncate:DF
+ (match_operand:XF 1 "register_operand" "")))
+ (clobber (match_operand:DF 2 "memory_operand" ""))]
"TARGET_80387 && reload_completed"
- [(set (match_dup 2)
- (match_dup 1))
- (set (match_dup 0)
- (float:DF (match_dup 2)))]
+ [(set (match_dup 2) (subreg:DF (match_dup 1) 0))
+ (set (match_dup 0) (match_dup 2))]
"")
+\f
+;; %%% Break up all these bad boys.
-(define_insn ""
- [(set (match_operand:DF 0 "register_operand" "=f")
- (float:DF (match_operand:SI 1 "memory_operand" "m")))]
+;; Signed conversion to DImode.
+
+(define_expand "fix_truncxfdi2"
+ [(parallel [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (fix:DI (match_operand:XF 1 "register_operand" "")))
+ (clobber (match_dup 2))
+ (clobber (match_dup 3))
+ (clobber (match_scratch:SI 4 ""))
+ (clobber (match_scratch:XF 5 ""))])]
"TARGET_80387"
- "* return AS1 (fild%z1,%1);"
- [(set_attr "type" "fpop")])
+ "operands[2] = assign_386_stack_local (SImode, 0);
+ operands[3] = assign_386_stack_local (DImode, 1);")
-(define_expand "floathidf2"
- [(parallel [(set (match_operand:DF 0 "register_operand" "")
- (float:DF (match_operand:HI 1 "nonimmediate_operand" "")))
- (clobber (match_dup 2))])]
+(define_expand "fix_truncdfdi2"
+ [(parallel [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (fix:DI (match_operand:DF 1 "register_operand" "")))
+ (clobber (match_dup 2))
+ (clobber (match_dup 3))
+ (clobber (match_scratch:SI 4 ""))
+ (clobber (match_scratch:XF 5 ""))])]
"TARGET_80387"
- "operands[2] = assign_386_stack_local (HImode, 0);")
+ "operands[2] = assign_386_stack_local (SImode, 0);
+ operands[3] = assign_386_stack_local (DImode, 1);")
-(define_insn ""
- [(set (match_operand:DF 0 "register_operand" "=f,f")
- (float:DF (match_operand:HI 1 "nonimmediate_operand" "m,!r")))
- (clobber (match_operand:HI 2 "memory_operand" "m,m"))]
+(define_expand "fix_truncsfdi2"
+ [(parallel [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (fix:DI (match_operand:SF 1 "register_operand" "")))
+ (clobber (match_dup 2))
+ (clobber (match_dup 3))
+ (clobber (match_scratch:SI 4 ""))
+ (clobber (match_scratch:XF 5 ""))])]
"TARGET_80387"
- "#")
+ "operands[2] = assign_386_stack_local (SImode, 0);
+ operands[3] = assign_386_stack_local (DImode, 1);")
+
+(define_insn "*fix_truncdi_1"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "m,?r")
+ (fix:DI (match_operand 1 "register_operand" "f,f")))
+ (clobber (match_operand:SI 2 "memory_operand" "o,o"))
+ (clobber (match_operand:DI 3 "memory_operand" "m,m"))
+ (clobber (match_scratch:SI 4 "=&r,=&r"))
+ (clobber (match_scratch:XF 5 "=f,f"))]
+ "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[1]))"
+ "* return output_fix_trunc (insn, operands);"
+ [(set_attr "type" "multi")])
-(define_split
- [(set (match_operand:DF 0 "register_operand" "")
- (float:DF (match_operand:HI 1 "memory_operand" "")))
- (clobber (match_operand:HI 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 0)
- (float:DF (match_dup 1)))]
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (fix:DI (match_operand 1 "register_operand" "")))
+ (clobber (match_operand:SI 2 "memory_operand" ""))
+ (clobber (match_operand:DI 3 "memory_operand" ""))
+ (clobber (match_scratch:SI 4 ""))
+ (clobber (match_scratch:XF 5 ""))]
+ "reload_completed && !reg_overlap_mentioned_p (operands[4], operands[3])"
+ [(parallel [(set (match_dup 3) (fix:DI (match_dup 1)))
+ (clobber (match_dup 2))
+ (clobber (match_dup 3))
+ (clobber (match_dup 4))
+ (clobber (match_dup 5))])
+ (set (match_dup 0) (match_dup 3))]
"")
-(define_split
- [(set (match_operand:DF 0 "register_operand" "")
- (float:DF (match_operand:HI 1 "register_operand" "")))
- (clobber (match_operand:HI 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 2)
- (match_dup 1))
- (set (match_dup 0)
- (float:DF (match_dup 2)))]
- "")
+;; Signed conversion to SImode.
-(define_insn ""
- [(set (match_operand:DF 0 "register_operand" "=f")
- (float:DF (match_operand:HI 1 "memory_operand" "m")))]
+(define_expand "fix_truncxfsi2"
+ [(parallel [(set (match_operand:SI 0 "nonimmediate_operand" "")
+ (fix:SI (match_operand:XF 1 "register_operand" "")))
+ (clobber (match_dup 2))
+ (clobber (match_dup 3))
+ (clobber (match_scratch:SI 4 ""))])]
"TARGET_80387"
- "* return AS1 (fild%z1,%1);"
- [(set_attr "type" "fpop")])
+ "operands[2] = assign_386_stack_local (SImode, 0);
+ operands[3] = assign_386_stack_local (SImode, 1);")
-(define_expand "floatdidf2"
- [(parallel [(set (match_operand:DF 0 "register_operand" "")
- (float:DF (match_operand:DI 1 "nonimmediate_operand" "")))
- (clobber (match_dup 2))])]
+(define_expand "fix_truncdfsi2"
+ [(parallel [(set (match_operand:SI 0 "nonimmediate_operand" "")
+ (fix:SI (match_operand:DF 1 "register_operand" "")))
+ (clobber (match_dup 2))
+ (clobber (match_dup 3))
+ (clobber (match_scratch:SI 4 ""))])]
"TARGET_80387"
- "operands[2] = assign_386_stack_local (DImode, 0);")
+ "operands[2] = assign_386_stack_local (SImode, 0);
+ operands[3] = assign_386_stack_local (SImode, 1);")
-(define_insn ""
- [(set (match_operand:DF 0 "register_operand" "=f,f")
- (float:DF (match_operand:DI 1 "nonimmediate_operand" "m,!r")))
- (clobber (match_operand:DI 2 "memory_operand" "m,o"))]
+(define_expand "fix_truncsfsi2"
+ [(parallel [(set (match_operand:SI 0 "nonimmediate_operand" "")
+ (fix:SI (match_operand:SF 1 "register_operand" "")))
+ (clobber (match_dup 2))
+ (clobber (match_dup 3))
+ (clobber (match_scratch:SI 4 ""))])]
"TARGET_80387"
- "#")
-
-(define_split
- [(set (match_operand:DF 0 "register_operand" "")
- (float:DF (match_operand:DI 1 "memory_operand" "")))
- (clobber (match_operand:DI 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 0)
- (float:DF (match_dup 1)))]
- "")
+ "operands[2] = assign_386_stack_local (SImode, 0);
+ operands[3] = assign_386_stack_local (SImode, 1);")
+
+(define_insn "*fix_truncsi_1"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "m,?r")
+ (fix:SI (match_operand 1 "register_operand" "f,f")))
+ (clobber (match_operand:SI 2 "memory_operand" "o,o"))
+ (clobber (match_operand:SI 3 "memory_operand" "m,m"))
+ (clobber (match_scratch:SI 4 "=&r,r"))]
+ "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[1]))"
+ "* return output_fix_trunc (insn, operands);"
+ [(set_attr "type" "multi")])
-(define_split
- [(set (match_operand:DF 0 "register_operand" "")
- (float:DF (match_operand:DI 1 "register_operand" "")))
- (clobber (match_operand:DI 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 2)
- (match_dup 1))
- (set (match_dup 0)
- (float:DF (match_dup 2)))]
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (fix:SI (match_operand 1 "register_operand" "")))
+ (clobber (match_operand:SI 2 "memory_operand" ""))
+ (clobber (match_operand:SI 3 "memory_operand" ""))
+ (clobber (match_scratch:SI 4 ""))]
+ "reload_completed"
+ [(parallel [(set (match_dup 3) (fix:SI (match_dup 1)))
+ (clobber (match_dup 2))
+ (clobber (match_dup 3))
+ (clobber (match_dup 4))])
+ (set (match_dup 0) (match_dup 3))]
"")
-(define_insn ""
- [(set (match_operand:DF 0 "register_operand" "=f")
- (float:DF (match_operand:DI 1 "memory_operand" "m")))]
+;; %% Not used yet.
+(define_insn "x86_fnstcw_1"
+ [(set (match_operand:HI 0 "memory_operand" "m")
+ (unspec [(reg:HI 18)] 11))]
"TARGET_80387"
- "* return AS1 (fild%z1,%1);"
- [(set_attr "type" "fpop")])
+ "fnstcw\\t%0"
+ [(set_attr "length_opcode" "2")
+ (set_attr "ppro_uops" "few")])
-(define_expand "floatsixf2"
- [(parallel [(set (match_operand:XF 0 "register_operand" "")
- (float:XF (match_operand:SI 1 "nonimmediate_operand" "")))
- (clobber (match_dup 2))])]
+(define_insn "x86_fldcw_1"
+ [(set (reg:HI 18)
+ (unspec [(match_operand:HI 0 "memory_operand" "m")] 12))]
"TARGET_80387"
- "operands[2] = assign_386_stack_local (SImode, 0);")
+ "fldcw\\t%0"
+ [(set_attr "length_opcode" "2")
+ (set_attr "ppro_uops" "few")])
+\f
+;; Conversion between fixed point and floating point.
-(define_insn ""
- [(set (match_operand:XF 0 "register_operand" "=f,f")
- (float:XF (match_operand:SI 1 "nonimmediate_operand" "m,!r")))
- (clobber (match_operand:SI 2 "memory_operand" "m,m"))]
+;; Even though we only accept memory inputs, the backend _really_
+;; wants to be able to do this between registers.
+
+(define_insn "floatsisf2"
+ [(set (match_operand:SF 0 "register_operand" "=f,f")
+ (float:SF (match_operand:SI 1 "nonimmediate_operand" "m,r")))]
"TARGET_80387"
- "#")
+ "@
+ fild%z1\\t%1
+ #"
+ [(set_attr "type" "fmov,multi")
+ (set_attr "fp_int_src" "true")])
-(define_split
- [(set (match_operand:XF 0 "register_operand" "")
- (float:XF (match_operand:SI 1 "memory_operand" "")))
- (clobber (match_operand:SI 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 0)
- (float:XF (match_dup 1)))]
- "")
+(define_insn "floatdisf2"
+ [(set (match_operand:SF 0 "register_operand" "=f,f")
+ (float:SF (match_operand:DI 1 "nonimmediate_operand" "m,r")))]
+ "TARGET_80387"
+ "@
+ fild%z1\\t%1
+ #"
+ [(set_attr "type" "fmov,multi")
+ (set_attr "fp_int_src" "true")])
-(define_split
- [(set (match_operand:XF 0 "register_operand" "")
- (float:XF (match_operand:SI 1 "register_operand" "")))
- (clobber (match_operand:SI 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 2)
- (match_dup 1))
- (set (match_dup 0)
- (float:XF (match_dup 2)))]
- "")
+(define_insn "floatsidf2"
+ [(set (match_operand:DF 0 "register_operand" "=f,f")
+ (float:DF (match_operand:SI 1 "nonimmediate_operand" "m,r")))]
+ "TARGET_80387"
+ "@
+ fild%z1\\t%1
+ #"
+ [(set_attr "type" "fmov,multi")
+ (set_attr "fp_int_src" "true")])
-(define_insn ""
- [(set (match_operand:XF 0 "register_operand" "=f")
- (float:XF (match_operand:SI 1 "memory_operand" "m")))]
+(define_insn "floatdidf2"
+ [(set (match_operand:DF 0 "register_operand" "=f,f")
+ (float:DF (match_operand:DI 1 "nonimmediate_operand" "m,r")))]
"TARGET_80387"
- "* return AS1 (fild%z1,%1);"
- [(set_attr "type" "fpop")])
+ "@
+ fild%z1\\t%1
+ #"
+ [(set_attr "type" "fmov,multi")
+ (set_attr "fp_int_src" "true")])
-(define_expand "floathixf2"
- [(parallel [(set (match_operand:XF 0 "register_operand" "")
- (float:XF (match_operand:HI 1 "nonimmediate_operand" "")))
- (clobber (match_dup 2))])]
+(define_insn "floatsixf2"
+ [(set (match_operand:XF 0 "register_operand" "=f,f")
+ (float:XF (match_operand:SI 1 "nonimmediate_operand" "m,r")))]
"TARGET_80387"
- "operands[2] = assign_386_stack_local (HImode, 0);")
+ "@
+ fild%z1\\t%1
+ #"
+ [(set_attr "type" "fmov,multi")
+ (set_attr "fp_int_src" "true")])
-(define_insn ""
+(define_insn "floatdixf2"
[(set (match_operand:XF 0 "register_operand" "=f,f")
- (float:XF (match_operand:HI 1 "nonimmediate_operand" "m,!r")))
- (clobber (match_operand:HI 2 "memory_operand" "m,m"))]
+ (float:XF (match_operand:DI 1 "nonimmediate_operand" "m,r")))]
"TARGET_80387"
- "#")
+ "@
+ fild%z1\\t%1
+ #"
+ [(set_attr "type" "fmov,multi")
+ (set_attr "fp_int_src" "true")])
+;; %%% Kill these when reload knows how to do it.
(define_split
- [(set (match_operand:XF 0 "register_operand" "")
- (float:XF (match_operand:HI 1 "memory_operand" "")))
- (clobber (match_operand:HI 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 0)
- (float:XF (match_dup 1)))]
- "")
+ [(set (match_operand 0 "register_operand" "")
+ (float (match_operand:SI 1 "register_operand" "")))]
+ "reload_completed && FLOAT_MODE_P (GET_MODE (operands[0]))"
+ [(set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 1))
+ (set (match_dup 0) (match_dup 2))
+ (parallel [(set (match_dup 1) (mem:SI (reg:SI 7)))
+ (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4)))])]
+ "operands[2] = gen_rtx_FLOAT (GET_MODE (operands[0]),
+ gen_rtx_MEM (SImode, stack_pointer_rtx));")
(define_split
- [(set (match_operand:XF 0 "register_operand" "")
- (float:XF (match_operand:HI 1 "register_operand" "")))
- (clobber (match_operand:HI 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 2)
- (match_dup 1))
- (set (match_dup 0)
- (float:XF (match_dup 2)))]
- "")
-
-(define_insn ""
- [(set (match_operand:XF 0 "register_operand" "=f")
- (float:XF (match_operand:HI 1 "memory_operand" "m")))]
- "TARGET_80387"
- "* return AS1 (fild%z1,%1);"
- [(set_attr "type" "fpop")])
+ [(set (match_operand 0 "register_operand" "")
+ (float (match_operand:DI 1 "nonmemory_operand" "")))]
+ "reload_completed && FLOAT_MODE_P (GET_MODE (operands[0]))"
+ [(set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 2))
+ (set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 1))
+ (set (match_dup 0) (match_dup 3))
+ (parallel [(set (match_dup 1) (mem:SI (reg:SI 7)))
+ (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4)))])
+ (parallel [(set (match_dup 2) (mem:SI (reg:SI 7)))
+ (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4)))])]
+ "split_di (operands+1, 1, operands+1, operands+2);
+ operands[3] = gen_rtx_FLOAT (GET_MODE (operands[0]),
+ gen_rtx_MEM (DImode, stack_pointer_rtx));")
+\f
+;; Add instructions
-(define_expand "floatdixf2"
- [(parallel [(set (match_operand:XF 0 "register_operand" "")
- (float:XF (match_operand:DI 1 "nonimmediate_operand" "")))
- (clobber (match_dup 2))])]
- "TARGET_80387"
- "operands[2] = assign_386_stack_local (DImode, 0);")
+;; %%% define_expand from the very first?
+;; %%% splits for addsidi3
+; [(set (match_operand:DI 0 "nonimmediate_operand" "")
+; (plus:DI (match_operand:DI 1 "general_operand" "")
+; (zero_extend:DI (match_operand:SI 2 "general_operand" ""))))]
-(define_insn ""
- [(set (match_operand:XF 0 "register_operand" "=f,f")
- (float:XF (match_operand:DI 1 "nonimmediate_operand" "m,!r")))
- (clobber (match_operand:DI 2 "memory_operand" "m,o"))]
- "TARGET_80387"
+(define_insn "adddi3"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=r,o")
+ (plus:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:DI 2 "general_operand" "roiF,riF")))
+ (clobber (reg:CC 17))]
+ ""
"#")
(define_split
- [(set (match_operand:XF 0 "register_operand" "")
- (float:XF (match_operand:DI 1 "memory_operand" "")))
- (clobber (match_operand:DI 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 0)
- (float:XF (match_dup 1)))]
- "")
-
-(define_split
- [(set (match_operand:XF 0 "register_operand" "")
- (float:XF (match_operand:DI 1 "register_operand" "")))
- (clobber (match_operand:DI 2 "memory_operand" ""))]
- "TARGET_80387 && reload_completed"
- [(set (match_dup 2)
- (match_dup 1))
- (set (match_dup 0)
- (float:XF (match_dup 2)))]
- "")
+ [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (plus:DI (match_operand:DI 1 "general_operand" "")
+ (match_operand:DI 2 "general_operand" "")))
+ (clobber (reg:CC 17))]
+ "cse_not_expected"
+ [(parallel [(set (reg:CC 17) (plus:CC (match_dup 1) (match_dup 2)))
+ (set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))])
+ (parallel [(set (match_dup 3)
+ (plus:SI (match_dup 4)
+ (plus:SI (match_dup 5)
+ (ltu:SI (reg:CC 17) (const_int 0)))))
+ (clobber (reg:CC 17))])]
+ "split_di (operands+0, 1, operands+0, operands+3);
+ split_di (operands+1, 1, operands+1, operands+4);
+ split_di (operands+2, 1, operands+2, operands+5);")
+
+(define_insn "addcsi3"
+ [(set (reg:CC 17) (plus:CC (match_operand:SI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:SI 2 "general_operand" "ri,rm")))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
+ (plus:SI (match_dup 1) (match_dup 2)))]
+ ""
+ "add{l}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_insn "addxsi3"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
+ (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0")
+ (plus:SI (match_operand:SI 2 "general_operand" "ri,rm")
+ (ltu:SI (reg:CC 17) (const_int 0)))))
+ (clobber (reg:CC 17))]
+ ""
+ "adc{l}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")
+ (set_attr "pent_pair" "pu")
+ (set_attr "ppro_uops" "few")])
-(define_insn ""
- [(set (match_operand:XF 0 "register_operand" "=f")
- (float:XF (match_operand:DI 1 "memory_operand" "m")))]
- "TARGET_80387"
- "* return AS1 (fild%z1,%1);"
- [(set_attr "type" "fpop")])
-\f
-;;- add instructions
+(define_expand "addsi3"
+ [(parallel [(set (match_operand:SI 0 "nonimmediate_operand" "")
+ (plus:SI (match_operand:SI 1 "nonimmediate_operand" "")
+ (match_operand:SI 2 "general_operand" "")))
+ (clobber (reg:CC 17))])]
+ ""
+ "ix86_expand_binary_operator (PLUS, SImode, operands); DONE;")
-(define_insn "*addsidi3_1"
- [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,r,o,!&r,!r,o,!o")
- (plus:DI (match_operand:DI 1 "general_operand" "0,0,0,o,riF,riF,o")
- (zero_extend:DI (match_operand:SI 2 "general_operand" "o,ri,ri,roi,roi,ri,ri"))))
- (clobber (match_scratch:SI 3 "=X,X,X,X,X,X,&r"))]
+(define_insn "*addsi_0"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (plus:SI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "nonmemory_operand" "rni")))]
""
"*
{
- rtx low[3], high[3], xops[7];
-
- CC_STATUS_INIT;
-
- split_di (operands, 2, low, high);
- high[2] = const0_rtx;
- low[2] = operands[2];
-
- if (!rtx_equal_p (operands[0], operands[1]))
- {
- xops[0] = high[0];
- xops[1] = low[0];
- xops[2] = high[1];
- xops[3] = low[1];
-
- if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
- {
- output_asm_insn (AS2 (mov%L1,%3,%1), xops);
- output_asm_insn (AS2 (mov%L0,%2,%0), xops);
- }
- else
- {
- xops[4] = high[2];
- xops[5] = low[2];
- xops[6] = operands[3];
- output_asm_insn (AS2 (mov%L6,%3,%6), xops);
- output_asm_insn (AS2 (add%L6,%5,%6), xops);
- output_asm_insn (AS2 (mov%L1,%6,%1), xops);
- output_asm_insn (AS2 (mov%L6,%2,%6), xops);
- output_asm_insn (AS2 (adc%L6,%4,%6), xops);
- output_asm_insn (AS2 (mov%L0,%6,%0), xops);
- RET;
- }
- }
-
- output_asm_insn (AS2 (add%L0,%2,%0), low);
- output_asm_insn (AS2 (adc%L0,%2,%0), high);
- cc_status.value1 = high[0];
- cc_status.flags = CC_NO_OVERFLOW;
- RET;
+ operands[1] = SET_SRC (PATTERN (insn));
+ return \"lea{l}\\t{%a1, %0|%0, %a1}\";
}"
- [(set_attr "type" "binary")])
+ [(set_attr "type" "lea")])
-(define_insn "addsidi3_2"
- [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,r,o,&r,!&r,&r,o,o,!o")
- (plus:DI (zero_extend:DI (match_operand:SI 2 "general_operand" "o,ri,ri,o,o,ri,ri,i,r"))
- (match_operand:DI 1 "general_operand" "0,0,0,iF,ro,roiF,riF,o,o")))
- (clobber (match_scratch:SI 3 "=X,X,X,X,X,X,X,&r,&r"))]
- ""
+(define_insn "*addsi_1"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=r,rm,r")
+ (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,r")
+ (match_operand:SI 2 "general_operand" "rmni,rni,rni")))
+ (clobber (reg:CC 17))]
+ "ix86_binary_operator_ok (PLUS, SImode, operands)"
"*
{
- rtx low[3], high[3], xops[7];
-
- CC_STATUS_INIT;
-
- split_di (operands, 2, low, high);
- high[2] = const0_rtx;
- low[2] = operands[2];
-
- if (!rtx_equal_p (operands[0], operands[1]))
+ switch (get_attr_type (insn))
{
- xops[0] = high[0];
- xops[1] = low[0];
- xops[2] = high[1];
- xops[3] = low[1];
+ case TYPE_LEA:
+ operands[2] = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
+ return \"lea{l}\\t{%a2, %0|%0, %a2}\";
- if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
- {
- if (rtx_equal_p (low[0], operands[2]))
- {
- output_asm_insn (AS2 (mov%L0,%2,%0), high);
- output_asm_insn (AS2 (add%L0,%1,%0), low);
- output_asm_insn (AS2 (adc%L0,%1,%0), high);
- RET;
- }
- if (rtx_equal_p (high[0], operands[2]))
- {
- if (GET_CODE (operands[0]) != MEM)
- {
- output_asm_insn (AS2 (mov%L0,%2,%0), low);
- output_asm_insn (AS2 (mov%L0,%2,%0), high);
- output_asm_insn (AS2 (add%L0,%1,%0), low);
- output_asm_insn (AS2 (adc%L0,%1,%0), high);
- }
- else
- {
- /* It's too late to ask for a scratch now - but this
- will probably not happen too often. */
- output_asm_insn (AS2 (add%L1,%2,%1), low);
- output_asm_insn (AS2 (mov%L0,%1,%0), low);
- output_asm_insn (AS2 (mov%L1,%2,%1), low);
- output_asm_insn (AS2 (mov%L0,%2,%0), high);
- output_asm_insn (AS2 (adc%L0,%1,%0), high);
- output_asm_insn (AS2 (sub%L1,%0,%1), low);
- output_asm_insn (AS1 (neg%L1,%1), low);
- }
- RET;
- }
- output_asm_insn (AS2 (mov%L1,%3,%1), xops);
- output_asm_insn (AS2 (mov%L0,%2,%0), xops);
- }
+ case TYPE_INCDEC:
+ if (! rtx_equal_p (operands[0], operands[1]))
+ abort ();
+ if (operands[2] == const1_rtx)
+ return \"inc{l}\\t%0\";
+ else if (operands[2] == constm1_rtx)
+ return \"dec{l}\\t%0\";
else
- {
- xops[4] = high[2];
- xops[5] = low[2];
- xops[6] = operands[3];
- output_asm_insn (AS2 (mov%L6,%3,%6), xops);
- output_asm_insn (AS2 (add%L6,%5,%6), xops);
- output_asm_insn (AS2 (mov%L1,%6,%1), xops);
- output_asm_insn (AS2 (mov%L6,%2,%6), xops);
- output_asm_insn (AS2 (adc%L6,%4,%6), xops);
- output_asm_insn (AS2 (mov%L0,%6,%0), xops);
- RET;
- }
- }
+ abort();
- output_asm_insn (AS2 (add%L0,%2,%0), low);
- output_asm_insn (AS2 (adc%L0,%2,%0), high);
- cc_status.value1 = high[0];
- cc_status.flags = CC_NO_OVERFLOW;
- RET;
-}"
- [(set_attr "type" "binary")])
+ default:
+ if (! rtx_equal_p (operands[0], operands[1]))
+ abort ();
-(define_insn "adddi3"
- [(set (match_operand:DI 0 "general_operand" "=&r,&ro,!r,o,!&r,!o,!o")
- (plus:DI (match_operand:DI 1 "general_operand" "%0,0,0,0iF,or,riF,o")
- (match_operand:DI 2 "general_operand" "o,riF,0,or,or,oriF,o")))
- (clobber (match_scratch:SI 3 "=X,X,X,&r,X,&r,&r"))]
- ""
+ /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'.
+ Exceptions: -128 encodes smaller than 128, so swap sign and op. */
+ if (GET_CODE (operands[2]) == CONST_INT
+ && (INTVAL (operands[2]) == 128
+ || (INTVAL (operands[2]) < 0
+ && INTVAL (operands[2]) != -128)))
+ {
+ operands[2] = GEN_INT (-INTVAL (operands[2]));
+ return \"sub{l}\\t{%2, %0|%0, %2}\";
+ }
+ return \"add{l}\\t{%2, %0|%0, %2}\";
+ }
+}"
+ [(set (attr "type")
+ (cond [(eq_attr "alternative" "2")
+ (const_string "lea")
+ ; Current assemblers are broken and do not allow @GOTOFF in
+ ; ought but a memory context.
+ (match_operand:SI 2 "pic_symbolic_operand" "")
+ (const_string "lea")
+ (match_operand:SI 2 "incdec_operand" "")
+ (const_string "incdec")
+ ]
+ (const_string "alu")))])
+
+(define_insn "*addsi_2"
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:SI 2 "general_operand" "rmni,rni"))
+ (const_int 0)))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=r,rm")
+ (plus:SI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (PLUS, SImode, operands)
+ /* Current assemblers are broken and do not allow @GOTOFF in
+ ought but a memory context. */
+ && ! pic_symbolic_operand (operands[2], VOIDmode)"
"*
{
- rtx low[3], high[3], xops[7], temp;
-
- CC_STATUS_INIT;
-
- if (rtx_equal_p (operands[0], operands[2]))
- {
- temp = operands[1];
- operands[1] = operands[2];
- operands[2] = temp;
- }
-
- split_di (operands, 3, low, high);
- if (!rtx_equal_p (operands[0], operands[1]))
+ switch (get_attr_type (insn))
{
- xops[0] = high[0];
- xops[1] = low[0];
- xops[2] = high[1];
- xops[3] = low[1];
-
- if (GET_CODE (operands[0]) != MEM)
- {
- output_asm_insn (AS2 (mov%L1,%3,%1), xops);
- output_asm_insn (AS2 (mov%L0,%2,%0), xops);
- }
+ case TYPE_INCDEC:
+ if (! rtx_equal_p (operands[0], operands[1]))
+ abort ();
+ if (operands[2] == const1_rtx)
+ return \"inc{l}\\t%0\";
+ else if (operands[2] == constm1_rtx)
+ return \"dec{l}\\t%0\";
else
- {
- xops[4] = high[2];
- xops[5] = low[2];
- xops[6] = operands[3];
- output_asm_insn (AS2 (mov%L6,%3,%6), xops);
- output_asm_insn (AS2 (add%L6,%5,%6), xops);
- output_asm_insn (AS2 (mov%L1,%6,%1), xops);
- output_asm_insn (AS2 (mov%L6,%2,%6), xops);
- output_asm_insn (AS2 (adc%L6,%4,%6), xops);
- output_asm_insn (AS2 (mov%L0,%6,%0), xops);
- RET;
- }
- }
-
- cc_status.value1 = high[0];
- cc_status.flags = CC_NO_OVERFLOW;
-
- if (GET_CODE (operands[3]) == REG && GET_CODE (operands[2]) != REG)
- {
- xops[0] = high[0];
- xops[1] = low[0];
- xops[2] = high[2];
- xops[3] = low[2];
- xops[4] = operands[3];
-
- output_asm_insn (AS2 (mov%L4,%3,%4), xops);
- output_asm_insn (AS2 (add%L1,%4,%1), xops);
- output_asm_insn (AS2 (mov%L4,%2,%4), xops);
- output_asm_insn (AS2 (adc%L0,%4,%0), xops);
- }
+ abort();
- else if (GET_CODE (low[2]) != CONST_INT || INTVAL (low[2]) != 0)
- {
- output_asm_insn (AS2 (add%L0,%2,%0), low);
- output_asm_insn (AS2 (adc%L0,%2,%0), high);
+ default:
+ if (! rtx_equal_p (operands[0], operands[1]))
+ abort ();
+ /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'.
+ Exceptions: -128 encodes smaller than 128, so swap sign and op. */
+ if (GET_CODE (operands[2]) == CONST_INT
+ && (INTVAL (operands[2]) == 128
+ || (INTVAL (operands[2]) < 0
+ && INTVAL (operands[2]) != -128)))
+ {
+ operands[2] = GEN_INT (-INTVAL (operands[2]));
+ return \"sub{l}\\t{%2, %0|%0, %2}\";
+ }
+ return \"add{l}\\t{%2, %0|%0, %2}\";
}
-
- else
- output_asm_insn (AS2 (add%L0,%2,%0), high);
-
- RET;
}"
- [(set_attr "type" "binary")])
-
-;; On a 486, it is faster to do movl/addl than to do a single leal if
-;; operands[1] and operands[2] are both registers.
-
-(define_expand "addsi3"
- [(set (match_operand:SI 0 "nonimmediate_operand" "")
- (plus:SI (match_operand:SI 1 "nonimmediate_operand" "")
- (match_operand:SI 2 "general_operand" "")))]
+ [(set (attr "type")
+ (if_then_else (match_operand:SI 2 "incdec_operand" "")
+ (const_string "incdec")
+ (const_string "alu")))])
+
+(define_insn "*addsi_3"
+ [(set (reg:CC 17)
+ (compare:CC (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:SI 2 "general_operand" "rmni,rni"))
+ (const_int 0)))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=r,rm")
+ (plus:SI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (PLUS, SImode, operands)
+ /* Current assemblers are broken and do not allow @GOTOFF in
+ ought but a memory context. */
+ && ! pic_symbolic_operand (operands[2], VOIDmode)"
+ "add{l}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+;; %%% Conditionally split these post-reload for better scheduling.
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (plus:SI (plus:SI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "register_operand" "%r"))
+ (match_operand:SI 3 "immediate_operand" "i")))]
""
- "IX86_EXPAND_BINARY_OPERATOR (PLUS, SImode, operands);")
+ "*
+{
+ operands[1] = SET_SRC (PATTERN (insn));
+ return \"lea{l}\\t{%a1, %0|%0, %a1}\";
+}"
+ [(set_attr "type" "lea")])
(define_insn ""
- [(set (match_operand:SI 0 "nonimmediate_operand" "=r,rm,r")
- (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,r")
- (match_operand:SI 2 "general_operand" "rmi,ri,ri")))]
- "ix86_binary_operator_ok (PLUS, SImode, operands)"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (plus:SI (mult:SI (match_operand:SI 1 "reg_no_sp_operand" "r")
+ (match_operand:SI 2 "const248_operand" "I"))
+ (match_operand:SI 3 "nonmemory_operand" "ir")))]
+ ""
"*
{
- if (REG_P (operands[0]) && REG_P (operands[1])
- && (REG_P (operands[2]) || CONSTANT_P (operands[2]))
- && REGNO (operands[0]) != REGNO (operands[1]))
- {
- if (REG_P (operands[2]) && REGNO (operands[0]) == REGNO (operands[2]))
- return AS2 (add%L0,%1,%0);
-
- if (operands[2] == stack_pointer_rtx)
- {
- rtx temp;
-
- temp = operands[1];
- operands[1] = operands[2];
- operands[2] = temp;
- }
-
- if (operands[2] != stack_pointer_rtx)
- {
- CC_STATUS_INIT;
- operands[1] = SET_SRC (PATTERN (insn));
- return AS2 (lea%L0,%a1,%0);
- }
- }
-
- if (!rtx_equal_p (operands[0], operands[1]))
- output_asm_insn (AS2 (mov%L0,%1,%0), operands);
-
- if (operands[2] == const1_rtx)
- return AS1 (inc%L0,%0);
-
- if (operands[2] == constm1_rtx)
- return AS1 (dec%L0,%0);
-
- /* subl $-128,%ebx is smaller than addl $128,%ebx. */
- if (GET_CODE (operands[2]) == CONST_INT
- && INTVAL (operands[2]) == 128)
- {
- /* This doesn't compute the carry bit in the same way
- * as add%L0, but we use inc and dec above and they
- * don't set the carry bit at all. If inc/dec don't need
- * a CC_STATUS_INIT, this doesn't either... */
- operands[2] = GEN_INT (-128);
- return AS2 (sub%L0,%2,%0);
- }
-
- return AS2 (add%L0,%2,%0);
+ operands[1] = SET_SRC (PATTERN (insn));
+ return \"lea{l}\\t{%a1, %0|%0, %a1}\";
}"
- [(set_attr "type" "binary")])
-
-;; addsi3 is faster, so put this after.
+ [(set_attr "type" "lea")])
-(define_insn "movsi_lea"
+(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
- (match_operand:QI 1 "address_operand" "p"))]
+ (plus:SI (plus:SI (mult:SI (match_operand:SI 1 "reg_no_sp_operand" "r")
+ (match_operand:SI 2 "const248_operand" "I"))
+ (match_operand 3 "register_operand" "%r"))
+ (match_operand:SI 4 "immediate_operand" "i")))]
""
"*
{
- /* Adding a constant to a register is faster with an add. */
- /* ??? can this ever happen? */
- if (GET_CODE (operands[1]) == PLUS
- && GET_CODE (XEXP (operands[1], 1)) == CONST_INT
- && rtx_equal_p (operands[0], XEXP (operands[1], 0)))
- {
- operands[1] = XEXP (operands[1], 1);
-
- if (operands[1] == const1_rtx)
- return AS1 (inc%L0,%0);
-
- if (operands[1] == constm1_rtx)
- return AS1 (dec%L0,%0);
-
- return AS2 (add%L0,%1,%0);
- }
-
- CC_STATUS_INIT;
- return AS2 (lea%L0,%a1,%0);
+ operands[1] = SET_SRC (PATTERN (insn));
+ return \"lea{l}\\t{%a1, %0|%0, %a1}\";
}"
[(set_attr "type" "lea")])
-;; ??? `lea' here, for three operand add? If leaw is used, only %bx,
-;; %si and %di can appear in SET_SRC, and output_asm_insn might not be
-;; able to handle the operand. But leal always works?
-
(define_expand "addhi3"
- [(set (match_operand:HI 0 "general_operand" "")
- (plus:HI (match_operand:HI 1 "nonimmediate_operand" "")
- (match_operand:HI 2 "general_operand" "")))]
+ [(parallel [(set (match_operand:HI 0 "general_operand" "")
+ (plus:HI (match_operand:HI 1 "nonimmediate_operand" "")
+ (match_operand:HI 2 "general_operand" "")))
+ (clobber (reg:CC 17))])]
""
- "IX86_EXPAND_BINARY_OPERATOR (PLUS, HImode, operands);")
+ "ix86_expand_binary_operator (PLUS, HImode, operands); DONE;")
-(define_insn ""
- [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r,?r")
- (plus:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0,r")
- (match_operand:HI 2 "general_operand" "ri,rm,ri")))]
+;; %%% After Dave's SUBREG_BYTE stuff goes in, re-enable incb %ah
+;; type optimizations enabled by define-splits. This is not important
+;; for PII, and in fact harmful because of partial register stalls.
+
+(define_insn "*addhi_1"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r")
+ (plus:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:HI 2 "general_operand" "ri,rm")))
+ (clobber (reg:CC 17))]
"ix86_binary_operator_ok (PLUS, HImode, operands)"
"*
{
- if (REG_P (operands[0]) && REG_P (operands[1])
- && (REG_P (operands[2]) || CONSTANT_P (operands[2]))
- && REGNO (operands[0]) != REGNO (operands[1]))
+ switch (get_attr_type (insn))
{
- if (operands[2] == stack_pointer_rtx)
- abort ();
+ case TYPE_INCDEC:
+ if (operands[2] == const1_rtx)
+ return \"inc{w}\\t%0\";
+ else if (operands[2] == constm1_rtx
+ || (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) == 65535))
+ return \"dec{w}\\t%0\";
+ abort();
- CC_STATUS_INIT;
- operands[1]
- = gen_rtx_PLUS (SImode,
- gen_rtx_REG (SImode, REGNO (operands[1])),
- (! REG_P (operands[2])
- ? operands[2]
- : gen_rtx_REG (SImode, REGNO (operands[2]))));
- operands[0] = gen_rtx_REG (SImode, REGNO (operands[0]));
- return AS2 (lea%L0,%a1,%0);
+ default:
+ /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'.
+ Exceptions: -128 encodes smaller than 128, so swap sign and op. */
+ if (GET_CODE (operands[2]) == CONST_INT
+ && (INTVAL (operands[2]) == 128
+ || (INTVAL (operands[2]) < 0
+ && INTVAL (operands[2]) != -128)))
+ {
+ operands[2] = GEN_INT (-INTVAL (operands[2]));
+ return \"sub{w}\\t{%2, %0|%0, %2}\";
+ }
+ return \"add{w}\\t{%2, %0|%0, %2}\";
}
+}"
+ [(set (attr "type")
+ (if_then_else (match_operand:HI 2 "incdec_operand" "")
+ (const_string "incdec")
+ (const_string "alu")))])
- /* ??? what about offsettable memory references? */
- if (!TARGET_PENTIUMPRO /* partial stalls are just too painful to risk. */
- && QI_REG_P (operands[0])
- && GET_CODE (operands[2]) == CONST_INT
- && (INTVAL (operands[2]) & 0xff) == 0
- && i386_cc_probably_useless_p (insn))
+;; If we know we're not touching memory, promote HImode references to SImode.
+(define_split
+ [(set (match_operand:HI 0 "register_operand" "")
+ (plus:HI (match_operand:HI 1 "register_operand" "")
+ (match_operand:HI 2 "nonmemory_operand" "")))
+ (clobber (reg:CC 17))]
+ "! TARGET_PARTIAL_REG_STALL && reload_completed"
+ [(parallel [(set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))
+ (clobber (reg:CC 17))])]
+ "operands[0] = gen_lowpart (SImode, operands[0]);
+ operands[1] = gen_lowpart (SImode, operands[1]);
+ operands[2] = gen_lowpart (SImode, operands[2]);")
+
+(define_insn "*addhi_2"
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (plus:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:HI 2 "general_operand" "rmni,rni"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=r,rm")
+ (plus:HI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (PLUS, HImode, operands)"
+ "*
+{
+ switch (get_attr_type (insn))
{
- int byteval = (INTVAL (operands[2]) >> 8) & 0xff;
- CC_STATUS_INIT;
+ case TYPE_INCDEC:
+ if (operands[2] == const1_rtx)
+ return \"inc{w}\\t%0\";
+ else if (operands[2] == constm1_rtx
+ || (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) == 65535))
+ return \"dec{w}\\t%0\";
+ abort();
- if (byteval == 1)
- return AS1 (inc%B0,%h0);
- else if (byteval == 255)
- return AS1 (dec%B0,%h0);
-
- operands[2] = GEN_INT (byteval);
- return AS2 (add%B0,%2,%h0);
+ default:
+ /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'.
+ Exceptions: -128 encodes smaller than 128, so swap sign and op. */
+ if (GET_CODE (operands[2]) == CONST_INT
+ && (INTVAL (operands[2]) == 128
+ || (INTVAL (operands[2]) < 0
+ && INTVAL (operands[2]) != -128)))
+ {
+ operands[2] = GEN_INT (-INTVAL (operands[2]));
+ return \"sub{w}\\t{%2, %0|%0, %2}\";
+ }
+ return \"add{w}\\t{%2, %0|%0, %2}\";
}
+}"
+ [(set (attr "type")
+ (if_then_else (match_operand:HI 2 "incdec_operand" "")
+ (const_string "incdec")
+ (const_string "alu")))])
+
+(define_insn "*addhi_3"
+ [(set (reg:CC 17)
+ (compare:CC (plus:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:HI 2 "general_operand" "rmni,rni"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=r,rm")
+ (plus:HI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (PLUS, HImode, operands)"
+ "add{w}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
- /* Use a 32-bit operation when possible, to avoid the prefix penalty. */
- if (REG_P (operands[0])
- && i386_aligned_p (operands[2])
- && i386_cc_probably_useless_p (insn))
+(define_expand "addqi3"
+ [(parallel [(set (match_operand:QI 0 "general_operand" "")
+ (plus:QI (match_operand:QI 1 "general_operand" "")
+ (match_operand:QI 2 "general_operand" "")))
+ (clobber (reg:CC 17))])]
+ ""
+ "ix86_expand_binary_operator (PLUS, QImode, operands); DONE;")
+
+;; %%% Potential partial reg stall on alternative 2. What to do?
+(define_insn "*addqi_1"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q,*r")
+ (plus:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0")
+ (match_operand:QI 2 "general_operand" "qn,qmn,*rn")))
+ (clobber (reg:CC 17))]
+ "ix86_binary_operator_ok (PLUS, QImode, operands)"
+ "*
+{
+ int widen = (which_alternative == 2);
+ switch (get_attr_type (insn))
{
- CC_STATUS_INIT;
+ case TYPE_INCDEC:
+ if (operands[2] == const1_rtx)
+ return widen ? \"inc{l}\\t%k0\" : \"inc{b}\\t%0\";
+ else if (operands[2] == constm1_rtx
+ || (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) == 255))
+ return widen ? \"dec{l}\\t%k0\" : \"dec{b}\\t%0\";
+ abort();
- if (GET_CODE (operands[2]) == CONST_INT)
+ default:
+ /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'.
+ Exceptions: -128 encodes smaller than 128, so swap sign and op. */
+ if (GET_CODE (operands[2]) == CONST_INT
+ && (INTVAL (operands[2]) == 128
+ || (INTVAL (operands[2]) < 0
+ && INTVAL (operands[2]) != -128)))
{
- HOST_WIDE_INT intval = 0xffff & INTVAL (operands[2]);
-
- if (intval == 1)
- return AS1 (inc%L0,%k0);
-
- if (intval == 0xffff)
- return AS1 (dec%L0,%k0);
-
- operands[2] = i386_sext16_if_const (operands[2]);
+ operands[2] = GEN_INT (-INTVAL (operands[2]));
+ if (widen)
+ return \"sub{l}\\t{%2, %k0|%k0, %2}\";
+ else
+ return \"sub{b}\\t{%2, %0|%0, %2}\";
}
- return AS2 (add%L0,%k2,%k0);
+ if (widen)
+ return \"add{l}\\t{%k2, %k0|%k0, %k2}\";
+ else
+ return \"add{b}\\t{%2, %0|%0, %2}\";
}
+}"
+ [(set (attr "type")
+ (if_then_else (match_operand:QI 2 "incdec_operand" "")
+ (const_string "incdec")
+ (const_string "alu")))])
+
+(define_insn "*addqi_2"
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (plus:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:QI 2 "general_operand" "qmni,qni"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "nonimmediate_operand" "=q,qm")
+ (plus:QI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (PLUS, QImode, operands)"
+ "*
+{
+ switch (get_attr_type (insn))
+ {
+ case TYPE_INCDEC:
+ if (operands[2] == const1_rtx)
+ return \"inc{b}\\t%0\";
+ else if (operands[2] == constm1_rtx
+ || (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) == 255))
+ return \"dec{b}\\t%0\";
+ abort();
- if (operands[2] == const1_rtx)
- return AS1 (inc%W0,%0);
-
- if (operands[2] == constm1_rtx
- || (GET_CODE (operands[2]) == CONST_INT
- && INTVAL (operands[2]) == 65535))
- return AS1 (dec%W0,%0);
-
- return AS2 (add%W0,%2,%0);
+ default:
+ /* Make things pretty and `subb $4,%al' rather than `addb $-4, %al'. */
+ if (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) < 0)
+ {
+ operands[2] = GEN_INT (-INTVAL (operands[2]));
+ return \"sub{b}\\t{%2, %0|%0, %2}\";
+ }
+ return \"add{b}\\t{%2, %0|%0, %2}\";
+ }
}"
- [(set_attr "type" "binary")])
+ [(set (attr "type")
+ (if_then_else (match_operand:QI 2 "incdec_operand" "")
+ (const_string "incdec")
+ (const_string "alu")))])
+
+(define_insn "*addqi_3"
+ [(set (reg:CC 17)
+ (compare:CC (plus:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:QI 2 "general_operand" "qmni,qni"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "nonimmediate_operand" "=q,qm")
+ (plus:QI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (PLUS, QImode, operands)"
+ "add{b}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
-(define_expand "addqi3"
- [(set (match_operand:QI 0 "general_operand" "")
- (plus:QI (match_operand:QI 1 "general_operand" "")
- (match_operand:QI 2 "general_operand" "")))]
+(define_insn "*addqi_low_1"
+ [(set (strict_low_part (match_operand:QI 0 "register_operand" "q"))
+ (plus:QI (match_operand:QI 1 "register_operand" "0")
+ (match_operand:QI 2 "general_operand" "qmn")))
+ (clobber (reg:CC 17))]
""
- "IX86_EXPAND_BINARY_OPERATOR (PLUS, QImode, operands);")
-
-(define_insn ""
- [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q,?q")
- (plus:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,q")
- (match_operand:QI 2 "general_operand" "qn,qmn,qn")))]
- "ix86_binary_operator_ok (PLUS, QImode, operands)"
"*
{
- if (REG_P (operands[0]) && REG_P (operands[1])
- && (REG_P (operands[2]) || CONSTANT_P (operands[2]))
- && (REGNO (operands[0]) != REGNO (operands[1])
- || NON_QI_REG_P (operands[1])
- || (REG_P (operands[2]) && NON_QI_REG_P (operands[2]))))
+ switch (get_attr_type (insn))
{
- if (operands[2] == stack_pointer_rtx)
- abort ();
+ case TYPE_INCDEC:
+ if (operands[2] == const1_rtx)
+ return \"inc{b}\\t%b0\";
+ else if (operands[2] == constm1_rtx
+ || (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) == 255))
+ return \"dec{b}\\t%b0\";
+ abort();
- CC_STATUS_INIT;
- operands[1]
- = gen_rtx_PLUS (SImode,
- gen_rtx_REG (SImode, REGNO (operands[1])),
- (! REG_P (operands[2])
- ? operands[2]
- : gen_rtx_REG (SImode, REGNO (operands[2]))));
- operands[0] = gen_rtx_REG (SImode, REGNO (operands[0]));
- return AS2 (lea%L0,%a1,%0);
+ default:
+ return \"add{b}\\t{%2, %b0|%b0, %2}\";
}
- if (operands[2] == const1_rtx)
- return AS1 (inc%B0,%0);
-
- if (operands[2] == constm1_rtx
- || (GET_CODE (operands[2]) == CONST_INT
- && INTVAL (operands[2]) == 255))
- return AS1 (dec%B0,%0);
+}"
+ [(set (attr "type")
+ (if_then_else (match_operand:QI 2 "incdec_operand" "")
+ (const_string "incdec")
+ (const_string "alu")))])
+
+(define_insn "addqi_ext_1"
+ [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=q")
+ (const_int 8)
+ (const_int 8))
+ (plus:SI
+ (zero_extract:SI
+ (match_operand 1 "ext_register_operand" "0")
+ (const_int 8)
+ (const_int 8))
+ (match_operand:QI 2 "general_operand" "qmn")))
+ (clobber (reg:CC 17))]
+ ""
+ "*
+{
+ switch (get_attr_type (insn))
+ {
+ case TYPE_INCDEC:
+ if (operands[2] == const1_rtx)
+ return \"inc{b}\\t%h0\";
+ else if (operands[2] == constm1_rtx
+ || (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) == 255))
+ return \"dec{b}\\t%h0\";
+ abort();
- return AS2 (add%B0,%2,%0);
+ default:
+ return \"add{b}\\t{%2, %h0|%h0, %2}\";
+ }
}"
- [(set_attr "type" "binary")])
-
-;Lennart Augustsson <augustss@cs.chalmers.se>
-;says this pattern just makes slower code:
-; pushl %ebp
-; addl $-80,(%esp)
-;instead of
-; leal -80(%ebp),%eax
-; pushl %eax
-;
-;(define_insn ""
-; [(set (match_operand:SI 0 "push_operand" "=<")
-; (plus:SI (match_operand:SI 1 "register_operand" "%r")
-; (match_operand:SI 2 "nonmemory_operand" "ri")))]
-; ""
-; "*
-;{
-; rtx xops[4];
-; xops[0] = operands[0];
-; xops[1] = operands[1];
-; xops[2] = operands[2];
-; xops[3] = gen_rtx_MEM (SImode, stack_pointer_rtx);
-; output_asm_insn (\"push%z1 %1\", xops);
-; output_asm_insn (AS2 (add%z3,%2,%3), xops);
-; RET;
-;}")
+ [(set (attr "type")
+ (if_then_else (match_operand:QI 2 "incdec_operand" "")
+ (const_string "incdec")
+ (const_string "alu")))])
+
+(define_insn "*addqi_ext_2"
+ [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=q")
+ (const_int 8)
+ (const_int 8))
+ (plus:SI
+ (zero_extract:SI
+ (match_operand 1 "ext_register_operand" "%0")
+ (const_int 8)
+ (const_int 8))
+ (zero_extract:SI
+ (match_operand 2 "ext_register_operand" "q")
+ (const_int 8)
+ (const_int 8))))
+ (clobber (reg:CC 17))]
+ ""
+ "add{b}\\t{%h2, %h0|%h0, %h2}"
+ [(set_attr "type" "alu")])
;; The patterns that match these are at the end of this file.
"TARGET_80387"
"")
\f
-;;- subtract instructions
-
-(define_insn "subsidi3"
- [(set (match_operand:DI 0 "general_operand" "=&r,&ro,&r,!&r,o,o,!o")
- (minus:DI (match_operand:DI 1 "general_operand" "0iF,0,roiF,roiF,riF,o,o")
- (zero_extend:DI (match_operand:SI 2 "general_operand" "o,ri,ri,o,ri,i,r"))))
- (clobber (match_scratch:SI 3 "=X,X,X,X,X,&r,&r"))]
- ""
- "*
-{
- rtx low[3], high[3], xops[7];
-
- CC_STATUS_INIT;
-
- split_di (operands, 2, low, high);
- high[2] = const0_rtx;
- low[2] = operands[2];
-
- if (!rtx_equal_p (operands[0], operands[1]))
- {
- xops[0] = high[0];
- xops[1] = low[0];
- xops[2] = high[1];
- xops[3] = low[1];
-
- if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
- {
- output_asm_insn (AS2 (mov%L1,%3,%1), xops);
- output_asm_insn (AS2 (mov%L0,%2,%0), xops);
- }
- else
- {
- xops[4] = high[2];
- xops[5] = low[2];
- xops[6] = operands[3];
- output_asm_insn (AS2 (mov%L6,%3,%6), xops);
- output_asm_insn (AS2 (sub%L6,%5,%6), xops);
- output_asm_insn (AS2 (mov%L1,%6,%1), xops);
- output_asm_insn (AS2 (mov%L6,%2,%6), xops);
- output_asm_insn (AS2 (sbb%L6,%4,%6), xops);
- output_asm_insn (AS2 (mov%L0,%6,%0), xops);
- RET;
- }
- }
-
- output_asm_insn (AS2 (sub%L0,%2,%0), low);
- output_asm_insn (AS2 (sbb%L0,%2,%0), high);
- cc_status.value1 = high[0];
- cc_status.flags = CC_NO_OVERFLOW;
+;; Subtract instructions
- RET;
-}"
- [(set_attr "type" "binary")])
+;; %%% define_expand from the very first?
+;; %%% splits for subsidi3
(define_insn "subdi3"
- [(set (match_operand:DI 0 "general_operand" "=&r,&ro,o,o,!&r,!o")
- (minus:DI (match_operand:DI 1 "general_operand" "0,0,0iF,or,roiF,roiF")
- (match_operand:DI 2 "general_operand" "or,riF,or,iF,roiF,roiF")))
- (clobber (match_scratch:SI 3 "=X,X,&r,&r,X,&r"))]
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=r,o")
+ (minus:DI (match_operand:DI 1 "general_operand" "0,0")
+ (match_operand:DI 2 "general_operand" "roiF,riF")))
+ (clobber (reg:CC 17))]
""
- "*
-{
- rtx low[3], high[3], xops[7];
-
- CC_STATUS_INIT;
-
- split_di (operands, 3, low, high);
-
- if (!rtx_equal_p (operands[0], operands[1]))
- {
- xops[0] = high[0];
- xops[1] = low[0];
- xops[2] = high[1];
- xops[3] = low[1];
-
- if (GET_CODE (operands[0]) != MEM)
- {
- output_asm_insn (AS2 (mov%L1,%3,%1), xops);
- output_asm_insn (AS2 (mov%L0,%2,%0), xops);
- }
- else
- {
- xops[4] = high[2];
- xops[5] = low[2];
- xops[6] = operands[3];
- output_asm_insn (AS2 (mov%L6,%3,%6), xops);
- output_asm_insn (AS2 (sub%L6,%5,%6), xops);
- output_asm_insn (AS2 (mov%L1,%6,%1), xops);
- output_asm_insn (AS2 (mov%L6,%2,%6), xops);
- output_asm_insn (AS2 (sbb%L6,%4,%6), xops);
- output_asm_insn (AS2 (mov%L0,%6,%0), xops);
- RET;
- }
- }
-
- cc_status.value1 = high[0];
- cc_status.flags = CC_NO_OVERFLOW;
-
- if (GET_CODE (operands[3]) == REG)
- {
- xops[0] = high[0];
- xops[1] = low[0];
- xops[2] = high[2];
- xops[3] = low[2];
- xops[4] = operands[3];
-
- output_asm_insn (AS2 (mov%L4,%3,%4), xops);
- output_asm_insn (AS2 (sub%L1,%4,%1), xops);
- output_asm_insn (AS2 (mov%L4,%2,%4), xops);
- output_asm_insn (AS2 (sbb%L0,%4,%0), xops);
- }
-
- else if (GET_CODE (low[2]) != CONST_INT || INTVAL (low[2]) != 0)
- {
- output_asm_insn (AS2 (sub%L0,%2,%0), low);
- output_asm_insn (AS2 (sbb%L0,%2,%0), high);
- }
-
- else
- output_asm_insn (AS2 (sub%L0,%2,%0), high);
-
+ "#")
- RET;
-}"
- [(set_attr "type" "binary")])
+(define_split
+ [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (minus:DI (match_operand:DI 1 "general_operand" "")
+ (match_operand:DI 2 "general_operand" "")))
+ (clobber (reg:CC 17))]
+ "cse_not_expected"
+ [(parallel [(set (reg:CC 17) (minus:CC (match_dup 1) (match_dup 2)))
+ (set (match_dup 0) (minus:SI (match_dup 1) (match_dup 2)))])
+ (parallel [(set (match_dup 3)
+ (minus:SI (match_dup 4)
+ (plus:SI (match_dup 5)
+ (ltu:SI (reg:CC 17) (const_int 0)))))
+ (clobber (reg:CC 17))])]
+ "split_di (operands+0, 1, operands+0, operands+3);
+ split_di (operands+1, 1, operands+1, operands+4);
+ split_di (operands+2, 1, operands+2, operands+5);")
+
+(define_insn "subcsi3"
+ [(set (reg:CC 17) (minus:CC (match_operand:SI 1 "nonimmediate_operand" "0,0")
+ (match_operand:SI 2 "general_operand" "ri,rm")))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
+ (minus:SI (match_dup 1) (match_dup 2)))]
+ ""
+ "sub{l}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_insn "subxsi3"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
+ (minus:SI (match_operand:SI 1 "nonimmediate_operand" "0,0")
+ (plus:SI (match_operand:SI 2 "general_operand" "ri,rm")
+ (ltu:SI (reg:CC 17) (const_int 0)))))
+ (clobber (reg:CC 17))]
+ ""
+ "sbb{l}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")
+ (set_attr "pent_pair" "pu")
+ (set_attr "ppro_uops" "few")])
(define_expand "subsi3"
- [(set (match_operand:SI 0 "nonimmediate_operand" "")
- (minus:SI (match_operand:SI 1 "nonimmediate_operand" "")
- (match_operand:SI 2 "general_operand" "")))]
+ [(parallel [(set (match_operand:SI 0 "nonimmediate_operand" "")
+ (minus:SI (match_operand:SI 1 "nonimmediate_operand" "")
+ (match_operand:SI 2 "general_operand" "")))
+ (clobber (reg:CC 17))])]
""
- "IX86_EXPAND_BINARY_OPERATOR (MINUS, SImode, operands);")
+ "ix86_expand_binary_operator (MINUS, SImode, operands); DONE;")
-(define_insn ""
+(define_insn "*subsi_1"
[(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
(minus:SI (match_operand:SI 1 "nonimmediate_operand" "0,0")
- (match_operand:SI 2 "general_operand" "ri,rm")))]
+ (match_operand:SI 2 "general_operand" "ri,rm")))
+ (clobber (reg:CC 17))]
+ "ix86_binary_operator_ok (MINUS, SImode, operands)"
+ "sub{l}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_insn "*subsi_2"
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (minus:SI (match_operand:SI 1 "nonimmediate_operand" "0,0")
+ (match_operand:SI 2 "general_operand" "ri,rm"))
+ (const_int 0)))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
+ (minus:SI (match_dup 1) (match_dup 2)))]
"ix86_binary_operator_ok (MINUS, SImode, operands)"
- "* return AS2 (sub%L0,%2,%0);"
- [(set_attr "type" "binary")])
+ "sub{l}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_insn "*subsi_3"
+ [(set (reg:CC 17)
+ (compare:CC
+ (minus:SI (match_operand:SI 1 "nonimmediate_operand" "0,0")
+ (match_operand:SI 2 "general_operand" "ri,rm"))
+ (const_int 0)))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
+ (minus:SI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (MINUS, SImode, operands)"
+ "sub{l}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
(define_expand "subhi3"
- [(set (match_operand:HI 0 "general_operand" "")
- (minus:HI (match_operand:HI 1 "nonimmediate_operand" "")
- (match_operand:HI 2 "general_operand" "")))]
+ [(parallel [(set (match_operand:HI 0 "general_operand" "")
+ (minus:HI (match_operand:HI 1 "nonimmediate_operand" "")
+ (match_operand:HI 2 "general_operand" "")))
+ (clobber (reg:CC 17))])]
""
- "IX86_EXPAND_BINARY_OPERATOR (MINUS, HImode, operands);")
+ "ix86_expand_binary_operator (MINUS, HImode, operands); DONE;")
-(define_insn ""
+(define_insn "*subhi_1"
[(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r")
(minus:HI (match_operand:HI 1 "nonimmediate_operand" "0,0")
- (match_operand:HI 2 "general_operand" "ri,rm")))]
+ (match_operand:HI 2 "general_operand" "ri,rm")))
+ (clobber (reg:CC 17))]
"ix86_binary_operator_ok (MINUS, HImode, operands)"
- "*
-{
- if (REG_P (operands[0])
- && i386_aligned_p (operands[2])
- && i386_cc_probably_useless_p (insn))
- {
- CC_STATUS_INIT;
- operands[2] = i386_sext16_if_const (operands[2]);
- return AS2 (sub%L0,%k2,%k0);
- }
- return AS2 (sub%W0,%2,%0);
-}"
- [(set_attr "type" "binary")])
+ "sub{w}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_insn "*subhi_2"
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (minus:HI (match_operand:HI 1 "nonimmediate_operand" "0,0")
+ (match_operand:HI 2 "general_operand" "ri,rm"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=rm,r")
+ (minus:HI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (MINUS, HImode, operands)"
+ "sub{w}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_insn "*subhi_3"
+ [(set (reg:CC 17)
+ (compare:CC
+ (minus:HI (match_operand:HI 1 "nonimmediate_operand" "0,0")
+ (match_operand:HI 2 "general_operand" "ri,rm"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=rm,r")
+ (minus:HI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (MINUS, HImode, operands)"
+ "sub{w}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
(define_expand "subqi3"
- [(set (match_operand:QI 0 "general_operand" "")
- (minus:QI (match_operand:QI 1 "general_operand" "")
- (match_operand:QI 2 "general_operand" "")))]
+ [(parallel [(set (match_operand:QI 0 "general_operand" "")
+ (minus:QI (match_operand:QI 1 "general_operand" "")
+ (match_operand:QI 2 "general_operand" "")))
+ (clobber (reg:CC 17))])]
""
- "IX86_EXPAND_BINARY_OPERATOR (MINUS, QImode, operands);")
+ "ix86_expand_binary_operator (MINUS, QImode, operands); DONE;")
-(define_insn ""
+(define_insn "*subqi_1"
[(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q")
(minus:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")
- (match_operand:QI 2 "general_operand" "qn,qmn")))]
+ (match_operand:QI 2 "general_operand" "qn,qmn")))
+ (clobber (reg:CC 17))]
+ "ix86_binary_operator_ok (MINUS, QImode, operands)"
+ "sub{b}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_insn "*subqi_2"
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (minus:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "general_operand" "qi,qm"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=qm,q")
+ (minus:HI (match_dup 1) (match_dup 2)))]
"ix86_binary_operator_ok (MINUS, QImode, operands)"
- "* return AS2 (sub%B0,%2,%0);"
- [(set_attr "type" "binary")])
+ "sub{b}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_insn "*subqi_3"
+ [(set (reg:CC 17)
+ (compare:CC
+ (minus:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "general_operand" "qi,qm"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=qm,q")
+ (minus:HI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (MINUS, QImode, operands)"
+ "sub{b}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
;; The patterns that match these are at the end of this file.
"TARGET_80387"
"")
\f
-;;- multiply instructions
+;; Multiply instructions
-;(define_insn "mulqi3"
-; [(set (match_operand:QI 0 "register_operand" "=a")
-; (mult:QI (match_operand:QI 1 "register_operand" "%0")
-; (match_operand:QI 2 "nonimmediate_operand" "qm")))]
-; ""
-; "imul%B0 %2,%0")
+(define_insn "mulsi3"
+ [(set (match_operand:SI 0 "register_operand" "=r,r,r")
+ (mult:SI (match_operand:SI 1 "nonimmediate_operand" "%rm,0,0")
+ (match_operand:SI 2 "general_operand" "K,i,mr")))
+ (clobber (reg:CC 17))]
+ ""
+ ; %%% There was a note about "Assembler has weird restrictions",
+ ; concerning alternative 1 when op1 == op0. True?
+ "@
+ imul{l}\\t{%2, %1, %0|%0, %1, %2}
+ imul{l}\\t{%2, %0|%0, %2}
+ imul{l}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "imul")
+ (set_attr "length" "2,3,2")])
(define_insn "mulhi3"
[(set (match_operand:HI 0 "register_operand" "=r,r")
- (mult:HI (match_operand:HI 1 "nonimmediate_operand" "%0,rm")
- (match_operand:HI 2 "general_operand" "g,i")))]
- ""
- "*
-{
- if (GET_CODE (operands[1]) == REG
- && REGNO (operands[1]) == REGNO (operands[0])
- && (GET_CODE (operands[2]) == MEM || GET_CODE (operands[2]) == REG))
- /* Assembler has weird restrictions. */
- return AS2 (imul%W0,%2,%0);
- return AS3 (imul%W0,%2,%1,%0);
-}"
- [(set_attr "type" "imul")])
-
-(define_insn "mulsi3"
- [(set (match_operand:SI 0 "register_operand" "=r,r")
- (mult:SI (match_operand:SI 1 "nonimmediate_operand" "%0,rm")
- (match_operand:SI 2 "general_operand" "g,i")))]
+ (mult:HI (match_operand:HI 1 "nonimmediate_operand" "%rm,0")
+ (match_operand:HI 2 "general_operand" "K,g")))
+ (clobber (reg:CC 17))]
""
- "*
-{
- if (GET_CODE (operands[1]) == REG
- && REGNO (operands[1]) == REGNO (operands[0])
- && (GET_CODE (operands[2]) == MEM || GET_CODE (operands[2]) == REG))
- /* Assembler has weird restrictions. */
- return AS2 (imul%L0,%2,%0);
- return AS3 (imul%L0,%2,%1,%0);
-}"
+ ; %%% There was a note about "Assembler has weird restrictions",
+ ; concerning alternative 1 when op1 == op0. True?
+ "@
+ imul{w}\\t{%2, %1, %0|%0, %1, %2}
+ imul{w}\\t{%2, %0|%0, %2}"
[(set_attr "type" "imul")])
(define_insn "umulqihi3"
[(set (match_operand:HI 0 "register_operand" "=a")
(mult:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "%0"))
- (zero_extend:HI (match_operand:QI 2 "nonimmediate_operand" "qm"))))]
+ (zero_extend:HI (match_operand:QI 2 "nonimmediate_operand" "qm"))))
+ (clobber (reg:CC 17))]
""
- "mul%B0 %2"
+ "mul{b}\\t%2"
[(set_attr "type" "imul")])
(define_insn "mulqihi3"
[(set (match_operand:HI 0 "register_operand" "=a")
(mult:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "%0"))
- (sign_extend:HI (match_operand:QI 2 "nonimmediate_operand" "qm"))))]
+ (sign_extend:HI (match_operand:QI 2 "nonimmediate_operand" "qm"))))
+ (clobber (reg:CC 17))]
""
- "imul%B0 %2"
+ "imul{b}\\t%2"
[(set_attr "type" "imul")])
(define_insn "umulsidi3"
[(set (match_operand:DI 0 "register_operand" "=A")
(mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "%0"))
- (zero_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm"))))]
- "TARGET_WIDE_MULTIPLY"
- "mul%L0 %2"
- [(set_attr "type" "imul")])
+ (zero_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm"))))
+ (clobber (reg:CC 17))]
+ ""
+ "mul{l}\\t%2"
+ [(set_attr "type" "imul")
+ (set_attr "ppro_uops" "few")])
(define_insn "mulsidi3"
[(set (match_operand:DI 0 "register_operand" "=A")
(mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "%0"))
- (sign_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm"))))]
- "TARGET_WIDE_MULTIPLY"
- "imul%L0 %2"
+ (sign_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm"))))
+ (clobber (reg:CC 17))]
+ ""
+ "imul{l}\\t%2"
[(set_attr "type" "imul")])
(define_insn "umulsi3_highpart"
[(set (match_operand:SI 0 "register_operand" "=d")
- (truncate:SI (lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "%a"))
- (zero_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm")))
- (const_int 32))))
- (clobber (match_scratch:SI 3 "=a"))]
- "TARGET_WIDE_MULTIPLY"
- "mul%L0 %2"
- [(set_attr "type" "imul")])
+ (truncate:SI
+ (lshiftrt:DI
+ (mult:DI (zero_extend:DI
+ (match_operand:SI 1 "register_operand" "%a"))
+ (zero_extend:DI
+ (match_operand:SI 2 "nonimmediate_operand" "rm")))
+ (const_int 32))))
+ (clobber (match_scratch:SI 3 "=a"))
+ (clobber (reg:CC 17))]
+ ""
+ "mul{l}\\t%2"
+ [(set_attr "type" "imul")
+ (set_attr "ppro_uops" "few")])
(define_insn "smulsi3_highpart"
[(set (match_operand:SI 0 "register_operand" "=d")
- (truncate:SI (lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "%a"))
- (sign_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm")))
- (const_int 32))))
- (clobber (match_scratch:SI 3 "=a"))]
- "TARGET_WIDE_MULTIPLY"
- "imul%L0 %2"
- [(set_attr "type" "imul")])
+ (truncate:SI
+ (lshiftrt:DI
+ (mult:DI (sign_extend:DI
+ (match_operand:SI 1 "register_operand" "%a"))
+ (sign_extend:DI
+ (match_operand:SI 2 "nonimmediate_operand" "rm")))
+ (const_int 32))))
+ (clobber (match_scratch:SI 3 "=a"))
+ (clobber (reg:CC 17))]
+ ""
+ "imul{l}\\t%2"
+ [(set_attr "type" "imul")
+ (set_attr "ppro_uops" "few")])
;; The patterns that match these are at the end of this file.
"TARGET_80387"
"")
\f
-;;- divide instructions
+;; Divide instructions
(define_insn "divqi3"
[(set (match_operand:QI 0 "register_operand" "=a")
(div:QI (match_operand:HI 1 "register_operand" "0")
- (match_operand:QI 2 "nonimmediate_operand" "qm")))]
+ (match_operand:QI 2 "nonimmediate_operand" "qm")))
+ (clobber (reg:CC 17))]
""
- "idiv%B0 %2")
+ "idiv{b}\\t%2"
+ [(set_attr "type" "idiv")
+ (set_attr "ppro_uops" "few")])
(define_insn "udivqi3"
[(set (match_operand:QI 0 "register_operand" "=a")
(udiv:QI (match_operand:HI 1 "register_operand" "0")
- (match_operand:QI 2 "nonimmediate_operand" "qm")))]
+ (match_operand:QI 2 "nonimmediate_operand" "qm")))
+ (clobber (reg:CC 17))]
""
- "div%B0 %2"
- [(set_attr "type" "idiv")])
+ "div{b}\\t%2"
+ [(set_attr "type" "idiv")
+ (set_attr "ppro_uops" "few")])
;; The patterns that match these are at the end of this file.
(define_insn "divmodsi4"
[(set (match_operand:SI 0 "register_operand" "=a")
- (div:SI (match_operand:SI 1 "register_operand" "0")
+ (div:SI (match_operand:SI 1 "register_operand" "A")
(match_operand:SI 2 "nonimmediate_operand" "rm")))
(set (match_operand:SI 3 "register_operand" "=&d")
- (mod:SI (match_dup 1) (match_dup 2)))]
+ (mod:SI (match_dup 1) (match_dup 2)))
+ (clobber (reg:CC 17))]
""
- "*
+ "{cltd|cdq}\;idiv{l}\\t%2"
+ [(set_attr "type" "multi")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=a")
+ (div:SI (match_operand:SI 1 "register_operand" "A")
+ (match_operand:SI 2 "nonimmediate_operand" "rm")))
+ (set (match_operand:SI 3 "register_operand" "=d")
+ (mod:SI (match_dup 1) (match_dup 2)))
+ (use (match_dup 3))
+ (clobber (reg:CC 17))]
+ ""
+ "idiv{l}\\t%2"
+ [(set_attr "type" "idiv")
+ (set_attr "ppro_uops" "few")])
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (div:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "nonimmediate_operand" "")))
+ (set (match_operand:SI 3 "register_operand" "")
+ (mod:SI (match_dup 1) (match_dup 2)))
+ (clobber (reg:CC 17))]
+ "reload_completed"
+ [(parallel [(set (match_dup 3)
+ (ashiftrt:SI (match_dup 4) (const_int 31)))
+ (clobber (reg:CC 17))])
+ (parallel [(set (match_dup 0)
+ (div:SI (match_dup 1) (match_dup 2)))
+ (set (match_dup 3)
+ (mod:SI (match_dup 1) (match_dup 2)))
+ (use (match_dup 3))
+ (clobber (reg:CC 17))])]
+ "
{
-#ifdef INTEL_SYNTAX
- output_asm_insn (\"cdq\", operands);
-#else
- output_asm_insn (\"cltd\", operands);
-#endif
- return AS1 (idiv%L0,%2);
-}"
- [(set_attr "type" "idiv")])
+ /* Avoid use of cltd in favour of a mov+shift. */
+ if (TARGET_PENTIUM && !optimize_size)
+ {
+ emit_move_insn (operands[3], operands[1]);
+ operands[4] = operands[3];
+ }
+ else
+ operands[4] = operands[1];
+}")
+;; %%% Split me.
(define_insn "divmodhi4"
[(set (match_operand:HI 0 "register_operand" "=a")
(div:HI (match_operand:HI 1 "register_operand" "0")
(match_operand:HI 2 "nonimmediate_operand" "rm")))
(set (match_operand:HI 3 "register_operand" "=&d")
- (mod:HI (match_dup 1) (match_dup 2)))]
+ (mod:HI (match_dup 1) (match_dup 2)))
+ (clobber (reg:CC 17))]
""
- "cwtd\;idiv%W0 %2"
- [(set_attr "type" "idiv")])
+ "cwtd\;idiv{w}\\t%2"
+ [(set_attr "type" "multi")])
-;; ??? Can we make gcc zero extend operand[0]?
(define_insn "udivmodsi4"
[(set (match_operand:SI 0 "register_operand" "=a")
(udiv:SI (match_operand:SI 1 "register_operand" "0")
(match_operand:SI 2 "nonimmediate_operand" "rm")))
(set (match_operand:SI 3 "register_operand" "=&d")
- (umod:SI (match_dup 1) (match_dup 2)))]
- ""
- "*
-{
- output_asm_insn (AS2 (xor%L3,%3,%3), operands);
- return AS1 (div%L0,%2);
-}"
- [(set_attr "type" "idiv")])
-
-;; ??? Can we make gcc zero extend operand[0]?
-(define_insn "udivmodhi4"
- [(set (match_operand:HI 0 "register_operand" "=a")
- (udiv:HI (match_operand:HI 1 "register_operand" "0")
- (match_operand:HI 2 "nonimmediate_operand" "rm")))
- (set (match_operand:HI 3 "register_operand" "=&d")
- (umod:HI (match_dup 1) (match_dup 2)))]
+ (umod:SI (match_dup 1) (match_dup 2)))
+ (clobber (reg:CC 17))]
""
- "*
-{
- output_asm_insn (AS2 (xor%W0,%3,%3), operands);
- return AS1 (div%W0,%2);
-}"
- [(set_attr "type" "idiv")])
-
-/*
-;;this should be a valid double division which we may want to add
+ "xor{l}\\t%3, %3\;div{l}\\t%2"
+ [(set_attr "type" "multi")])
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=a")
- (udiv:DI (match_operand:DI 1 "register_operand" "a")
+ (udiv:SI (match_operand:SI 1 "register_operand" "0")
(match_operand:SI 2 "nonimmediate_operand" "rm")))
(set (match_operand:SI 3 "register_operand" "=d")
- (umod:SI (match_dup 1) (match_dup 2)))]
- ""
- "div%L0 %2,%0"
- [(set_attr "type" "idiv")])
-*/
-\f
-;;- and instructions
-
-;; On i386,
-;; movzbl %bl,%ebx
-;; is faster than
-;; andl $255,%ebx
-;;
-;; but if the reg is %eax, then the "andl" is faster.
-;;
-;; On i486, the "andl" is always faster than the "movzbl".
-;;
-;; On both i386 and i486, a three operand AND is as fast with movzbl or
-;; movzwl as with andl, if operands[0] != operands[1].
-
-;; The `r' in `rm' for operand 3 looks redundant, but it causes
-;; optional reloads to be generated if op 3 is a pseudo in a stack slot.
-
-(define_insn "andsi3"
- [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
- (and:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0")
- (match_operand:SI 2 "general_operand" "ri,rm")))]
- ""
- "*
-{
- HOST_WIDE_INT intval;
- if (!rtx_equal_p (operands[0], operands[1])
- && rtx_equal_p (operands[0], operands[2]))
- {
- rtx tmp;
- tmp = operands[1];
- operands[1] = operands[2];
- operands[2] = tmp;
- }
- switch (GET_CODE (operands[2]))
- {
- case CONST_INT:
- if (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))
- break;
- intval = INTVAL (operands[2]);
- /* zero-extend 16->32? */
- if (intval == 0xffff && REG_P (operands[0])
- && (! REG_P (operands[1])
- || REGNO (operands[0]) != 0 || REGNO (operands[1]) != 0)
- && (!TARGET_ZERO_EXTEND_WITH_AND || ! rtx_equal_p (operands[0], operands[1])))
- {
- /* ??? tege: Should forget CC_STATUS only if we clobber a
- remembered operand. Fix that later. */
- CC_STATUS_INIT;
-#ifdef INTEL_SYNTAX
- return AS2 (movzx,%w1,%0);
-#else
- return AS2 (movz%W0%L0,%w1,%0);
-#endif
- }
-
- /* zero extend 8->32? */
- if (intval == 0xff && REG_P (operands[0])
- && !(REG_P (operands[1]) && NON_QI_REG_P (operands[1]))
- && (! REG_P (operands[1])
- || REGNO (operands[0]) != 0 || REGNO (operands[1]) != 0)
- && (!TARGET_ZERO_EXTEND_WITH_AND || ! rtx_equal_p (operands[0], operands[1])))
- {
- /* ??? tege: Should forget CC_STATUS only if we clobber a
- remembered operand. Fix that later. */
- CC_STATUS_INIT;
-#ifdef INTEL_SYNTAX
- return AS2 (movzx,%b1,%0);
-#else
- return AS2 (movz%B0%L0,%b1,%0);
-#endif
- }
-
- /* Check partial bytes.. non-QI-regs are not available */
- if (REG_P (operands[0]) && ! QI_REG_P (operands[0]))
- break;
-
- /* only low byte has zero bits? */
- if (~(intval | 0xff) == 0)
- {
- intval &= 0xff;
- if (REG_P (operands[0]))
- {
- if (intval == 0)
- {
- CC_STATUS_INIT;
- return AS2 (xor%B0,%b0,%b0);
- }
-
- /* we're better off with the 32-bit version if reg != EAX */
- /* the value is sign-extended in 8 bits */
- if (REGNO (operands[0]) != 0 && (intval & 0x80))
- break;
- }
-
- CC_STATUS_INIT;
-
- operands[2] = GEN_INT (intval);
-
- if (intval == 0)
- return AS2 (mov%B0,%2,%b0);
-
- return AS2 (and%B0,%2,%b0);
- }
-
- /* only second byte has zero? */
- if (~(intval | 0xff00) == 0)
- {
- CC_STATUS_INIT;
-
- intval = (intval >> 8) & 0xff;
- operands[2] = GEN_INT (intval);
- if (intval == 0)
- {
- if (REG_P (operands[0]))
- return AS2 (xor%B0,%h0,%h0);
- operands[0] = adj_offsettable_operand (operands[0], 1);
- return AS2 (mov%B0,%2,%b0);
- }
-
- if (REG_P (operands[0]))
- return AS2 (and%B0,%2,%h0);
-
- operands[0] = adj_offsettable_operand (operands[0], 1);
- return AS2 (and%B0,%2,%b0);
- }
-
- if (REG_P (operands[0]))
- break;
-
- /* third byte has zero bits? */
- if (~(intval | 0xff0000) == 0)
- {
- intval = (intval >> 16) & 0xff;
- operands[0] = adj_offsettable_operand (operands[0], 2);
-byte_and_operation:
- CC_STATUS_INIT;
- operands[2] = GEN_INT (intval);
- if (intval == 0)
- return AS2 (mov%B0,%2,%b0);
- return AS2 (and%B0,%2,%b0);
- }
-
- /* fourth byte has zero bits? */
- if (~(intval | 0xff000000) == 0)
- {
- intval = (intval >> 24) & 0xff;
- operands[0] = adj_offsettable_operand (operands[0], 3);
- goto byte_and_operation;
- }
-
- /* Low word is zero? */
- if (intval == 0xffff0000)
- {
-word_zero_and_operation:
- CC_STATUS_INIT;
- operands[2] = const0_rtx;
- return AS2 (mov%W0,%2,%w0);
- }
-
- /* High word is zero? */
- if (intval == 0x0000ffff)
- {
- operands[0] = adj_offsettable_operand (operands[0], 2);
- goto word_zero_and_operation;
- }
-
- default:
- break;
- }
-
- return AS2 (and%L0,%2,%0);
-}"
- [(set_attr "type" "binary")])
-
-(define_insn "andhi3"
- [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r")
- (and:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
- (match_operand:HI 2 "general_operand" "ri,rm")))]
+ (umod:SI (match_dup 1) (match_dup 2)))
+ (use (match_dup 3))
+ (clobber (reg:CC 17))]
""
- "*
-{
- if (GET_CODE (operands[2]) == CONST_INT
- && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
- {
- /* Can we ignore the upper byte? */
- if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
- && (INTVAL (operands[2]) & 0xff00) == 0xff00)
- {
- CC_STATUS_INIT;
-
- if ((INTVAL (operands[2]) & 0xff) == 0)
- {
- operands[2] = const0_rtx;
- return AS2 (mov%B0,%2,%b0);
- }
-
- operands[2] = GEN_INT (INTVAL (operands[2]) & 0xff);
- return AS2 (and%B0,%2,%b0);
- }
-
- /* Can we ignore the lower byte? */
- /* ??? what about offsettable memory references? */
- if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & 0xff) == 0xff)
- {
- CC_STATUS_INIT;
-
- if ((INTVAL (operands[2]) & 0xff00) == 0)
- {
- operands[2] = const0_rtx;
- return AS2 (mov%B0,%2,%h0);
- }
-
- operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);
- return AS2 (and%B0,%2,%h0);
- }
-
- /* use 32-bit ops on registers when there are no sign issues.. */
- if (REG_P (operands[0]))
- {
- if (!(INTVAL (operands[2]) & ~0x7fff))
- return AS2 (and%L0,%2,%k0);
- }
- }
-
- if (REG_P (operands[0])
- && i386_aligned_p (operands[2]))
- {
- CC_STATUS_INIT;
- /* If op[2] is constant, we should zero-extend it and */
- /* make a note that op[0] has been zero-extended, so */
- /* that we could use 32-bit ops on it forthwith, but */
- /* there is no such reg-note available. Instead we do */
- /* a sign extension as that can result in shorter asm */
- operands[2] = i386_sext16_if_const (operands[2]);
- return AS2 (and%L0,%k2,%k0);
- }
-
- /* Use a 32-bit word with the upper bits set, invalidate CC */
- if (GET_CODE (operands[2]) == CONST_INT
- && i386_aligned_p (operands[0]))
- {
- HOST_WIDE_INT val = INTVAL (operands[2]);
- CC_STATUS_INIT;
- val |= ~0xffff;
- if (val != INTVAL (operands[2]))
- operands[2] = GEN_INT (val);
- return AS2 (and%L0,%k2,%k0);
- }
+ "div{l}\\t%2"
+ [(set_attr "type" "idiv")
+ (set_attr "ppro_uops" "few")])
- return AS2 (and%W0,%2,%0);
-}"
- [(set_attr "type" "binary")])
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (udiv:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "nonimmediate_operand" "")))
+ (set (match_operand:SI 3 "register_operand" "")
+ (umod:SI (match_dup 1) (match_dup 2)))
+ (clobber (reg:CC 17))]
+ "reload_completed"
+ [(parallel [(set (match_dup 3) (const_int 0))
+ (clobber (reg:CC 17))])
+ (parallel [(set (match_dup 0)
+ (udiv:SI (match_dup 1) (match_dup 2)))
+ (set (match_dup 3)
+ (umod:SI (match_dup 1) (match_dup 2)))
+ (use (match_dup 3))
+ (clobber (reg:CC 17))])]
+ "")
-(define_insn "andqi3"
- [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q")
- (and:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
- (match_operand:QI 2 "general_operand" "qn,qmn")))]
+(define_expand "udivmodhi4"
+ [(parallel [(set (match_dup 4) (const_int 0))
+ (clobber (reg:CC 17))])
+ (parallel [(set (match_operand:HI 0 "register_operand" "=a")
+ (udiv:HI (match_operand:HI 1 "register_operand" "0")
+ (match_operand:HI 2 "nonimmediate_operand" "rm")))
+ (set (match_operand:HI 3 "register_operand" "=&d")
+ (umod:HI (match_dup 1) (match_dup 2)))
+ (use (match_dup 4))
+ (clobber (reg:CC 17))])]
""
- "* return AS2 (and%B0,%2,%0);"
- [(set_attr "type" "binary")])
-
-/* I am nervous about these two.. add them later..
-;I presume this means that we have something in say op0= eax which is small
-;and we want to and it with memory so we can do this by just an
-;andb m,%al and have success.
-(define_insn ""
- [(set (match_operand:SI 0 "general_operand" "=r")
- (and:SI (zero_extend:SI
- (match_operand:HI 1 "nonimmediate_operand" "rm"))
- (match_operand:SI 2 "general_operand" "0")))]
- "GET_CODE (operands[2]) == CONST_INT
- && (unsigned int) INTVAL (operands[2]) < (1 << GET_MODE_BITSIZE (HImode))"
- "and%W0 %1,%0")
-
-(define_insn ""
- [(set (match_operand:SI 0 "register_operand" "=q")
- (and:SI
- (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "qm"))
- (match_operand:SI 2 "register_operand" "0")))]
- "GET_CODE (operands[2]) == CONST_INT
- && (unsigned int) INTVAL (operands[2]) < (1 << GET_MODE_BITSIZE (QImode))"
- "and%L0 %1,%0")
+ "operands[4] = gen_reg_rtx (HImode);")
-*/
+(define_insn "*udivmodhi_1"
+ [(set (match_operand:HI 0 "register_operand" "=a")
+ (udiv:HI (match_operand:HI 1 "register_operand" "0")
+ (match_operand:HI 2 "nonimmediate_operand" "rm")))
+ (set (match_operand:HI 3 "register_operand" "=d")
+ (umod:HI (match_dup 1) (match_dup 2)))
+ (use (match_operand:HI 4 "register_operand" "3"))
+ (clobber (reg:CC 17))]
+ ""
+ "div{w}\\t%2"
+ [(set_attr "type" "idiv")
+ (set_attr "ppro_uops" "few")])
+
+;; We can not use div/idiv for double division, because it causes
+;; "division by zero" on the overflow and that's not what we expect
+;; from truncate. Because true (non truncating) double division is
+;; never generated, we can't create this insn anyway.
+;
+;(define_insn ""
+; [(set (match_operand:SI 0 "register_operand" "=a")
+; (truncate:SI
+; (udiv:DI (match_operand:DI 1 "register_operand" "A")
+; (zero_extend:DI
+; (match_operand:SI 2 "nonimmediate_operand" "rm")))))
+; (set (match_operand:SI 3 "register_operand" "=d")
+; (truncate:SI
+; (umod:DI (match_dup 1) (zero_extend:DI (match_dup 2)))))
+; (clobber (reg:CC 17))]
+; ""
+; "div{l}\\t{%2, %0|%0, %2}"
+; [(set_attr "type" "idiv")
+; (set_attr "ppro_uops" "few")])
\f
-;;- Bit set (inclusive or) instructions
-
-;; This optimizes known byte-wide operations to memory, and in some cases
-;; to QI registers.. Note that we don't want to use the QI registers too
-;; aggressively, because often the 32-bit register instruction is the same
-;; size, and likely to be faster on PentiumPro.
-(define_insn "iorsi3"
- [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
- (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0")
- (match_operand:SI 2 "general_operand" "ri,rm")))]
- ""
- "*
-{
- HOST_WIDE_INT intval;
- switch (GET_CODE (operands[2]))
- {
- case CONST_INT:
-
- if (REG_P (operands[0]) && ! QI_REG_P (operands[0]))
- break;
-
- /* don't try to optimize volatile accesses */
- if (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))
- break;
-
- intval = INTVAL (operands[2]);
- if ((intval & ~0xff) == 0)
- {
- if (REG_P (operands[0]))
- {
- /* Do low byte access only for %eax or when high bit is set */
- if (REGNO (operands[0]) != 0 && !(intval & 0x80))
- break;
- }
-
-byte_or_operation:
- CC_STATUS_INIT;
-
- if (intval != INTVAL (operands[2]))
- operands[2] = GEN_INT (intval);
-
- if (intval == 0xff)
- return AS2 (mov%B0,%2,%b0);
-
- return AS2 (or%B0,%2,%b0);
- }
-
- /* second byte? */
- if ((intval & ~0xff00) == 0)
- {
- intval >>= 8;
-
- if (REG_P (operands[0]))
- {
- CC_STATUS_INIT;
- operands[2] = GEN_INT (intval);
- if (intval == 0xff)
- return AS2 (mov%B0,%2,%h0);
-
- return AS2 (or%B0,%2,%h0);
- }
-
- operands[0] = adj_offsettable_operand (operands[0], 1);
- goto byte_or_operation;
- }
-
- if (REG_P (operands[0]))
- break;
-
- /* third byte? */
- if ((intval & ~0xff0000) == 0)
- {
- intval >>= 16;
- operands[0] = adj_offsettable_operand (operands[0], 2);
- goto byte_or_operation;
- }
-
- /* fourth byte? */
- if ((intval & ~0xff000000) == 0)
- {
- intval = (intval >> 24) & 0xff;
- operands[0] = adj_offsettable_operand (operands[0], 3);
- goto byte_or_operation;
- }
-
- default:
- break;
- }
-
- return AS2 (or%L0,%2,%0);
-}"
- [(set_attr "type" "binary")])
-
-(define_insn "iorhi3"
- [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r")
- (ior:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
- (match_operand:HI 2 "general_operand" "ri,rm")))]
- ""
- "*
-{
- HOST_WIDE_INT intval;
- switch (GET_CODE (operands[2]))
- {
- case CONST_INT:
-
- if (REG_P (operands[0]) && ! QI_REG_P (operands[0]))
- break;
-
- /* don't try to optimize volatile accesses */
- if (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))
- break;
-
- intval = 0xffff & INTVAL (operands[2]);
-
- if ((intval & 0xff00) == 0)
- {
- if (REG_P (operands[0]))
- {
- /* Do low byte access only for %eax or when high bit is set */
- if (REGNO (operands[0]) != 0 && !(intval & 0x80))
- break;
- }
-
-byte_or_operation:
- CC_STATUS_INIT;
-
- if (intval == 0xff)
- return AS2 (mov%B0,%2,%b0);
-
- return AS2 (or%B0,%2,%b0);
- }
-
- /* high byte? */
- if ((intval & 0xff) == 0)
- {
- intval >>= 8;
- operands[2] = GEN_INT (intval);
-
- if (REG_P (operands[0]))
- {
- CC_STATUS_INIT;
- if (intval == 0xff)
- return AS2 (mov%B0,%2,%h0);
-
- return AS2 (or%B0,%2,%h0);
- }
-
- operands[0] = adj_offsettable_operand (operands[0], 1);
-
- goto byte_or_operation;
- }
+;;- Logical AND instructions
+
+;; On Pentium, "test imm, reg" is pairable only with eax, ax, and al.
+;; Note that this excludes ah.
+
+(define_insn "testsi_1"
+ [(set (reg:CCNO 17)
+ (compare:CCNO (and:SI (match_operand:SI 0 "nonimmediate_operand" "%*a,r,rm")
+ (match_operand:SI 1 "general_operand" "in,in,rin"))
+ (const_int 0)))]
+ ""
+ "test{l}\\t{%1, %0|%0, %1}"
+ [(set_attr "type" "icmp")
+ (set_attr "pent_pair" "uv,np,uv")])
+
+(define_insn "*testhi_1"
+ [(set (reg:CCNO 17)
+ (compare:CCNO (and:HI (match_operand:HI 0 "nonimmediate_operand" "%*a,r,rm")
+ (match_operand:HI 1 "general_operand" "n,n,rn"))
+ (const_int 0)))]
+ ""
+ "test{w}\\t{%1, %0|%0, %1}"
+ [(set_attr "type" "icmp")
+ (set_attr "pent_pair" "uv,np,uv")])
+
+(define_insn "testqi_1"
+ [(set (reg:CCNO 17)
+ (compare:CCNO (and:QI (match_operand:QI 0 "nonimmediate_operand" "%*a,q,qm")
+ (match_operand:QI 1 "general_operand" "n,n,qn"))
+ (const_int 0)))]
+ ""
+ "test{b}\\t{%1, %0|%0, %1}"
+ [(set_attr "type" "icmp")
+ (set_attr "pent_pair" "uv,np,uv")])
+
+;; ??? A bug in recog prevents it from recognizing a const_int as an
+;; operand to zero_extend in andqi_ext_1. It was checking explicitly
+;; for a QImode operand, which of course failed.
+
+(define_insn "testqi_ext_0"
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (and:SI
+ (zero_extract:SI
+ (match_operand 0 "ext_register_operand" "q")
+ (const_int 8)
+ (const_int 8))
+ (match_operand 1 "const_int_operand" "n"))
+ (const_int 0)))]
+ "(unsigned HOST_WIDE_INT) INTVAL (operands[1]) <= 0xff"
+ "test{b}\\t{%1, %h0|%h0, %1}"
+ [(set_attr "type" "icmp")
+ (set_attr "pent_pair" "np")])
+
+(define_insn "*testqi_ext_1"
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (and:SI
+ (zero_extract:SI
+ (match_operand 0 "ext_register_operand" "q")
+ (const_int 8)
+ (const_int 8))
+ (zero_extend:SI
+ (match_operand:QI 1 "nonimmediate_operand" "qm")))
+ (const_int 0)))]
+ ""
+ "test{b}\\t{%1, %h0|%h0, %1}"
+ [(set_attr "type" "icmp")])
+
+(define_insn "*testqi_ext_2"
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (and:SI
+ (zero_extract:SI
+ (match_operand 0 "ext_register_operand" "q")
+ (const_int 8)
+ (const_int 8))
+ (zero_extract:SI
+ (match_operand 1 "ext_register_operand" "q")
+ (const_int 8)
+ (const_int 8)))
+ (const_int 0)))]
+ ""
+ "test{b}\\t{%h1, %h0|%h0, %h1}"
+ [(set_attr "type" "icmp")])
+
+;; Combine likes to form bit extractions for some tests. Humor it.
+(define_insn ""
+ [(set (reg:CCNO 17)
+ (compare:CCNO (zero_extract:SI
+ (match_operand 0 "nonimmediate_operand" "rm")
+ (match_operand:SI 1 "const_int_operand" "")
+ (match_operand:SI 2 "const_int_operand" ""))
+ (const_int 0)))]
+ "GET_MODE (operands[0]) == SImode
+ || GET_MODE (operands[0]) == HImode
+ || GET_MODE (operands[0]) == QImode"
+ "#")
- default:
- break;
- }
+(define_split
+ [(set (reg:CCNO 17)
+ (compare:CCNO (zero_extract:SI
+ (match_operand 0 "nonimmediate_operand" "rm")
+ (match_operand:SI 1 "const_int_operand" "")
+ (match_operand:SI 2 "const_int_operand" ""))
+ (const_int 0)))]
+ ""
+ [(set (reg:CCNO 17) (compare:CCNO (match_dup 3) (const_int 0)))]
+ "
+{
+ HOST_WIDE_INT len = INTVAL (operands[1]);
+ HOST_WIDE_INT pos = INTVAL (operands[2]);
+ HOST_WIDE_INT mask;
+ enum machine_mode mode;
- if (REG_P (operands[0])
- && i386_aligned_p (operands[2]))
+ mode = GET_MODE (operands[0]);
+ if (GET_CODE (operands[0]) == MEM)
{
- CC_STATUS_INIT;
- operands[2] = i386_sext16_if_const (operands[2]);
- return AS2 (or%L0,%k2,%k0);
+ /* ??? Combine likes to put non-volatile mem extractions in QImode
+ no matter the size of the test. So find a mode that works. */
+ if (! MEM_VOLATILE_P (operands[0]))
+ {
+ mode = smallest_mode_for_size (pos + len, MODE_INT);
+ operands[0] = change_address (operands[0], mode, NULL_RTX);
+ }
}
-
- if (GET_CODE (operands[2]) == CONST_INT
- && i386_aligned_p (operands[0]))
+ else if (mode == HImode && pos + len <= 8)
{
- CC_STATUS_INIT;
- intval = 0xffff & INTVAL (operands[2]);
- if (intval != INTVAL (operands[2]))
- operands[2] = GEN_INT (intval);
- return AS2 (or%L0,%2,%k0);
+ /* Small HImode tests can be converted to QImode. */
+ mode = QImode;
+ operands[0] = gen_lowpart (QImode, operands[0]);
}
- return AS2 (or%W0,%2,%0);
-}"
- [(set_attr "type" "binary")])
+ mask = ((HOST_WIDE_INT)1 << (pos + len)) - 1;
+ mask &= ~(((HOST_WIDE_INT)1 << pos) - 1);
-(define_insn "iorqi3"
- [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q")
- (ior:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
- (match_operand:QI 2 "general_operand" "qn,qmn")))]
- ""
- "* return AS2 (or%B0,%2,%0);"
- [(set_attr "type" "binary")])
-\f
-;;- xor instructions
+ operands[3] = gen_rtx_AND (mode, operands[0], GEN_INT (mask));
+}")
-(define_insn "xorsi3"
- [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
- (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0")
- (match_operand:SI 2 "general_operand" "ri,rm")))]
- ""
+;; %%% This used to optimize known byte-wide and operations to memory,
+;; and sometimes to QImode registers. If this is considered useful,
+;; it should be done with splitters.
+
+(define_expand "andsi3"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "")
+ (and:SI (match_operand:SI 1 "nonimmediate_operand" "")
+ (match_operand:SI 2 "general_operand" "")))
+ (clobber (reg:CC 17))]
+ ""
+ "ix86_expand_binary_operator (AND, SImode, operands); DONE;")
+
+(define_insn "*andsi_1"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r,r")
+ (and:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,qm")
+ (match_operand:SI 2 "general_operand" "ri,rm,L")))
+ (clobber (reg:CC 17))]
+ "ix86_binary_operator_ok (AND, SImode, operands)"
"*
{
- HOST_WIDE_INT intval;
- switch (GET_CODE (operands[2]))
+ switch (get_attr_type (insn))
{
- case CONST_INT:
-
- if (REG_P (operands[0]) && ! QI_REG_P (operands[0]))
- break;
-
- /* don't try to optimize volatile accesses */
- if (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))
- break;
+ case TYPE_IMOVX:
+ {
+ enum machine_mode mode;
- intval = INTVAL (operands[2]);
- if ((intval & ~0xff) == 0)
- {
- if (REG_P (operands[0]))
- {
- /* Do low byte access only for %eax or when high bit is set */
- if (REGNO (operands[0]) != 0 && !(intval & 0x80))
- break;
- }
+ if (GET_CODE (operands[2]) != CONST_INT)
+ abort ();
+ if (INTVAL (operands[2]) == 0xff)
+ mode = QImode;
+ else if (INTVAL (operands[2]) == 0xffff)
+ mode = HImode;
+ else
+ abort ();
+
+ operands[1] = gen_lowpart (mode, operands[1]);
+ if (mode == QImode)
+ return \"movz{bl|x}\\t{%1,%0|%0, %1}\";
+ else
+ return \"movz{wl|x}\\t{%1,%0|%0, %1}\";
+ }
-byte_xor_operation:
- CC_STATUS_INIT;
-
- if (intval == 0xff
- && (!TARGET_PENTIUM || optimize_size
- || (GET_CODE (operands[0]) == MEM
- && memory_address_info (XEXP (operands[0], 0), 1))))
- return AS1 (not%B0,%b0);
-
- if (intval != INTVAL (operands[2]))
- operands[2] = GEN_INT (intval);
- return AS2 (xor%B0,%2,%b0);
- }
+ default:
+ if (! rtx_equal_p (operands[0], operands[1]))
+ abort ();
- /* second byte? */
- if ((intval & ~0xff00) == 0)
+ /* If operands[2] is an immediate, we may be able to use xor.
+ Walk through the cases to figure out which subword we are
+ supposed to clear. */
+ if (REG_P (operands[0])
+ && GET_CODE (operands[2]) == CONST_INT
+ && (optimize_size
+ || ! TARGET_PARTIAL_REG_STALL))
{
- intval >>= 8;
-
- if (REG_P (operands[0]))
+ if (INTVAL (operands[2]) == (HOST_WIDE_INT) 0xffff0000
+ && optimize_size)
+ return \"xor{w}\\t{%w0, %w0|%w0, %w0}\";
+ if (QI_REG_P (operands[0]))
{
- CC_STATUS_INIT;
- if (intval == 0xff
- && (!TARGET_PENTIUM || optimize_size
- || (GET_CODE (operands[0]) == MEM
- && memory_address_info (XEXP (operands[0], 0), 1))))
- return AS1 (not%B0,%h0);
-
- operands[2] = GEN_INT (intval);
- return AS2 (xor%B0,%2,%h0);
+ if (INTVAL (operands[2]) == (HOST_WIDE_INT) 0xffffff00)
+ return \"xor{b}\\t{%b0, %b0|%b0, %b0}\";
+ if (INTVAL (operands[2]) == (HOST_WIDE_INT) 0xffff00ff)
+ return \"xor{b}\\t{%h0, %h0|%h0, %h0}\";
}
-
- operands[0] = adj_offsettable_operand (operands[0], 1);
-
- goto byte_xor_operation;
- }
-
- if (REG_P (operands[0]))
- break;
-
- /* third byte? */
- if ((intval & ~0xff0000) == 0)
- {
- intval >>= 16;
- operands[0] = adj_offsettable_operand (operands[0], 2);
- goto byte_xor_operation;
- }
-
- /* fourth byte? */
- if ((intval & ~0xff000000) == 0)
- {
- intval = (intval >> 24) & 0xff;
- operands[0] = adj_offsettable_operand (operands[0], 3);
- goto byte_xor_operation;
}
-
- default:
- break;
+ return \"and{l}\\t{%2, %0|%0, %2}\";
}
-
- return AS2 (xor%L0,%2,%0);
}"
- [(set_attr "type" "binary")])
-
-(define_insn "xorhi3"
- [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r")
- (xor:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
- (match_operand:HI 2 "general_operand" "ri,rm")))]
- ""
+ [(set_attr "type" "alu,alu,imovx")])
+
+(define_insn "*andsi_2"
+ [(set (reg:CCNO 17)
+ (compare:CCNO (and:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:SI 2 "general_operand" "rim,ri"))
+ (const_int 0)))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=r,rm")
+ (and:SI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (AND, SImode, operands)"
+ "and{l}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_expand "andhi3"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "")
+ (and:HI (match_operand:HI 1 "nonimmediate_operand" "")
+ (match_operand:HI 2 "general_operand" "")))
+ (clobber (reg:CC 17))]
+ ""
+ "ix86_expand_binary_operator (AND, HImode, operands); DONE;")
+
+(define_insn "*andhi_1"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r,r")
+ (and:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0,qm")
+ (match_operand:HI 2 "general_operand" "ri,rm,L")))
+ (clobber (reg:CC 17))]
+ "ix86_binary_operator_ok (AND, HImode, operands)"
"*
{
- if (GET_CODE (operands[2]) == CONST_INT
- && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
+ switch (get_attr_type (insn))
{
- /* Can we ignore the upper byte? */
- if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
- && (INTVAL (operands[2]) & 0xff00) == 0)
- {
- CC_STATUS_INIT;
- if (INTVAL (operands[2]) & 0xffff0000)
- operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
-
- if (INTVAL (operands[2]) == 0xff
- && (!TARGET_PENTIUM || optimize_size
- || (GET_CODE (operands[0]) == MEM
- && memory_address_info (XEXP (operands[0], 0), 1))))
- return AS1 (not%B0,%b0);
+ case TYPE_IMOVX:
+ if (GET_CODE (operands[2]) != CONST_INT)
+ abort ();
+ if (INTVAL (operands[2]) == 0xff)
+ return \"movz{bl|x}\\t{%b1, %k0|%k0, %b1}\";
+ abort ();
- return AS2 (xor%B0,%2,%b0);
- }
+ default:
+ if (! rtx_equal_p (operands[0], operands[1]))
+ abort ();
- /* Can we ignore the lower byte? */
- /* ??? what about offsettable memory references? */
- if (QI_REG_P (operands[0])
- && (INTVAL (operands[2]) & 0xff) == 0)
+ /* If operands[2] is an immediate, we may be able to use xor.
+ Walk through the cases to figure out which subword we are
+ supposed to clear. */
+ /* %%% Do these as splits. They get length_prefix wrong. */
+ if (GET_CODE (operands[2]) == CONST_INT
+ && QI_REG_P (operands[0])
+ && (optimize_size
+ || ! TARGET_PARTIAL_REG_STALL))
{
- CC_STATUS_INIT;
- operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);
-
- if (INTVAL (operands[2]) == 0xff
- && (!TARGET_PENTIUM || optimize_size
- || (GET_CODE (operands[0]) == MEM
- && memory_address_info (XEXP (operands[0], 0), 1))))
- return AS1 (not%B0,%h0);
-
- return AS2 (xor%B0,%2,%h0);
+ if ((INTVAL (operands[2]) & 0xffff) == 0xff00)
+ return \"xor{b}\\t{%b0, %b0|%b0, %b0}\";
+ if ((INTVAL (operands[2]) & 0xffff) == 0x00ff)
+ return \"xor{b}\\t{%h0, %h0|%h0, %h0}\";
}
- }
- if (REG_P (operands[0])
- && i386_aligned_p (operands[2]))
- {
- CC_STATUS_INIT;
- operands[2] = i386_sext16_if_const (operands[2]);
- return AS2 (xor%L0,%k2,%k0);
+ return \"and{w}\\t{%2, %0|%0, %2}\";
}
+}"
+ [(set_attr "type" "alu,alu,imovx")])
- if (GET_CODE (operands[2]) == CONST_INT
- && i386_aligned_p (operands[0]))
- {
- HOST_WIDE_INT intval;
- CC_STATUS_INIT;
- intval = 0xffff & INTVAL (operands[2]);
- if (intval != INTVAL (operands[2]))
- operands[2] = GEN_INT (intval);
- return AS2 (xor%L0,%2,%k0);
- }
+(define_split
+ [(set (match_operand:HI 0 "register_operand" "")
+ (and:HI (match_operand:HI 1 "register_operand" "")
+ (match_operand:HI 2 "nonmemory_operand" "")))
+ (clobber (reg:CC 17))]
+ "! TARGET_PARTIAL_REG_STALL && reload_completed"
+ [(parallel [(set (match_dup 0) (and:SI (match_dup 1) (match_dup 2)))
+ (clobber (reg:CC 17))])]
+ "operands[0] = gen_lowpart (SImode, operands[0]);
+ operands[1] = gen_lowpart (SImode, operands[1]);
+ operands[2] = gen_lowpart (SImode, operands[2]);")
+
+(define_insn "*andhi_2"
+ [(set (reg:CCNO 17)
+ (compare:CCNO (and:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:HI 2 "general_operand" "rim,ri"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=r,rm")
+ (and:HI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (AND, HImode, operands)"
+ "and{w}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
- return AS2 (xor%W0,%2,%0);
-}"
- [(set_attr "type" "binary")])
+(define_split
+ [(set (reg:CCNO 17)
+ (compare:CCNO (and:HI (match_operand:HI 1 "register_operand" "")
+ (match_operand:HI 2 "immediate_operand" ""))
+ (const_int 0)))
+ (set (match_operand:HI 0 "register_operand" "")
+ (and:HI (match_dup 1) (match_dup 2)))]
+ "! TARGET_PARTIAL_REG_STALL && reload_completed"
+ [(parallel [(set (reg:CCNO 17)
+ (compare:CCNO (and:SI (match_dup 1) (match_dup 2))
+ (const_int 0)))
+ (set (match_dup 0)
+ (and:SI (match_dup 1) (match_dup 2)))])]
+ "operands[0] = gen_lowpart (SImode, operands[0]);
+ operands[1] = gen_lowpart (SImode, operands[1]);
+ operands[2] = gen_lowpart (SImode, operands[2]);")
-(define_insn "xorqi3"
- [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q")
- (xor:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
- (match_operand:QI 2 "general_operand" "qn,qm")))]
- ""
- "* return AS2 (xor%B0,%2,%0);"
- [(set_attr "type" "binary")])
+(define_expand "andqi3"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "")
+ (and:QI (match_operand:QI 1 "nonimmediate_operand" "")
+ (match_operand:QI 2 "general_operand" "")))
+ (clobber (reg:CC 17))]
+ ""
+ "ix86_expand_binary_operator (AND, QImode, operands); DONE;")
+
+;; %%% Potential partial reg stall on alternative 2. What to do?
+(define_insn "*andqi_1"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q,*r")
+ (and:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0")
+ (match_operand:QI 2 "general_operand" "qi,qmi,*ri")))
+ (clobber (reg:CC 17))]
+ "ix86_binary_operator_ok (AND, QImode, operands)"
+ "@
+ and{b}\\t{%2, %0|%0, %2}
+ and{b}\\t{%2, %0|%0, %2}
+ and{l}\\t{%k2, %k0|%k0, %k2}"
+ [(set_attr "type" "alu")])
+
+(define_insn "*andqi_2"
+ [(set (reg:CCNO 17)
+ (compare:CCNO (and:QI
+ (match_operand:QI 1 "nonimmediate_operand" "%0,0,0")
+ (match_operand:QI 2 "general_operand" "qim,qi,i"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "nonimmediate_operand" "=q,qm,*r")
+ (and:QI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (AND, QImode, operands)"
+ "@
+ and{b}\\t{%2, %0|%0, %2}
+ and{b}\\t{%2, %0|%0, %2}
+ and{l}\\t{%2, %k0|%k0, %2}"
+ [(set_attr "type" "alu")])
+
+;; ??? A bug in recog prevents it from recognizing a const_int as an
+;; operand to zero_extend in andqi_ext_1. It was checking explicitly
+;; for a QImode operand, which of course failed.
+
+(define_insn "andqi_ext_0"
+ [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=q")
+ (const_int 8)
+ (const_int 8))
+ (and:SI
+ (zero_extract:SI
+ (match_operand 1 "ext_register_operand" "0")
+ (const_int 8)
+ (const_int 8))
+ (match_operand 2 "const_int_operand" "n")))
+ (clobber (reg:CC 17))]
+ "(unsigned HOST_WIDE_INT)INTVAL (operands[2]) <= 0xff"
+ "and{b}\\t{%2, %h0|%h0, %2}"
+ [(set_attr "type" "alu")])
+
+;; Generated by peephole translating test to and. This shows up
+;; often in fp comparisons.
+
+(define_insn "*andqi_ext_0_cc"
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (and:SI
+ (zero_extract:SI
+ (match_operand 1 "ext_register_operand" "q")
+ (const_int 8)
+ (const_int 8))
+ (match_operand 2 "const_int_operand" "n"))
+ (const_int 0)))
+ (set (zero_extract:SI (match_operand 0 "ext_register_operand" "=q")
+ (const_int 8)
+ (const_int 8))
+ (and:SI
+ (zero_extract:SI
+ (match_dup 1)
+ (const_int 8)
+ (const_int 8))
+ (match_dup 2)))]
+ "(unsigned HOST_WIDE_INT)INTVAL (operands[2]) <= 0xff"
+ "and{b}\\t{%2, %h0|%h0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_insn "*andqi_ext_1"
+ [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=q")
+ (const_int 8)
+ (const_int 8))
+ (and:SI
+ (zero_extract:SI
+ (match_operand 1 "ext_register_operand" "0")
+ (const_int 8)
+ (const_int 8))
+ (zero_extend:SI
+ (match_operand:QI 2 "general_operand" "qm"))))
+ (clobber (reg:CC 17))]
+ ""
+ "and{b}\\t{%2, %h0|%h0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_insn "*andqi_ext_2"
+ [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=q")
+ (const_int 8)
+ (const_int 8))
+ (and:SI
+ (zero_extract:SI
+ (match_operand 1 "ext_register_operand" "%0")
+ (const_int 8)
+ (const_int 8))
+ (zero_extract:SI
+ (match_operand 2 "ext_register_operand" "q")
+ (const_int 8)
+ (const_int 8))))
+ (clobber (reg:CC 17))]
+ ""
+ "and{b}\\t{%h2, %h0|%h0, %h2}"
+ [(set_attr "type" "alu")])
\f
-;; logical operations for DImode
+;; Logical inclusive OR instructions
-(define_insn "anddi3"
- [(set (match_operand:DI 0 "general_operand" "=&r,&ro")
- (and:DI (match_operand:DI 1 "general_operand" "%0,0")
- (match_operand:DI 2 "general_operand" "oriF,riF")))]
+;; %%% This used to optimize known byte-wide and operations to memory.
+;; If this is considered useful, it should be done with splitters.
+
+(define_expand "iorsi3"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "")
+ (ior:SI (match_operand:SI 1 "nonimmediate_operand" "")
+ (match_operand:SI 2 "general_operand" "")))
+ (clobber (reg:CC 17))]
""
- "#"
- [(set_attr "type" "binary")])
+ "ix86_expand_binary_operator (IOR, SImode, operands); DONE;")
+
+(define_insn "*iorsi_1"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
+ (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:SI 2 "general_operand" "ri,rmi")))
+ (clobber (reg:CC 17))]
+ "ix86_binary_operator_ok (IOR, SImode, operands)"
+ "or{l}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_insn "*iorsi_2"
+ [(set (reg:CCNO 17)
+ (compare:CCNO (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:SI 2 "general_operand" "rim,ri"))
+ (const_int 0)))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=r,rm")
+ (ior:SI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (IOR, SImode, operands)"
+ "or{l}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_expand "iorhi3"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "")
+ (ior:HI (match_operand:HI 1 "nonimmediate_operand" "")
+ (match_operand:HI 2 "general_operand" "")))
+ (clobber (reg:CC 17))]
+ ""
+ "ix86_expand_binary_operator (IOR, HImode, operands); DONE;")
+
+(define_insn "*iorhi_1"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=r,m")
+ (ior:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:HI 2 "general_operand" "rmi,ri")))
+ (clobber (reg:CC 17))]
+ "ix86_binary_operator_ok (IOR, HImode, operands)"
+ "or{w}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_split
+ [(set (match_operand:HI 0 "register_operand" "")
+ (ior:HI (match_operand:HI 1 "register_operand" "")
+ (match_operand:HI 2 "nonmemory_operand" "")))
+ (clobber (reg:CC 17))]
+ "! TARGET_PARTIAL_REG_STALL && reload_completed"
+ [(parallel [(set (match_dup 0) (ior:SI (match_dup 1) (match_dup 2)))
+ (clobber (reg:CC 17))])]
+ "operands[0] = gen_lowpart (SImode, operands[0]);
+ operands[1] = gen_lowpart (SImode, operands[1]);
+ operands[2] = gen_lowpart (SImode, operands[2]);")
+
+(define_insn "*iorhi_2"
+ [(set (reg:CCNO 17)
+ (compare:CCNO (ior:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:HI 2 "general_operand" "rim,ri"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=r,rm")
+ (ior:HI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (IOR, HImode, operands)"
+ "or{w}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_expand "iorqi3"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "")
+ (ior:QI (match_operand:QI 1 "nonimmediate_operand" "")
+ (match_operand:QI 2 "general_operand" "")))
+ (clobber (reg:CC 17))]
+ ""
+ "ix86_expand_binary_operator (IOR, QImode, operands); DONE;")
+
+;; %%% Potential partial reg stall on alternative 2. What to do?
+(define_insn "*iorqi_1"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=q,m,*r")
+ (ior:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0")
+ (match_operand:QI 2 "general_operand" "qmi,qi,*ri")))
+ (clobber (reg:CC 17))]
+ "ix86_binary_operator_ok (IOR, QImode, operands)"
+ "@
+ or{b}\\t{%2, %0|%0, %2}
+ or{b}\\t{%2, %0|%0, %2}
+ or{l}\\t{%k2, %k0|%k0, %k2}"
+ [(set_attr "type" "alu")])
+
+(define_insn "*iorqi_2"
+ [(set (reg:CCNO 17)
+ (compare:CCNO (ior:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:QI 2 "general_operand" "qim,qi"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "nonimmediate_operand" "=q,qm")
+ (ior:QI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (IOR, QImode, operands)"
+ "or{b}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+\f
+;; Logical XOR instructions
+;; %%% This used to optimize known byte-wide and operations to memory.
+;; If this is considered useful, it should be done with splitters.
-(define_insn "iordi3"
- [(set (match_operand:DI 0 "general_operand" "=&r,&ro")
- (ior:DI (match_operand:DI 1 "general_operand" "%0,0")
- (match_operand:DI 2 "general_operand" "oriF,riF")))]
+(define_expand "xorsi3"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "")
+ (xor:SI (match_operand:SI 1 "nonimmediate_operand" "")
+ (match_operand:SI 2 "general_operand" "")))
+ (clobber (reg:CC 17))]
""
- "#"
- [(set_attr "type" "binary")])
+ "ix86_expand_binary_operator (XOR, SImode, operands); DONE;")
-(define_insn "xordi3"
- [(set (match_operand:DI 0 "general_operand" "=&r,&ro")
- (xor:DI (match_operand:DI 1 "general_operand" "%0,0")
- (match_operand:DI 2 "general_operand" "oriF,riF")))]
+(define_insn "*xorsi_1"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
+ (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:SI 2 "general_operand" "ri,rm")))
+ (clobber (reg:CC 17))]
+ "ix86_binary_operator_ok (XOR, SImode, operands)"
+ "xor{l}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_insn "*xorsi_2"
+ [(set (reg:CCNO 17)
+ (compare:CCNO (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:SI 2 "general_operand" "rim,ri"))
+ (const_int 0)))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=r,rm")
+ (xor:SI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (XOR, SImode, operands)"
+ "xor{l}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_expand "xorhi3"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "")
+ (xor:HI (match_operand:HI 1 "nonimmediate_operand" "")
+ (match_operand:HI 2 "general_operand" "")))
+ (clobber (reg:CC 17))]
""
- "#"
- [(set_attr "type" "binary")])
+ "ix86_expand_binary_operator (XOR, HImode, operands); DONE;")
+
+(define_insn "*xorhi_1"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=r,m")
+ (xor:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:HI 2 "general_operand" "rmi,ri")))
+ (clobber (reg:CC 17))]
+ "ix86_binary_operator_ok (XOR, HImode, operands)"
+ "xor{w}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
(define_split
- [(set (match_operand:DI 0 "general_operand" "")
- (match_operator:DI 3 "ix86_logical_operator"
- [(match_operand:DI 1 "general_operand" "")
- (match_operand:DI 2 "general_operand" "")]))]
- ""
- [(set (match_dup 4) (match_op_dup:SI 3 [(match_dup 6) (match_dup 8)]))
- (set (match_dup 5) (match_op_dup:SI 3 [(match_dup 7) (match_dup 9)]))]
- "split_di (&operands[0], 1, &operands[4], &operands[5]);
- split_di (&operands[1], 1, &operands[6], &operands[7]);
- split_di (&operands[2], 1, &operands[8], &operands[9]);")
+ [(set (match_operand:HI 0 "register_operand" "")
+ (xor:HI (match_operand:HI 1 "register_operand" "")
+ (match_operand:HI 2 "nonmemory_operand" "")))
+ (clobber (reg:CC 17))]
+ "! TARGET_PARTIAL_REG_STALL && reload_completed"
+ [(parallel [(set (match_dup 0) (xor:SI (match_dup 1) (match_dup 2)))
+ (clobber (reg:CC 17))])]
+ "operands[0] = gen_lowpart (SImode, operands[0]);
+ operands[1] = gen_lowpart (SImode, operands[1]);
+ operands[2] = gen_lowpart (SImode, operands[2]);")
+
+(define_insn "*xorhi_2"
+ [(set (reg:CCNO 17)
+ (compare:CCNO (xor:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:HI 2 "general_operand" "rim,ri"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=r,rm")
+ (xor:HI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (XOR, HImode, operands)"
+ "xor{w}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_expand "xorqi3"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "")
+ (xor:QI (match_operand:QI 1 "nonimmediate_operand" "")
+ (match_operand:QI 2 "general_operand" "")))
+ (clobber (reg:CC 17))]
+ ""
+ "ix86_expand_binary_operator (XOR, QImode, operands); DONE;")
+
+;; %%% Potential partial reg stall on alternative 2. What to do?
+(define_insn "*xorqi_1"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=q,m,*r")
+ (xor:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0")
+ (match_operand:QI 2 "general_operand" "qmi,qi,*ri")))
+ (clobber (reg:CC 17))]
+ "ix86_binary_operator_ok (XOR, QImode, operands)"
+ "@
+ xor{b}\\t{%2, %0|%0, %2}
+ xor{b}\\t{%2, %0|%0, %2}
+ xor{l}\\t{%k2, %k0|%k0, %k2}"
+ [(set_attr "type" "alu")])
+
+(define_insn "xorcqi_1"
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (xor:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:QI 2 "general_operand" "qim,qi"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "nonimmediate_operand" "=q,qm")
+ (xor:QI (match_dup 1) (match_dup 2)))]
+ "ix86_binary_operator_ok (XOR, QImode, operands)"
+ "xor{b}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "alu")])
+
+(define_insn "xorcqi_ext_1"
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (xor:SI
+ (zero_extract:SI
+ (match_operand 1 "ext_register_operand" "0")
+ (const_int 8)
+ (const_int 8))
+ (match_operand:QI 2 "general_operand" "qmn"))
+ (const_int 0)))
+ (set (zero_extract:SI (match_operand 0 "ext_register_operand" "=q")
+ (const_int 8)
+ (const_int 8))
+ (xor:SI
+ (zero_extract:SI (match_dup 1) (const_int 8) (const_int 8))
+ (match_dup 2)))]
+ ""
+ "xor{b}\\t{%2, %h0|%h0, %2}"
+ [(set_attr "type" "alu")])
+\f
+;; Negation instructions
-;;- negation instructions
+;; %%% define_expand from the very first?
(define_insn "negdi2"
- [(set (match_operand:DI 0 "general_operand" "=&ro")
- (neg:DI (match_operand:DI 1 "general_operand" "0")))]
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=ro")
+ (neg:DI (match_operand:DI 1 "general_operand" "0")))
+ (clobber (reg:CC 17))]
""
- "*
-{
- rtx xops[2], low[1], high[1];
-
- CC_STATUS_INIT;
-
- split_di (operands, 1, low, high);
- xops[0] = const0_rtx;
- xops[1] = high[0];
+ "#")
- output_asm_insn (AS1 (neg%L0,%0), low);
- output_asm_insn (AS2 (adc%L1,%0,%1), xops);
- output_asm_insn (AS1 (neg%L0,%0), high);
- RET;
-}")
+(define_split
+ [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (neg:DI (match_operand:DI 1 "general_operand" "")))
+ (clobber (reg:CC 17))]
+ "cse_not_expected"
+ [(parallel
+ [(set (reg:CCNO 17)
+ (compare:CCNO (neg:SI (match_dup 2)) (const_int 0)))
+ (set (match_dup 0) (neg:SI (match_dup 2)))])
+ (parallel
+ [(set (match_dup 1)
+ (plus:SI (match_dup 3)
+ (plus:SI (const_int 0)
+ (ltu:SI (reg:CC 17) (const_int 0)))))
+ (clobber (reg:CC 17))])
+ (parallel
+ [(set (match_dup 1)
+ (neg:SI (match_dup 1)))
+ (clobber (reg:CC 17))])]
+ "split_di (operands+1, 1, operands+2, operands+3);
+ split_di (operands+0, 1, operands+0, operands+1);")
(define_insn "negsi2"
[(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
- (neg:SI (match_operand:SI 1 "nonimmediate_operand" "0")))]
+ (neg:SI (match_operand:SI 1 "nonimmediate_operand" "0")))
+ (clobber (reg:CC 17))]
+ ""
+ "neg{l}\\t%0"
+ [(set_attr "type" "negnot")])
+
+(define_insn ""
+ [(set (reg:CCNO 17)
+ (compare:CCNO (neg:SI (match_operand:SI 1 "nonimmediate_operand" "0"))
+ (const_int 0)))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=rm")
+ (neg:SI (match_dup 1)))]
""
- "neg%L0 %0")
+ "neg{l}\\t%0"
+ [(set_attr "type" "negnot")])
+
+(define_insn ""
+ [(set (reg:CC 17)
+ (compare:CC (neg:SI (match_operand:SI 1 "nonimmediate_operand" "0"))
+ (const_int 0)))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=rm")
+ (neg:SI (match_dup 1)))]
+ ""
+ "neg{l}\\t%0"
+ [(set_attr "type" "negnot")])
(define_insn "neghi2"
[(set (match_operand:HI 0 "nonimmediate_operand" "=rm")
- (neg:HI (match_operand:HI 1 "nonimmediate_operand" "0")))]
+ (neg:HI (match_operand:HI 1 "nonimmediate_operand" "0")))
+ (clobber (reg:CC 17))]
""
- "*
- if (REG_P (operands[0]) && i386_cc_probably_useless_p (insn))
- {
- CC_STATUS_INIT;
- return AS1(neg%L0,%k0);
- }
- return AS1(neg%W0,%0);")
+ "neg{w}\\t%0"
+ [(set_attr "type" "negnot")])
+
+(define_insn ""
+ [(set (reg:CCNO 17)
+ (compare:CCNO (neg:HI (match_operand:HI 1 "nonimmediate_operand" "0"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=rm")
+ (neg:HI (match_dup 1)))]
+ ""
+ "neg{w}\\t%0"
+ [(set_attr "type" "negnot")])
+
+(define_insn ""
+ [(set (reg:CC 17)
+ (compare:CC (neg:HI (match_operand:HI 1 "nonimmediate_operand" "0"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=rm")
+ (neg:HI (match_dup 1)))]
+ ""
+ "neg{w}\\t%0"
+ [(set_attr "type" "negnot")])
(define_insn "negqi2"
[(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
- (neg:QI (match_operand:QI 1 "nonimmediate_operand" "0")))]
+ (neg:QI (match_operand:QI 1 "nonimmediate_operand" "0")))
+ (clobber (reg:CC 17))]
+ ""
+ "neg{b}\\t%0"
+ [(set_attr "type" "negnot")])
+
+(define_insn ""
+ [(set (reg:CCNO 17)
+ (compare:CCNO (neg:QI (match_operand:QI 1 "nonimmediate_operand" "0"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "nonimmediate_operand" "=qm")
+ (neg:QI (match_dup 1)))]
+ ""
+ "neg{b}\\t%0"
+ [(set_attr "type" "negnot")])
+
+(define_insn ""
+ [(set (reg:CC 17)
+ (compare:CC (neg:QI (match_operand:QI 1 "nonimmediate_operand" "0"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "nonimmediate_operand" "=qm")
+ (neg:QI (match_dup 1)))]
""
- "neg%B0 %0")
+ "neg{b}\\t%0"
+ [(set_attr "type" "negnot")])
(define_insn "negsf2"
[(set (match_operand:SF 0 "register_operand" "=f")
(neg:SF (match_operand:SF 1 "register_operand" "0")))]
"TARGET_80387"
"fchs"
- [(set_attr "type" "fpop")])
+ [(set_attr "type" "fsgn")
+ (set_attr "ppro_uops" "few")])
(define_insn "negdf2"
[(set (match_operand:DF 0 "register_operand" "=f")
(neg:DF (match_operand:DF 1 "register_operand" "0")))]
"TARGET_80387"
"fchs"
- [(set_attr "type" "fpop")])
+ [(set_attr "type" "fsgn")
+ (set_attr "ppro_uops" "few")])
(define_insn ""
[(set (match_operand:DF 0 "register_operand" "=f")
- (neg:DF (float_extend:DF (match_operand:SF 1 "register_operand" "0"))))]
+ (neg:DF (float_extend:DF
+ (match_operand:SF 1 "register_operand" "0"))))]
"TARGET_80387"
"fchs"
- [(set_attr "type" "fpop")])
+ [(set_attr "type" "fsgn")
+ (set_attr "ppro_uops" "few")])
(define_insn "negxf2"
[(set (match_operand:XF 0 "register_operand" "=f")
(neg:XF (match_operand:XF 1 "register_operand" "0")))]
"TARGET_80387"
"fchs"
- [(set_attr "type" "fpop")])
+ [(set_attr "type" "fsgn")
+ (set_attr "ppro_uops" "few")])
+
+(define_insn ""
+ [(set (match_operand:XF 0 "register_operand" "=f")
+ (neg:XF (float_extend:XF
+ (match_operand:DF 1 "register_operand" "0"))))]
+ "TARGET_80387"
+ "fchs"
+ [(set_attr "type" "fsgn")
+ (set_attr "ppro_uops" "few")])
(define_insn ""
[(set (match_operand:XF 0 "register_operand" "=f")
- (neg:XF (float_extend:XF (match_operand:DF 1 "register_operand" "0"))))]
+ (neg:XF (float_extend:XF
+ (match_operand:SF 1 "register_operand" "0"))))]
"TARGET_80387"
"fchs"
- [(set_attr "type" "fpop")])
+ [(set_attr "type" "fsgn")
+ (set_attr "ppro_uops" "few")])
\f
;; Absolute value instructions
(abs:SF (match_operand:SF 1 "register_operand" "0")))]
"TARGET_80387"
"fabs"
- [(set_attr "type" "fpop")])
+ [(set_attr "type" "fsgn")])
(define_insn "absdf2"
[(set (match_operand:DF 0 "register_operand" "=f")
(abs:DF (match_operand:DF 1 "register_operand" "0")))]
"TARGET_80387"
"fabs"
- [(set_attr "type" "fpop")])
+ [(set_attr "type" "fsgn")])
(define_insn ""
[(set (match_operand:DF 0 "register_operand" "=f")
- (abs:DF (float_extend:DF (match_operand:SF 1 "register_operand" "0"))))]
+ (abs:DF (float_extend:DF
+ (match_operand:SF 1 "register_operand" "0"))))]
"TARGET_80387"
"fabs"
- [(set_attr "type" "fpop")])
+ [(set_attr "type" "fsgn")])
(define_insn "absxf2"
[(set (match_operand:XF 0 "register_operand" "=f")
(abs:XF (match_operand:XF 1 "register_operand" "0")))]
"TARGET_80387"
"fabs"
- [(set_attr "type" "fpop")])
+ [(set_attr "type" "fsgn")])
(define_insn ""
[(set (match_operand:XF 0 "register_operand" "=f")
- (abs:XF (float_extend:XF (match_operand:DF 1 "register_operand" "0"))))]
+ (abs:XF (float_extend:XF
+ (match_operand:DF 1 "register_operand" "0"))))]
"TARGET_80387"
"fabs"
- [(set_attr "type" "fpop")])
-
-(define_insn "sqrtsf2"
- [(set (match_operand:SF 0 "register_operand" "=f")
- (sqrt:SF (match_operand:SF 1 "register_operand" "0")))]
- "! TARGET_NO_FANCY_MATH_387 && TARGET_80387"
- "fsqrt")
-
-(define_insn "sqrtdf2"
- [(set (match_operand:DF 0 "register_operand" "=f")
- (sqrt:DF (match_operand:DF 1 "register_operand" "0")))]
- "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
- && (TARGET_IEEE_FP || flag_fast_math) "
- "fsqrt")
-
-(define_insn ""
- [(set (match_operand:DF 0 "register_operand" "=f")
- (sqrt:DF (float_extend:DF
- (match_operand:SF 1 "register_operand" "0"))))]
- "! TARGET_NO_FANCY_MATH_387 && TARGET_80387"
- "fsqrt")
-
-(define_insn "sqrtxf2"
- [(set (match_operand:XF 0 "register_operand" "=f")
- (sqrt:XF (match_operand:XF 1 "register_operand" "0")))]
- "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
- && (TARGET_IEEE_FP || flag_fast_math) "
- "fsqrt")
-
-(define_insn ""
- [(set (match_operand:XF 0 "register_operand" "=f")
- (sqrt:XF (float_extend:XF
- (match_operand:DF 1 "register_operand" "0"))))]
- "! TARGET_NO_FANCY_MATH_387 && TARGET_80387"
- "fsqrt")
-
-(define_insn ""
- [(set (match_operand:XF 0 "register_operand" "=f")
- (sqrt:XF (float_extend:XF
- (match_operand:SF 1 "register_operand" "0"))))]
- "! TARGET_NO_FANCY_MATH_387 && TARGET_80387"
- "fsqrt")
-
-(define_insn "sindf2"
- [(set (match_operand:DF 0 "register_operand" "=f")
- (unspec:DF [(match_operand:DF 1 "register_operand" "0")] 1))]
- "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
- "fsin")
-
-(define_insn "sinsf2"
- [(set (match_operand:SF 0 "register_operand" "=f")
- (unspec:SF [(match_operand:SF 1 "register_operand" "0")] 1))]
- "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
- "fsin")
+ [(set_attr "type" "fsgn")])
(define_insn ""
- [(set (match_operand:DF 0 "register_operand" "=f")
- (unspec:DF [(float_extend:DF
- (match_operand:SF 1 "register_operand" "0"))] 1))]
- "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
- "fsin")
-
-(define_insn "sinxf2"
- [(set (match_operand:XF 0 "register_operand" "=f")
- (unspec:XF [(match_operand:XF 1 "register_operand" "0")] 1))]
- "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
- "fsin")
-
-(define_insn "cosdf2"
- [(set (match_operand:DF 0 "register_operand" "=f")
- (unspec:DF [(match_operand:DF 1 "register_operand" "0")] 2))]
- "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
- "fcos")
-
-(define_insn "cossf2"
- [(set (match_operand:SF 0 "register_operand" "=f")
- (unspec:SF [(match_operand:SF 1 "register_operand" "0")] 2))]
- "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
- "fcos")
-
-(define_insn ""
- [(set (match_operand:DF 0 "register_operand" "=f")
- (unspec:DF [(float_extend:DF
- (match_operand:SF 1 "register_operand" "0"))] 2))]
- "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
- "fcos")
-
-(define_insn "cosxf2"
[(set (match_operand:XF 0 "register_operand" "=f")
- (unspec:XF [(match_operand:XF 1 "register_operand" "0")] 2))]
- "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
- "fcos")
+ (abs:XF (float_extend:XF
+ (match_operand:SF 1 "register_operand" "0"))))]
+ "TARGET_80387"
+ "fabs"
+ [(set_attr "type" "fsgn")])
\f
-;;- one complement instructions
+;; One complement instructions
(define_insn "one_cmplsi2"
[(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
(not:SI (match_operand:SI 1 "nonimmediate_operand" "0")))]
""
- "*
-{
- /* A Pentium NOT is not pariable. Output it only in case of complex
- memory address, because XOR will be inpariable anyway because
- of immediate/displacement rule. */
+ "not{l}\\t%0"
+ [(set_attr "type" "negnot")])
- if (TARGET_PENTIUM && !optimize_size
- && (GET_CODE (operands[0]) != MEM
- || memory_address_info (XEXP (operands[0], 0), 1) == 0))
- {
- rtx xops[2];
- xops[0] = operands[0];
- xops[1] = GEN_INT (0xffffffff);
- output_asm_insn (AS2 (xor%L0,%1,%0), xops);
- RET;
- }
- else
- return AS1 (not%L0,%0);
-}")
+(define_insn ""
+ [(set (reg:CCNO 17)
+ (compare:CCNO (not:SI (match_operand:SI 1 "nonimmediate_operand" "0"))
+ (const_int 0)))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=rm")
+ (not:SI (match_dup 1)))]
+ ""
+ "#"
+ [(set_attr "type" "alu1")])
+
+(define_split
+ [(set (reg:CCNO 17)
+ (compare:CCNO (not:SI (match_operand:SI 1 "nonimmediate_operand" ""))
+ (const_int 0)))
+ (set (match_operand:SI 0 "nonimmediate_operand" "")
+ (not:SI (match_dup 1)))]
+ ""
+ [(parallel [(set (reg:CCNO 17)
+ (compare:CCNO (xor:SI (match_dup 1) (const_int -1))
+ (const_int 0)))
+ (set (match_dup 0)
+ (xor:SI (match_dup 1) (const_int -1)))])]
+ "")
(define_insn "one_cmplhi2"
[(set (match_operand:HI 0 "nonimmediate_operand" "=rm")
(not:HI (match_operand:HI 1 "nonimmediate_operand" "0")))]
""
- "*
-{
- /* A Pentium NOT is not pariable. Output it only in case of complex
- memory address, because XOR will be inpariable anyway because
- of immediate/displacement rule. */
+ "not{w}\\t%0"
+ [(set_attr "type" "negnot")])
- if (TARGET_PENTIUM && !optimize_size
- && (GET_CODE (operands[0]) != MEM
- || memory_address_info (XEXP (operands[0], 0), 1) == 0))
- {
- rtx xops[2];
- xops[0] = operands[0];
- xops[1] = GEN_INT (0xffff);
- if (REG_P (operands[0])
- && i386_cc_probably_useless_p (insn))
- {
- CC_STATUS_INIT;
- output_asm_insn (AS2 (xor%L0,%1,%k0), xops);
- }
- else
- output_asm_insn (AS2 (xor%W0,%1,%0), xops);
- RET;
- }
- else
- {
- if (REG_P (operands[0])
- && i386_cc_probably_useless_p (insn))
- {
- CC_STATUS_INIT;
- return AS1 (not%L0,%k0);
- }
- return AS1 (not%W0,%0);
- }
-}")
+(define_split
+ [(set (match_operand:HI 0 "register_operand" "")
+ (not:HI (match_operand:HI 1 "register_operand" "")))
+ (clobber (reg:CC 17))]
+ "! TARGET_PARTIAL_REG_STALL && reload_completed"
+ [(parallel [(set (match_dup 0) (not:SI (match_dup 1)))
+ (clobber (reg:CC 17))])]
+ "operands[0] = gen_lowpart (SImode, operands[0]);
+ operands[1] = gen_lowpart (SImode, operands[1]);")
+
+(define_insn ""
+ [(set (reg:CCNO 17)
+ (compare:CCNO (not:HI (match_operand:HI 1 "nonimmediate_operand" "0"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=rm")
+ (not:HI (match_dup 1)))]
+ ""
+ "#"
+ [(set_attr "type" "alu1")])
+
+(define_split
+ [(set (reg:CCNO 17)
+ (compare:CCNO (not:HI (match_operand:HI 1 "nonimmediate_operand" ""))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "")
+ (not:HI (match_dup 1)))]
+ ""
+ [(parallel [(set (reg:CCNO 17)
+ (compare:CCNO (xor:HI (match_dup 1) (const_int -1))
+ (const_int 0)))
+ (set (match_dup 0)
+ (xor:HI (match_dup 1) (const_int -1)))])]
+ "")
+;; %%% Potential partial reg stall on alternative 1. What to do?
(define_insn "one_cmplqi2"
- [(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
- (not:QI (match_operand:QI 1 "nonimmediate_operand" "0")))]
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,*r")
+ (not:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")))]
""
- "*
-{
- /* A Pentium NOT is not pariable. Output it only in case of complex
- memory address, because XOR will be inpariable anyway because
- of immediate/displacement rule. */
+ "@
+ not{b}\\t%0
+ not{l}\\t%k0"
+ [(set_attr "type" "negnot")])
- if (TARGET_PENTIUM && !optimize_size
- && (GET_CODE (operands[0]) != MEM
- || memory_address_info (XEXP (operands[0], 0), 1) == 0))
- {
- rtx xops[2];
- xops[0] = operands[0];
- xops[1] = GEN_INT (0xff);
- output_asm_insn (AS2 (xor%B0,%1,%0), xops);
- RET;
- }
- else
- return AS1 (not%B0,%0);
-}")
+(define_insn ""
+ [(set (reg:CCNO 17)
+ (compare:CCNO (not:QI (match_operand:QI 1 "nonimmediate_operand" "0"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "nonimmediate_operand" "=qm")
+ (not:QI (match_dup 1)))]
+ ""
+ "#"
+ [(set_attr "type" "alu1")])
+
+(define_split
+ [(set (reg:CCNO 17)
+ (compare:CCNO (not:QI (match_operand:QI 1 "nonimmediate_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "nonimmediate_operand" "")
+ (not:QI (match_dup 1)))]
+ ""
+ [(parallel [(set (reg:CCNO 17)
+ (compare:CCNO (xor:QI (match_dup 1) (const_int -1))
+ (const_int 0)))
+ (set (match_dup 0)
+ (xor:QI (match_dup 1) (const_int -1)))])]
+ "")
\f
-;;- arithmetic shift instructions
+;; Arithmetic shift instructions
;; DImode shifts are implemented using the i386 "shift double" opcode,
;; which is written as "sh[lr]d[lw] imm,reg,reg/mem". If the shift count
;; is variable, then the count is in %cl and the "imm" operand is dropped
;; from the assembler input.
-
+;;
;; This instruction shifts the target reg/mem as usual, but instead of
;; shifting in zeros, bits are shifted in from reg operand. If the insn
;; is a left shift double, bits are taken from the high order bits of
;; reg, else if the insn is a shift right double, bits are taken from the
;; low order bits of reg. So if %eax is "1234" and %edx is "5678",
;; "shldl $8,%edx,%eax" leaves %edx unchanged and sets %eax to "2345".
-
+;;
;; Since sh[lr]d does not change the `reg' operand, that is done
;; separately, making all shifts emit pairs of shift double and normal
;; shift. Since sh[lr]d does not shift more than 31 bits, and we wish to
;; support a 63 bit shift, each shift where the count is in a reg expands
;; to a pair of shifts, a branch, a shift by 32 and a label.
-
+;;
;; If the shift count is a constant, we need never emit more than one
;; shift pair, instead using moves and sign extension for counts greater
;; than 31.
(define_expand "ashldi3"
- [(set (match_operand:DI 0 "register_operand" "")
- (ashift:DI (match_operand:DI 1 "register_operand" "")
- (match_operand:QI 2 "nonmemory_operand" "")))]
+ [(parallel [(set (match_operand:DI 0 "register_operand" "=r")
+ (ashift:DI (match_operand:DI 1 "register_operand" "0")
+ (match_operand:QI 2 "nonmemory_operand" "Jc")))
+ (clobber (reg:CC 17))])]
""
"
{
- if (GET_CODE (operands[2]) != CONST_INT
- || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))
+ if (TARGET_CMOVE && ! immediate_operand (operands[2], QImode))
{
- operands[2] = copy_to_mode_reg (QImode, operands[2]);
- emit_insn (gen_ashldi3_non_const_int (operands[0], operands[1],
- operands[2]));
+ emit_insn (gen_ashldi3_1 (operands[0], operands[1], operands[2]));
+ DONE;
}
- else
- emit_insn (gen_ashldi3_const_int (operands[0], operands[1], operands[2]));
-
- DONE;
}")
-(define_insn "ashldi3_const_int"
- [(set (match_operand:DI 0 "register_operand" "=&r")
+(define_insn "ashldi3_1"
+ [(set (match_operand:DI 0 "register_operand" "=r")
(ashift:DI (match_operand:DI 1 "register_operand" "0")
- (match_operand:QI 2 "const_int_operand" "J")))]
- "CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J')"
- "*
-{
- rtx xops[4], low[1], high[1];
-
- CC_STATUS_INIT;
-
- split_di (operands, 1, low, high);
- xops[0] = operands[2];
- xops[1] = const1_rtx;
- xops[2] = low[0];
- xops[3] = high[0];
-
- if (INTVAL (xops[0]) > 31)
- {
- output_asm_insn (AS2 (mov%L3,%2,%3), xops); /* Fast shift by 32 */
- output_asm_insn (AS2 (xor%L2,%2,%2), xops);
-
- if (INTVAL (xops[0]) > 32)
- {
- xops[0] = GEN_INT (INTVAL (xops[0]) - 32);
- output_asm_insn (AS2 (sal%L3,%0,%3), xops); /* Remaining shift */
- }
- }
- else
- {
- output_asm_insn (AS3 (shld%L3,%0,%2,%3), xops);
- output_asm_insn (AS2 (sal%L2,%0,%2), xops);
- }
- RET;
-}")
+ (match_operand:QI 2 "nonmemory_operand" "Jc")))
+ (clobber (match_scratch:SI 3 "=&r"))
+ (clobber (reg:CC 17))]
+ "TARGET_CMOVE"
+ "#"
+ [(set_attr "type" "multi")])
-(define_insn "ashldi3_non_const_int"
- [(set (match_operand:DI 0 "register_operand" "=&r")
+(define_insn "*ashldi3_2"
+ [(set (match_operand:DI 0 "register_operand" "=r")
(ashift:DI (match_operand:DI 1 "register_operand" "0")
- (match_operand:QI 2 "register_operand" "c")))]
+ (match_operand:QI 2 "nonmemory_operand" "Jc")))
+ (clobber (reg:CC 17))]
""
- "*
-{
- rtx xops[5], low[1], high[1];
-
- CC_STATUS_INIT;
-
- split_di (operands, 1, low, high);
- xops[0] = operands[2];
- xops[1] = GEN_INT (32);
- xops[2] = low[0];
- xops[3] = high[0];
- xops[4] = gen_label_rtx ();
-
- output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops);
- output_asm_insn (AS2 (sal%L2,%0,%2), xops);
- output_asm_insn (AS2 (test%B0,%1,%b0), xops);
- output_asm_insn (AS1 (je,%X4), xops);
- output_asm_insn (AS2 (mov%L3,%2,%3), xops); /* Fast shift by 32 */
- output_asm_insn (AS2 (xor%L2,%2,%2), xops);
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\",
- CODE_LABEL_NUMBER (xops[4]));
- RET;
-}")
+ "#"
+ [(set_attr "type" "multi")])
-(define_expand "ashlsi3"
- [(set (match_operand:SI 0 "nonimmediate_operand" "")
- (ashift:SI (match_operand:SI 1 "nonimmediate_operand" "")
- (match_operand:SI 2 "nonmemory_operand" "")))]
- ""
- "")
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ashift:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:QI 2 "nonmemory_operand" "")))
+ (clobber (match_scratch:SI 3 ""))
+ (clobber (reg:CC 17))]
+ "TARGET_CMOVE && cse_not_expected"
+ [(const_int 0)]
+ "ix86_split_ashldi (operands, operands[3]); DONE;")
-(define_expand "ashlhi3"
- [(set (match_operand:HI 0 "nonimmediate_operand" "")
- (ashift:HI (match_operand:HI 1 "nonimmediate_operand" "")
- (match_operand:HI 2 "nonmemory_operand" "")))]
- ""
- "")
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ashift:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:QI 2 "nonmemory_operand" "")))
+ (clobber (reg:CC 17))]
+ "cse_not_expected"
+ [(const_int 0)]
+ "ix86_split_ashldi (operands, NULL_RTX); DONE;")
-(define_expand "ashlqi3"
- [(set (match_operand:QI 0 "nonimmediate_operand" "")
- (ashift:QI (match_operand:QI 1 "nonimmediate_operand" "")
- (match_operand:QI 2 "nonmemory_operand" "")))]
+(define_insn "x86_shld_1"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "+r*m,r*m")
+ (ior:SI (ashift:SI (match_dup 0)
+ (match_operand:QI 2 "nonmemory_operand" "I,c"))
+ (lshiftrt:SI (match_operand:SI 1 "register_operand" "r,r")
+ (minus:QI (const_int 32) (match_dup 2)))))
+ (clobber (reg:CC 17))]
""
+ "@
+ shld{l}\\t{%2, %1, %0|%0, %1, %2}
+ shld{l}\\t{%s2%1, %0|%0, %1, %2}"
+ [(set_attr "type" "ishift")
+ (set_attr "length_opcode" "3")
+ (set_attr "pent_pair" "np")
+ (set_attr "ppro_uops" "few")])
+
+(define_expand "x86_shift_adj_1"
+ [(set (reg:CCNO 17)
+ (compare:CCNO (and:QI (match_operand:QI 2 "register_operand" "")
+ (const_int 32))
+ (const_int 0)))
+ (set (match_operand:SI 0 "register_operand" "")
+ (if_then_else:SI (ne (reg:CCNO 17) (const_int 0))
+ (match_operand:SI 1 "register_operand" "")
+ (match_dup 0)))
+ (set (match_dup 1)
+ (if_then_else:SI (ne (reg:CCNO 17) (const_int 0))
+ (match_operand:SI 3 "register_operand" "r")
+ (match_dup 1)))]
+ "TARGET_CMOVE"
"")
-;; Pattern for shifts which can be encoded into an lea instruction.
-;; This is kept as a separate pattern so that regmove can optimize cases
-;; where we know the source and destination must match.
-;;
-;; Do not expose this pattern when optimizing for size since we never want
-;; to use lea when optimizing for size since mov+sal is smaller than lea.
-
-(define_insn ""
- [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r")
- (ashift:SI (match_operand:SI 1 "nonimmediate_operand" "0,r")
- (match_operand:SI 2 "small_shift_operand" "M,M")))]
- "! optimize_size"
- "* return output_ashl (insn, operands);")
-
-;; Generic left shift pattern to catch all cases not handled by the
-;; shift pattern above.
-(define_insn ""
- [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
- (ashift:SI (match_operand:SI 1 "nonimmediate_operand" "0")
- (match_operand:SI 2 "nonmemory_operand" "cI")))]
- ""
- "* return output_ashl (insn, operands);")
-
-(define_insn ""
- [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r")
- (ashift:HI (match_operand:HI 1 "nonimmediate_operand" "0,r")
- (match_operand:HI 2 "small_shift_operand" "M,M")))]
- "! optimize_size"
- "* return output_ashl (insn, operands);")
-
-(define_insn ""
- [(set (match_operand:HI 0 "nonimmediate_operand" "=rm")
- (ashift:HI (match_operand:HI 1 "nonimmediate_operand" "0")
- (match_operand:HI 2 "nonmemory_operand" "cI")))]
+(define_expand "x86_shift_adj_2"
+ [(use (match_operand:SI 0 "register_operand" ""))
+ (use (match_operand:SI 1 "register_operand" ""))
+ (use (match_operand:QI 2 "register_operand" ""))]
""
- "* return output_ashl (insn, operands);")
+ "
+{
+ rtx label = gen_label_rtx ();
+ rtx tmp;
-(define_insn ""
- [(set (match_operand:QI 0 "nonimmediate_operand" "=q,q")
- (ashift:QI (match_operand:QI 1 "nonimmediate_operand" "0,q")
- (match_operand:QI 2 "small_shift_operand" "M,M")))]
- "! optimize_size"
- "* return output_ashl (insn, operands);")
+ emit_insn (gen_testqi_1 (operands[2], GEN_INT (32)));
-;; Generic left shift pattern to catch all cases not handled by the
-;; shift pattern above.
-(define_insn ""
- [(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
- (ashift:QI (match_operand:QI 1 "nonimmediate_operand" "0")
- (match_operand:QI 2 "nonmemory_operand" "cI")))]
- ""
- "* return output_ashl (insn, operands);")
+ tmp = gen_rtx_REG (CCNOmode, FLAGS_REG);
+ tmp = gen_rtx_EQ (VOIDmode, tmp, const0_rtx);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode, label),
+ pc_rtx);
+ tmp = emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
+ JUMP_LABEL (tmp) = label;
-;; See comment above `ashldi3' about how this works.
+ emit_move_insn (operands[0], operands[1]);
+ emit_move_insn (operands[1], const0_rtx);
-(define_expand "ashrdi3"
- [(set (match_operand:DI 0 "register_operand" "")
- (ashiftrt:DI (match_operand:DI 1 "register_operand" "")
- (match_operand:QI 2 "nonmemory_operand" "")))]
- ""
- "
-{
- if (GET_CODE (operands[2]) != CONST_INT
- || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))
- {
- operands[2] = copy_to_mode_reg (QImode, operands[2]);
- emit_insn (gen_ashrdi3_non_const_int (operands[0], operands[1],
- operands[2]));
- }
- else
- emit_insn (gen_ashrdi3_const_int (operands[0], operands[1], operands[2]));
+ emit_label (label);
+ LABEL_NUSES (label) = 1;
DONE;
}")
-(define_insn "ashldi3_32"
- [(set (match_operand:DI 0 "nonimmediate_operand" "=r,m")
- (ashift:DI (match_operand:DI 1 "nonimmediate_operand" "ro,r")
- (const_int 32)))]
+(define_insn "ashlsi3"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
+ (ashift:SI (match_operand:SI 1 "nonimmediate_operand" "0,r")
+ (match_operand:QI 2 "nonmemory_operand" "cI,M")))
+ (clobber (reg:CC 17))]
""
"*
{
- rtx low[2], high[2], xops[4];
-
- split_di (operands, 2, low, high);
- xops[0] = high[0];
- xops[1] = low[1];
- xops[2] = low[0];
- xops[3] = const0_rtx;
- if (!rtx_equal_p (xops[0], xops[1]))
- output_asm_insn (AS2 (mov%L0,%1,%0), xops);
-
- if (GET_CODE (low[0]) == MEM)
- output_asm_insn (AS2 (mov%L2,%3,%2), xops);
- else
- output_asm_insn (AS2 (xor%L2,%2,%2), xops);
+ switch (get_attr_type (insn))
+ {
+ case TYPE_ALU:
+ if (operands[2] != const1_rtx)
+ abort ();
+ if (!rtx_equal_p (operands[0], operands[1]))
+ abort ();
+ return \"add{l}\\t{%0, %0|%0, %0}\";
- RET;
-}")
+ case TYPE_LEA:
+ if (GET_CODE (operands[2]) != CONST_INT
+ || (unsigned HOST_WIDE_INT) INTVAL (operands[2]) > 3)
+ abort ();
+ operands[1] = gen_rtx_MULT (SImode, operands[1],
+ GEN_INT (1 << INTVAL (operands[2])));
+ return \"lea{l}\\t{%a1, %0|%0, %a1}\";
-(define_insn "ashrdi3_const_int"
- [(set (match_operand:DI 0 "register_operand" "=&r")
- (ashiftrt:DI (match_operand:DI 1 "register_operand" "0")
- (match_operand:QI 2 "const_int_operand" "J")))]
- "CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J')"
+ default:
+ if (REG_P (operands[2]))
+ return \"sal{l}\\t{%b2, %0|%0, %b2}\";
+ else
+ return \"sal{l}\\t{%2, %0|%0, %2}\";
+ }
+}"
+ [(set (attr "type")
+ (cond [(eq_attr "alternative" "1")
+ (const_string "lea")
+ (and (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD")
+ (const_int 0))
+ (match_operand 0 "register_operand" ""))
+ (match_operand 2 "const1_operand" ""))
+ (const_string "alu")
+ ]
+ (const_string "ishift")))])
+
+(define_insn ""
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (ashift:SI (match_operand:SI 1 "nonimmediate_operand" "0")
+ (match_operand:QI 2 "nonmemory_operand" "cI"))
+ (const_int 0)))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=rm")
+ (ashift:SI (match_dup 1) (match_dup 2)))]
+ ""
"*
{
- rtx xops[4], low[1], high[1];
-
- CC_STATUS_INIT;
-
- split_di (operands, 1, low, high);
- xops[0] = operands[2];
- xops[1] = const1_rtx;
- xops[2] = low[0];
- xops[3] = high[0];
-
- if (INTVAL (xops[0]) > 31)
+ switch (get_attr_type (insn))
{
- xops[1] = GEN_INT (31);
- output_asm_insn (AS2 (mov%L2,%3,%2), xops);
- output_asm_insn (AS2 (sar%L3,%1,%3), xops); /* shift by 32 */
+ case TYPE_ALU:
+ if (operands[2] != const1_rtx)
+ abort ();
+ return \"add{l}\\t{%0, %0|%0, %0}\";
- if (INTVAL (xops[0]) > 32)
- {
- xops[0] = GEN_INT (INTVAL (xops[0]) - 32);
- output_asm_insn (AS2 (sar%L2,%0,%2), xops); /* Remaining shift */
- }
- }
- else
- {
- output_asm_insn (AS3 (shrd%L2,%0,%3,%2), xops);
- output_asm_insn (AS2 (sar%L3,%0,%3), xops);
+ default:
+ if (REG_P (operands[2]))
+ return \"sal{l}\\t{%b2, %0|%0, %b2}\";
+ else
+ return \"sal{l}\\t{%2, %0|%0, %2}\";
}
-
- RET;
-}")
-
-(define_insn "ashrdi3_non_const_int"
- [(set (match_operand:DI 0 "register_operand" "=&r")
- (ashiftrt:DI (match_operand:DI 1 "register_operand" "0")
- (match_operand:QI 2 "register_operand" "c")))]
+}"
+ [(set (attr "type")
+ (cond [(and (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD")
+ (const_int 0))
+ (match_operand 0 "register_operand" ""))
+ (match_operand 2 "const1_operand" ""))
+ (const_string "alu")
+ ]
+ (const_string "ishift")))])
+
+(define_insn "ashlhi3"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=rm")
+ (ashift:HI (match_operand:HI 1 "nonimmediate_operand" "0")
+ (match_operand:QI 2 "nonmemory_operand" "cI")))
+ (clobber (reg:CC 17))]
""
"*
{
- rtx xops[5], low[1], high[1];
-
- CC_STATUS_INIT;
-
- split_di (operands, 1, low, high);
- xops[0] = operands[2];
- xops[1] = GEN_INT (32);
- xops[2] = low[0];
- xops[3] = high[0];
- xops[4] = gen_label_rtx ();
-
- output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
- output_asm_insn (AS2 (sar%L3,%0,%3), xops);
- output_asm_insn (AS2 (test%B0,%1,%b0), xops);
- output_asm_insn (AS1 (je,%X4), xops);
- xops[1] = GEN_INT (31);
- output_asm_insn (AS2 (mov%L2,%3,%2), xops);
- output_asm_insn (AS2 (sar%L3,%1,%3), xops); /* shift by 32 */
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\",
- CODE_LABEL_NUMBER (xops[4]));
- RET;
-}")
+ switch (get_attr_type (insn))
+ {
+ case TYPE_ALU:
+ if (operands[2] != const1_rtx)
+ abort ();
+ return \"add{w}\\t{%0, %0|%0, %0}\";
-(define_insn "ashrsi3_31"
- [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,d")
- (ashiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0,a")
- (const_int 31)))]
- "!TARGET_PENTIUM || optimize_size"
- "@
- sar%L0 $31,%0
- cltd")
+ default:
+ if (REG_P (operands[2]))
+ return \"sal{w}\\t{%b2, %0|%0, %b2}\";
+ else
+ return \"sal{w}\\t{%2, %0|%0, %2}\";
+ }
+}"
+ [(set (attr "type")
+ (cond [(and (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD")
+ (const_int 0))
+ (match_operand 0 "register_operand" ""))
+ (match_operand 2 "const1_operand" ""))
+ (const_string "alu")
+ ]
+ (const_string "ishift")))])
-(define_insn "ashrsi3"
- [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
- (ashiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0")
- (match_operand:SI 2 "nonmemory_operand" "cI")))]
+(define_split
+ [(set (match_operand:HI 0 "register_operand" "")
+ (ashift:HI (match_operand:HI 1 "register_operand" "")
+ (match_operand:QI 2 "nonmemory_operand" "")))
+ (clobber (reg:CC 17))]
+ "! TARGET_PARTIAL_REG_STALL && reload_completed"
+ [(parallel [(set (match_dup 0) (ashift:SI (match_dup 1) (match_dup 2)))
+ (clobber (reg:CC 17))])]
+ "operands[0] = gen_lowpart (SImode, operands[0]);
+ operands[1] = gen_lowpart (SImode, operands[1]);")
+
+(define_insn ""
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (ashift:HI (match_operand:HI 1 "nonimmediate_operand" "0")
+ (match_operand:QI 2 "nonmemory_operand" "cI"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=rm")
+ (ashift:HI (match_dup 1) (match_dup 2)))]
""
"*
{
- if (REG_P (operands[2]))
- return AS2 (sar%L0,%b2,%0);
- else
- return AS2 (sar%L0,%2,%0);
-}")
+ switch (get_attr_type (insn))
+ {
+ case TYPE_ALU:
+ if (operands[2] != const1_rtx)
+ abort ();
+ return \"add{w}\\t{%0, %0|%0, %0}\";
-(define_insn "ashrhi3"
- [(set (match_operand:HI 0 "nonimmediate_operand" "=rm")
- (ashiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "0")
- (match_operand:HI 2 "nonmemory_operand" "cI")))]
+ default:
+ if (REG_P (operands[2]))
+ return \"sal{w}\\t{%b2, %0|%0, %b2}\";
+ else
+ return \"sal{w}\\t{%2, %0|%0, %2}\";
+ }
+}"
+ [(set (attr "type")
+ (cond [(and (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD")
+ (const_int 0))
+ (match_operand 0 "register_operand" ""))
+ (match_operand 2 "const1_operand" ""))
+ (const_string "alu")
+ ]
+ (const_string "ishift")))])
+
+;; %%% Potential partial reg stall on alternative 2. What to do?
+(define_insn "ashlqi3"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,*r")
+ (ashift:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "cI,cI")))
+ (clobber (reg:CC 17))]
""
"*
{
- if (REG_P (operands[2]))
- return AS2 (sar%W0,%b2,%0);
- else
- return AS2 (sar%W0,%2,%0);
-}")
+ switch (get_attr_type (insn))
+ {
+ case TYPE_ALU:
+ if (operands[2] != const1_rtx)
+ abort ();
+ if (NON_QI_REG_P (operands[1]))
+ return \"add{l}\\t{%k0, %k0|%k0, %k0}\";
+ else
+ return \"add{b}\\t{%0, %0|%0, %0}\";
-(define_insn "ashrqi3"
- [(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
- (ashiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0")
- (match_operand:QI 2 "nonmemory_operand" "cI")))]
+ default:
+ if (REG_P (operands[2]))
+ {
+ if (NON_QI_REG_P (operands[1]))
+ return \"sal{l}\\t{%k2, %0|%0, %k2}\";
+ else
+ return \"sal{b}\\t{%b2, %0|%0, %b2}\";
+ }
+ else
+ {
+ if (NON_QI_REG_P (operands[1]))
+ return \"sal{b}\\t{%2, %k0|%k0, %2}\";
+ else
+ return \"sal{b}\\t{%2, %0|%0, %2}\";
+ }
+ }
+}"
+ [(set (attr "type")
+ (cond [(and (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD")
+ (const_int 0))
+ (match_operand 0 "register_operand" ""))
+ (match_operand 2 "const1_operand" ""))
+ (const_string "alu")
+ ]
+ (const_string "ishift")))])
+
+(define_insn ""
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (ashift:QI (match_operand:QI 1 "nonimmediate_operand" "0")
+ (match_operand:QI 2 "nonmemory_operand" "cI"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "nonimmediate_operand" "=qm")
+ (ashift:QI (match_dup 1) (match_dup 2)))]
""
"*
{
- if (REG_P (operands[2]))
- return AS2 (sar%B0,%b2,%0);
- else
- return AS2 (sar%B0,%2,%0);
-}")
-\f
-;;- logical shift instructions
+ switch (get_attr_type (insn))
+ {
+ case TYPE_ALU:
+ if (operands[2] != const1_rtx)
+ abort ();
+ return \"add{b}\\t{%0, %0|%0, %0}\";
+
+ default:
+ if (REG_P (operands[2]))
+ return \"sal{b}\\t{%b2, %0|%0, %b2}\";
+ else
+ return \"sal{b}\\t{%2, %0|%0, %2}\";
+ }
+}"
+ [(set (attr "type")
+ (cond [(and (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD")
+ (const_int 0))
+ (match_operand 0 "register_operand" ""))
+ (match_operand 2 "const1_operand" ""))
+ (const_string "alu")
+ ]
+ (const_string "ishift")))])
;; See comment above `ashldi3' about how this works.
-(define_expand "lshrdi3"
- [(set (match_operand:DI 0 "register_operand" "")
- (lshiftrt:DI (match_operand:DI 1 "register_operand" "")
- (match_operand:QI 2 "nonmemory_operand" "")))]
+(define_expand "ashrdi3"
+ [(parallel [(set (match_operand:DI 0 "register_operand" "=r")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "0")
+ (match_operand:QI 2 "nonmemory_operand" "Jc")))
+ (clobber (reg:CC 17))])]
""
"
{
- if (GET_CODE (operands[2]) != CONST_INT
- || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))
+ if (TARGET_CMOVE && ! immediate_operand (operands[2], QImode))
{
- operands[2] = copy_to_mode_reg (QImode, operands[2]);
- emit_insn (gen_lshrdi3_non_const_int (operands[0], operands[1],
- operands[2]));
+ emit_insn (gen_ashrdi3_1 (operands[0], operands[1], operands[2]));
+ DONE;
}
- else
- emit_insn (gen_lshrdi3_const_int (operands[0], operands[1], operands[2]));
-
- DONE;
-}")
-
-(define_insn "lshrdi3_32"
- [(set (match_operand:DI 0 "nonimmediate_operand" "=r,m")
- (lshiftrt:DI (match_operand:DI 1 "nonimmediate_operand" "ro,r")
- (const_int 32)))]
- ""
- "*
-{
- rtx low[2], high[2], xops[4];
-
- split_di (operands, 2, low, high);
- xops[0] = low[0];
- xops[1] = high[1];
- xops[2] = high[0];
- xops[3] = const0_rtx;
- if (!rtx_equal_p (xops[0], xops[1]))
- output_asm_insn (AS2 (mov%L0,%1,%0), xops);
-
- if (GET_CODE (low[0]) == MEM)
- output_asm_insn (AS2 (mov%L2,%3,%2), xops);
- else
- output_asm_insn (AS2 (xor%L2,%2,%2), xops);
-
- RET;
}")
-(define_insn "lshrdi3_const_int"
- [(set (match_operand:DI 0 "register_operand" "=&r")
- (lshiftrt:DI (match_operand:DI 1 "register_operand" "0")
- (match_operand:QI 2 "const_int_operand" "J")))]
- "CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J')"
- "*
-{
- rtx xops[4], low[1], high[1];
-
- CC_STATUS_INIT;
-
- split_di (operands, 1, low, high);
- xops[0] = operands[2];
- xops[1] = const1_rtx;
- xops[2] = low[0];
- xops[3] = high[0];
-
- if (INTVAL (xops[0]) > 31)
- {
- output_asm_insn (AS2 (mov%L2,%3,%2), xops); /* Fast shift by 32 */
- output_asm_insn (AS2 (xor%L3,%3,%3), xops);
-
- if (INTVAL (xops[0]) > 32)
- {
- xops[0] = GEN_INT (INTVAL (xops[0]) - 32);
- output_asm_insn (AS2 (shr%L2,%0,%2), xops); /* Remaining shift */
- }
- }
- else
- {
- output_asm_insn (AS3 (shrd%L2,%0,%3,%2), xops);
- output_asm_insn (AS2 (shr%L3,%0,%3), xops);
- }
-
- RET;
-}")
+(define_insn "ashrdi3_1"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "0")
+ (match_operand:QI 2 "nonmemory_operand" "Jc")))
+ (clobber (match_scratch:SI 3 "=&r"))
+ (clobber (reg:CC 17))]
+ "TARGET_CMOVE"
+ "#"
+ [(set_attr "type" "multi")])
-(define_insn "lshrdi3_non_const_int"
- [(set (match_operand:DI 0 "register_operand" "=&r")
- (lshiftrt:DI (match_operand:DI 1 "register_operand" "0")
- (match_operand:QI 2 "register_operand" "c")))]
+(define_insn "*ashrdi3_2"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "0")
+ (match_operand:QI 2 "nonmemory_operand" "Jc")))
+ (clobber (reg:CC 17))]
""
- "*
-{
- rtx xops[5], low[1], high[1];
-
- CC_STATUS_INIT;
-
- split_di (operands, 1, low, high);
- xops[0] = operands[2];
- xops[1] = GEN_INT (32);
- xops[2] = low[0];
- xops[3] = high[0];
- xops[4] = gen_label_rtx ();
-
- output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
- output_asm_insn (AS2 (shr%L3,%0,%3), xops);
- output_asm_insn (AS2 (test%B0,%1,%b0), xops);
- output_asm_insn (AS1 (je,%X4), xops);
- output_asm_insn (AS2 (mov%L2,%3,%2), xops); /* Fast shift by 32 */
- output_asm_insn (AS2 (xor%L3,%3,%3), xops);
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\",
- CODE_LABEL_NUMBER (xops[4]));
- RET;
-}")
+ "#"
+ [(set_attr "type" "multi")])
-(define_insn "lshrsi3"
- [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
- (lshiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0")
- (match_operand:SI 2 "nonmemory_operand" "cI")))]
- ""
- "*
-{
- if (REG_P (operands[2]))
- return AS2 (shr%L0,%b2,%0);
- else
- return AS2 (shr%L0,%2,%1);
-}")
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:QI 2 "nonmemory_operand" "")))
+ (clobber (match_scratch:SI 3 ""))
+ (clobber (reg:CC 17))]
+ "TARGET_CMOVE && cse_not_expected"
+ [(const_int 0)]
+ "ix86_split_ashrdi (operands, operands[3]); DONE;")
-(define_insn "lshrhi3"
- [(set (match_operand:HI 0 "nonimmediate_operand" "=rm")
- (lshiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "0")
- (match_operand:HI 2 "nonmemory_operand" "cI")))]
- ""
- "*
-{
- if (REG_P (operands[2]))
- return AS2 (shr%W0,%b2,%0);
- else
- return AS2 (shr%W0,%2,%0);
-}")
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:QI 2 "nonmemory_operand" "")))
+ (clobber (reg:CC 17))]
+ "cse_not_expected"
+ [(const_int 0)]
+ "ix86_split_ashrdi (operands, NULL_RTX); DONE;")
-(define_insn "lshrqi3"
- [(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
- (lshiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0")
- (match_operand:QI 2 "nonmemory_operand" "cI")))]
+(define_insn "x86_shrd_1"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "+r*m,r*m")
+ (ior:SI (ashiftrt:SI (match_dup 0)
+ (match_operand:QI 2 "nonmemory_operand" "I,c"))
+ (ashift:SI (match_operand:SI 1 "register_operand" "r,r")
+ (minus:QI (const_int 32) (match_dup 2)))))
+ (clobber (reg:CC 17))]
""
- "*
+ "@
+ shrd{l}\\t{%2, %1, %0|%0, %1, %2}
+ shrd{l}\\t{%s2%1, %0|%0, %1, %2}"
+ [(set_attr "type" "ishift")
+ (set_attr "length_opcode" "3")
+ (set_attr "pent_pair" "np")
+ (set_attr "ppro_uops" "few")])
+
+(define_expand "x86_shift_adj_3"
+ [(use (match_operand:SI 0 "register_operand" ""))
+ (use (match_operand:SI 1 "register_operand" ""))
+ (use (match_operand:QI 2 "register_operand" ""))]
+ ""
+ "
{
- if (REG_P (operands[2]))
- return AS2 (shr%B0,%b2,%0);
- else
- return AS2 (shr%B0,%2,%0);
+ rtx label = gen_label_rtx ();
+ rtx tmp;
+
+ emit_insn (gen_testqi_1 (operands[2], GEN_INT (32)));
+
+ tmp = gen_rtx_REG (CCNOmode, FLAGS_REG);
+ tmp = gen_rtx_EQ (VOIDmode, tmp, const0_rtx);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode, label),
+ pc_rtx);
+ tmp = emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
+ JUMP_LABEL (tmp) = label;
+
+ emit_move_insn (operands[0], operands[1]);
+ emit_insn (gen_ashrsi3_31 (operands[1], operands[1], GEN_INT (31)));
+
+ emit_label (label);
+ LABEL_NUSES (label) = 1;
+
+ DONE;
}")
-\f
-;;- rotate instructions
-(define_insn "rotlsi3"
- [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
- (rotate:SI (match_operand:SI 1 "nonimmediate_operand" "0")
- (match_operand:SI 2 "nonmemory_operand" "cI")))]
+(define_insn "ashrsi3_31"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=*d,rm")
+ (ashiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "*a,0")
+ (match_operand:SI 2 "const_int_operand" "i,i")))
+ (clobber (reg:CC 17))]
+ "INTVAL (operands[2]) == 31"
+ "@
+ {cltd|cdq}
+ sar{l}\\t{%2, %0|%0, %2}"
+ [(set_attr "type" "imovx,ishift")
+ (set_attr "length" "1,*")])
+
+(define_insn "ashrsi3"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm")
+ (ashiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c")))
+ (clobber (reg:CC 17))]
""
- "*
-{
- if (REG_P (operands[2]))
- return AS2 (rol%L0,%b2,%0);
- else
- return AS2 (rol%L0,%2,%0);
-}")
+ "@
+ sar{l}\\t{%2, %0|%0, %2}
+ sar{l}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
-(define_insn "rotlhi3"
- [(set (match_operand:HI 0 "nonimmediate_operand" "=rm")
- (rotate:HI (match_operand:HI 1 "nonimmediate_operand" "0")
- (match_operand:HI 2 "nonmemory_operand" "cI")))]
+(define_insn ""
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (ashiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c"))
+ (const_int 0)))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm")
+ (ashiftrt:SI (match_dup 1) (match_dup 2)))]
""
- "*
-{
- if (REG_P (operands[2]))
- return AS2 (rol%W0,%b2,%0);
- else
- return AS2 (rol%W0,%2,%0);
-}")
+ "@
+ sar{l}\\t{%2, %0|%0, %2}
+ sar{l}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
-(define_insn "rotlqi3"
- [(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
- (rotate:QI (match_operand:QI 1 "nonimmediate_operand" "0")
- (match_operand:QI 2 "nonmemory_operand" "cI")))]
+(define_insn "ashrhi3"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,rm")
+ (ashiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c")))
+ (clobber (reg:CC 17))]
""
- "*
-{
- if (REG_P (operands[2]))
- return AS2 (rol%B0,%b2,%0);
- else
- return AS2 (rol%B0,%2,%0);
-}")
+ "@
+ sar{w}\\t{%2, %0|%0, %2}
+ sar{w}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
-(define_insn "rotrsi3"
- [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
- (rotatert:SI (match_operand:SI 1 "nonimmediate_operand" "0")
- (match_operand:SI 2 "nonmemory_operand" "cI")))]
+(define_insn ""
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (ashiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=rm,rm")
+ (ashiftrt:HI (match_dup 1) (match_dup 2)))]
""
- "*
-{
- if (REG_P (operands[2]))
- return AS2 (ror%L0,%b2,%0);
- else
- return AS2 (ror%L0,%2,%0);
-}")
+ "@
+ sar{w}\\t{%2, %0|%0, %2}
+ sar{w}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
-(define_insn "rotrhi3"
- [(set (match_operand:HI 0 "nonimmediate_operand" "=rm")
- (rotatert:HI (match_operand:HI 1 "nonimmediate_operand" "0")
- (match_operand:HI 2 "nonmemory_operand" "cI")))]
+(define_insn "ashrqi3"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,qm")
+ (ashiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c")))
+ (clobber (reg:CC 17))]
""
- "*
-{
- if (REG_P (operands[2]))
- return AS2 (ror%W0,%b2,%0);
- else
- return AS2 (ror%W0,%2,%0);
-}")
+ "@
+ sar{b}\\t{%2, %0|%0, %2}
+ sar{b}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
-(define_insn "rotrqi3"
- [(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
- (rotatert:QI (match_operand:QI 1 "nonimmediate_operand" "0")
- (match_operand:QI 2 "nonmemory_operand" "cI")))]
+(define_insn ""
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (ashiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "nonimmediate_operand" "=rm,rm")
+ (ashiftrt:QI (match_dup 1) (match_dup 2)))]
""
- "*
-{
- if (REG_P (operands[2]))
- return AS2 (ror%B0,%b2,%0);
- else
- return AS2 (ror%B0,%2,%0);
-}")
+ "@
+ sar{b}\\t{%2, %0|%0, %2}
+ sar{b}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
\f
-/*
-;; This usually looses. But try a define_expand to recognize a few case
-;; we can do efficiently, such as accessing the "high" QImode registers,
-;; %ah, %bh, %ch, %dh.
-;; ??? Note this has a botch on the mode of operand 0, which needs to be
-;; fixed if this is ever enabled.
-(define_insn "insv"
- [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+&r")
- (match_operand:SI 1 "immediate_operand" "i")
- (match_operand:SI 2 "immediate_operand" "i"))
- (match_operand:SI 3 "nonmemory_operand" "ri"))]
+;; Logical shift instructions
+
+;; See comment above `ashldi3' about how this works.
+
+(define_expand "lshrdi3"
+ [(parallel [(set (match_operand:DI 0 "register_operand" "=r")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "0")
+ (match_operand:QI 2 "nonmemory_operand" "Jc")))
+ (clobber (reg:CC 17))])]
""
- "*
+ "
{
- if (INTVAL (operands[1]) + INTVAL (operands[2]) > GET_MODE_BITSIZE (SImode))
- abort ();
- if (GET_CODE (operands[3]) == CONST_INT)
- {
- unsigned int mask = (1 << INTVAL (operands[1])) - 1;
- operands[1] = GEN_INT (~(mask << INTVAL (operands[2])));
- output_asm_insn (AS2 (and%L0,%1,%0), operands);
- operands[3] = GEN_INT (INTVAL (operands[3]) << INTVAL (operands[2]));
- output_asm_insn (AS2 (or%L0,%3,%0), operands);
- }
- else
+ if (TARGET_CMOVE && ! immediate_operand (operands[2], QImode))
{
- operands[0] = gen_rtx_REG (SImode, REGNO (operands[0]));
- if (INTVAL (operands[2]))
- output_asm_insn (AS2 (ror%L0,%2,%0), operands);
- output_asm_insn (AS3 (shrd%L0,%1,%3,%0), operands);
- operands[2] = GEN_INT (BITS_PER_WORD
- - INTVAL (operands[1]) - INTVAL (operands[2]));
- if (INTVAL (operands[2]))
- output_asm_insn (AS2 (ror%L0,%2,%0), operands);
+ emit_insn (gen_lshrdi3_1 (operands[0], operands[1], operands[2]));
+ DONE;
}
- RET;
}")
-*/
-/*
-;; ??? There are problems with the mode of operand[3]. The point of this
-;; is to represent an HImode move to a "high byte" register.
-(define_expand "insv"
- [(set (zero_extract:SI (match_operand:SI 0 "general_operand" "")
- (match_operand:SI 1 "immediate_operand" "")
- (match_operand:SI 2 "immediate_operand" ""))
- (match_operand:QI 3 "nonmemory_operand" "ri"))]
- ""
- "
-{
- if (GET_CODE (operands[1]) != CONST_INT
- || GET_CODE (operands[2]) != CONST_INT)
- FAIL;
+(define_insn "lshrdi3_1"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "0")
+ (match_operand:QI 2 "nonmemory_operand" "Jc")))
+ (clobber (match_scratch:SI 3 "=&r"))
+ (clobber (reg:CC 17))]
+ "TARGET_CMOVE"
+ "#"
+ [(set_attr "type" "multi")])
- if (! (INTVAL (operands[1]) == 8
- && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 0))
- && ! INTVAL (operands[1]) == 1)
- FAIL;
-}")
-*/
+(define_insn "*lshrdi3_2"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "0")
+ (match_operand:QI 2 "nonmemory_operand" "Jc")))
+ (clobber (reg:CC 17))]
+ ""
+ "#"
+ [(set_attr "type" "multi")])
-;; On i386, the register count for a bit operation is *not* truncated,
-;; so SHIFT_COUNT_TRUNCATED must not be defined.
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:QI 2 "nonmemory_operand" "")))
+ (clobber (match_scratch:SI 3 ""))
+ (clobber (reg:CC 17))]
+ "TARGET_CMOVE && cse_not_expected"
+ [(const_int 0)]
+ "ix86_split_lshrdi (operands, operands[3]); DONE;")
-;; On i486, the shift & or/and code is faster than bts or btr. If
-;; operands[0] is a MEM, the bt[sr] is half as fast as the normal code.
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:QI 2 "nonmemory_operand" "")))
+ (clobber (reg:CC 17))]
+ "cse_not_expected"
+ [(const_int 0)]
+ "ix86_split_lshrdi (operands, NULL_RTX); DONE;")
-;; On i386, bts is a little faster if operands[0] is a reg, and a
-;; little slower if operands[0] is a MEM, than the shift & or/and code.
-;; Use bts & btr, since they reload better.
+(define_insn "lshrsi3"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm")
+ (lshiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c")))
+ (clobber (reg:CC 17))]
+ ""
+ "@
+ shr{l}\\t{%2, %0|%0, %2}
+ shr{l}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
-;; General bit set and clear.
(define_insn ""
- [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+rm")
- (const_int 1)
- (match_operand:SI 2 "register_operand" "r"))
- (match_operand:SI 3 "const_int_operand" "n"))]
- "TARGET_USE_BIT_TEST && GET_CODE (operands[2]) != CONST_INT"
- "*
-{
- CC_STATUS_INIT;
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (lshiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c"))
+ (const_int 0)))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm")
+ (lshiftrt:SI (match_dup 1) (match_dup 2)))]
+ ""
+ "@
+ shr{l}\\t{%2, %0|%0, %2}
+ shr{l}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
- if (INTVAL (operands[3]) == 1)
- return AS2 (bts%L0,%2,%0);
- else
- return AS2 (btr%L0,%2,%0);
-}")
+(define_insn "lshrhi3"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,rm")
+ (lshiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c")))
+ (clobber (reg:CC 17))]
+ ""
+ "@
+ shr{w}\\t{%2, %0|%0, %2}
+ shr{w}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
-;; Bit complement. See comments on previous pattern.
-;; ??? Is this really worthwhile?
(define_insn ""
- [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
- (xor:SI (ashift:SI (const_int 1)
- (match_operand:SI 1 "register_operand" "r"))
- (match_operand:SI 2 "nonimmediate_operand" "0")))]
- "TARGET_USE_BIT_TEST && GET_CODE (operands[1]) != CONST_INT"
- "*
-{
- CC_STATUS_INIT;
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (lshiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=rm,rm")
+ (lshiftrt:HI (match_dup 1) (match_dup 2)))]
+ ""
+ "@
+ shr{w}\\t{%2, %0|%0, %2}
+ shr{w}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
- return AS2 (btc%L0,%1,%0);
-}")
+(define_insn "lshrqi3"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,qm")
+ (lshiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c")))
+ (clobber (reg:CC 17))]
+ ""
+ "@
+ shr{b}\\t{%2, %0|%0, %2}
+ shr{b}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
(define_insn ""
- [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
- (xor:SI (match_operand:SI 1 "nonimmediate_operand" "0")
- (ashift:SI (const_int 1)
- (match_operand:SI 2 "register_operand" "r"))))]
- "TARGET_USE_BIT_TEST && GET_CODE (operands[2]) != CONST_INT"
- "*
-{
- CC_STATUS_INIT;
-
- return AS2 (btc%L0,%2,%0);
-}")
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (lshiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "nonimmediate_operand" "=rm,rm")
+ (lshiftrt:QI (match_dup 1) (match_dup 2)))]
+ ""
+ "@
+ shr{b}\\t{%2, %0|%0, %2}
+ shr{b}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
\f
-;; Recognizers for bit-test instructions.
-
-;; The bt opcode allows a MEM in operands[0]. But on both i386 and
-;; i486, it is faster to copy a MEM to REG and then use bt, than to use
-;; bt on the MEM directly.
+;; Rotate instructions
-;; ??? The first argument of a zero_extract must not be reloaded, so
-;; don't allow a MEM in the operand predicate without allowing it in the
-;; constraint.
+(define_insn "rotlsi3"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm")
+ (rotate:SI (match_operand:SI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c")))
+ (clobber (reg:CC 17))]
+ ""
+ "@
+ rol{l}\\t{%2, %0|%0, %2}
+ rol{l}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
(define_insn ""
- [(set (cc0) (zero_extract (match_operand:SI 0 "register_operand" "r")
- (const_int 1)
- (match_operand:SI 1 "register_operand" "r")))]
- "GET_CODE (operands[1]) != CONST_INT"
- "*
-{
- cc_status.flags |= CC_Z_IN_NOT_C;
- return AS2 (bt%L0,%1,%0);
-}")
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (rotate:SI (match_operand:SI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c"))
+ (const_int 0)))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm")
+ (rotate:SI (match_dup 1) (match_dup 2)))]
+ ""
+ "@
+ rol{l}\\t{%2, %0|%0, %2}
+ rol{l}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
+
+(define_insn "rotlhi3"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,rm")
+ (rotate:HI (match_operand:HI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c")))
+ (clobber (reg:CC 17))]
+ ""
+ "@
+ rol{w}\\t{%2, %0|%0, %2}
+ rol{w}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
(define_insn ""
- [(set (cc0) (zero_extract (match_operand:SI 0 "register_operand" "r")
- (match_operand:SI 1 "const_int_operand" "n")
- (match_operand:SI 2 "const_int_operand" "n")))]
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (rotate:HI (match_operand:HI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=rm,rm")
+ (rotate:HI (match_dup 1) (match_dup 2)))]
""
- "*
-{
- unsigned int mask;
+ "@
+ rol{w}\\t{%2, %0|%0, %2}
+ rol{w}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
- mask = ((1 << INTVAL (operands[1])) - 1) << INTVAL (operands[2]);
- operands[1] = GEN_INT (mask);
+(define_insn "rotlqi3"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,qm")
+ (rotate:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c")))
+ (clobber (reg:CC 17))]
+ ""
+ "@
+ rol{b}\\t{%2, %0|%0, %2}
+ rol{b}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
- if (QI_REG_P (operands[0])
- /* A Pentium test is pairable only with eax. Not with ah or al. */
- && (! REG_P (operands[0]) || REGNO (operands[0]) || !TARGET_PENTIUM
- || optimize_size))
- {
- if ((mask & ~0xff) == 0)
- {
- cc_status.flags |= CC_NOT_NEGATIVE;
- return AS2 (test%B0,%1,%b0);
- }
+(define_insn ""
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (rotate:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "nonimmediate_operand" "=rm,rm")
+ (rotate:QI (match_dup 1) (match_dup 2)))]
+ ""
+ "@
+ rol{b}\\t{%2, %0|%0, %2}
+ rol{b}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
- if ((mask & ~0xff00) == 0)
- {
- cc_status.flags |= CC_NOT_NEGATIVE;
- operands[1] = GEN_INT (mask >> 8);
- return AS2 (test%B0,%1,%h0);
- }
- }
+(define_insn "rotrsi3"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm")
+ (rotatert:SI (match_operand:SI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c")))
+ (clobber (reg:CC 17))]
+ ""
+ "@
+ ror{l}\\t{%2, %0|%0, %2}
+ ror{l}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
- return AS2 (test%L0,%1,%0);
-}")
+(define_insn ""
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (rotatert:SI (match_operand:SI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c"))
+ (const_int 0)))
+ (set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm")
+ (rotatert:SI (match_dup 1) (match_dup 2)))]
+ ""
+ "@
+ ror{l}\\t{%2, %0|%0, %2}
+ ror{l}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
-;; ??? All bets are off if operand 0 is a volatile MEM reference.
-;; The CPU may access unspecified bytes around the actual target byte.
+(define_insn "rotrhi3"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,rm")
+ (rotatert:HI (match_operand:HI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c")))
+ (clobber (reg:CC 17))]
+ ""
+ "@
+ ror{w}\\t{%2, %0|%0, %2}
+ ror{w}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
(define_insn ""
- [(set (cc0) (zero_extract (match_operand:QI 0 "memory_operand" "m")
- (match_operand:SI 1 "const_int_operand" "n")
- (match_operand:SI 2 "const_int_operand" "n")))]
- "GET_CODE (operands[0]) != MEM || ! MEM_VOLATILE_P (operands[0])"
- "*
-{
- unsigned int mask;
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (rotatert:HI (match_operand:HI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c"))
+ (const_int 0)))
+ (set (match_operand:HI 0 "nonimmediate_operand" "=rm,rm")
+ (rotatert:HI (match_dup 1) (match_dup 2)))]
+ ""
+ "@
+ ror{w}\\t{%2, %0|%0, %2}
+ ror{w}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
- mask = ((1 << INTVAL (operands[1])) - 1) << INTVAL (operands[2]);
- operands[1] = GEN_INT (mask);
+(define_insn "rotrqi3"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,qm")
+ (rotatert:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c")))
+ (clobber (reg:CC 17))]
+ ""
+ "@
+ ror{b}\\t{%2, %0|%0, %2}
+ ror{b}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
- if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
- /* A Pentium test is pairable only with eax. Not with ah or al. */
- && (! REG_P (operands[0]) || REGNO (operands[0]) || !TARGET_PENTIUM
- || optimize_size))
- {
- if ((mask & ~0xff) == 0)
- {
- cc_status.flags |= CC_NOT_NEGATIVE;
- return AS2 (test%B0,%1,%b0);
- }
+(define_insn ""
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (rotatert:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")
+ (match_operand:QI 2 "nonmemory_operand" "I,c"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "nonimmediate_operand" "=rm,rm")
+ (rotatert:QI (match_dup 1) (match_dup 2)))]
+ ""
+ "@
+ ror{b}\\t{%2, %0|%0, %2}
+ ror{b}\\t{%b2, %0|%0, %b2}"
+ [(set_attr "type" "ishift")])
+\f
+;; Bit set / bit test instructions
- if ((mask & ~0xff00) == 0)
- {
- cc_status.flags |= CC_NOT_NEGATIVE;
- operands[1] = GEN_INT (mask >> 8);
+(define_expand "extv"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (sign_extract:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "immediate_operand" "")
+ (match_operand:SI 3 "immediate_operand" "")))]
+ ""
+ "
+{
+ /* Handle extractions from %ah et al. */
+ if (INTVAL (operands[2]) != 8 || INTVAL (operands[3]) != 8)
+ FAIL;
- if (QI_REG_P (operands[0]))
- return AS2 (test%B0,%1,%h0);
- else
- {
- operands[0] = adj_offsettable_operand (operands[0], 1);
- return AS2 (test%B0,%1,%b0);
- }
- }
+ /* From mips.md: extract_bit_field doesn't verify that our source
+ matches the predicate, so check it again here. */
+ if (! register_operand (operands[1], VOIDmode))
+ FAIL;
+}")
- if (GET_CODE (operands[0]) == MEM && (mask & ~0xff0000) == 0)
- {
- cc_status.flags |= CC_NOT_NEGATIVE;
- operands[1] = GEN_INT (mask >> 16);
- operands[0] = adj_offsettable_operand (operands[0], 2);
- return AS2 (test%B0,%1,%b0);
- }
+(define_expand "extzv"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (zero_extract:SI (match_operand 1 "ext_register_operand" "")
+ (match_operand:SI 2 "immediate_operand" "")
+ (match_operand:SI 3 "immediate_operand" "")))]
+ ""
+ "
+{
+ /* Handle extractions from %ah et al. */
+ if (INTVAL (operands[2]) != 8 || INTVAL (operands[3]) != 8)
+ FAIL;
- if (GET_CODE (operands[0]) == MEM && (mask & ~0xff000000) == 0)
- {
- cc_status.flags |= CC_NOT_NEGATIVE;
- operands[1] = GEN_INT (mask >> 24);
- operands[0] = adj_offsettable_operand (operands[0], 3);
- return AS2 (test%B0,%1,%b0);
- }
- }
+ /* From mips.md: extract_bit_field doesn't verify that our source
+ matches the predicate, so check it again here. */
+ if (! register_operand (operands[1], VOIDmode))
+ FAIL;
+}")
- if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
- return AS2 (test%L0,%1,%0);
+(define_expand "insv"
+ [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "")
+ (match_operand:SI 1 "immediate_operand" "")
+ (match_operand:SI 2 "immediate_operand" ""))
+ (match_operand:SI 3 "register_operand" ""))]
+ ""
+ "
+{
+ /* Handle extractions from %ah et al. */
+ if (INTVAL (operands[1]) != 8 || INTVAL (operands[2]) != 8)
+ FAIL;
- return AS2 (test%L1,%0,%1);
+ /* From mips.md: insert_bit_field doesn't verify that our source
+ matches the predicate, so check it again here. */
+ if (! register_operand (operands[0], VOIDmode))
+ FAIL;
}")
+
+;; %%% bts, btr, btc, bt.
\f
;; Store-flag instructions.
;; For all sCOND expanders, also expand the compare or test insn that
;; generates cc0. Generate an equality comparison if `seq' or `sne'.
+;; %%% Do the expansion to SImode. If PII, do things the xor+setcc way
+;; to avoid partial register stalls. Otherwise do things the setcc+movzx
+;; way, which can later delete the movzx if only QImode is needed.
+
(define_expand "seq"
- [(match_dup 1)
- (set (match_operand:QI 0 "register_operand" "")
- (eq:QI (cc0) (const_int 0)))]
+ [(set (match_operand:SI 0 "register_operand" "")
+ (eq:SI (reg:CC 17) (const_int 0)))]
""
- "
-{
- if (TARGET_IEEE_FP
- && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT)
- operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1);
- else
- operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
-}")
+ "if (ix86_expand_setcc (EQ, 1, operands[0])) DONE; else FAIL;")
(define_expand "sne"
- [(match_dup 1)
- (set (match_operand:QI 0 "register_operand" "")
- (ne:QI (cc0) (const_int 0)))]
+ [(set (match_operand:SI 0 "register_operand" "")
+ (ne:SI (reg:CC 17) (const_int 0)))]
""
- "
-{
- if (TARGET_IEEE_FP
- && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT)
- operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1);
- else
- operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
-}")
+ "if (ix86_expand_setcc (NE, 1, operands[0])) DONE; else FAIL;")
(define_expand "sgt"
- [(match_dup 1)
- (set (match_operand:QI 0 "register_operand" "")
- (gt:QI (cc0) (const_int 0)))]
+ [(set (match_operand:SI 0 "register_operand" "")
+ (gt:SI (reg:CC 17) (const_int 0)))]
""
- "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+ "if (ix86_expand_setcc (GT, 0, operands[0])) DONE; else FAIL;")
(define_expand "sgtu"
- [(match_dup 1)
- (set (match_operand:QI 0 "register_operand" "")
- (gtu:QI (cc0) (const_int 0)))]
+ [(set (match_operand:SI 0 "register_operand" "")
+ (gtu:SI (reg:CC 17) (const_int 0)))]
""
- "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+ "if (ix86_expand_setcc (GTU, 0, operands[0])) DONE; else FAIL;")
(define_expand "slt"
- [(match_dup 1)
- (set (match_operand:QI 0 "register_operand" "")
- (lt:QI (cc0) (const_int 0)))]
+ [(set (match_operand:SI 0 "register_operand" "")
+ (lt:SI (reg:CC 17) (const_int 0)))]
""
- "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+ "if (ix86_expand_setcc (LT, 0, operands[0])) DONE; else FAIL;")
(define_expand "sltu"
- [(match_dup 1)
- (set (match_operand:QI 0 "register_operand" "")
- (ltu:QI (cc0) (const_int 0)))]
+ [(set (match_operand:SI 0 "register_operand" "")
+ (ltu:SI (reg:CC 17) (const_int 0)))]
""
- "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+ "if (ix86_expand_setcc (LTU, 0, operands[0])) DONE; else FAIL;")
(define_expand "sge"
- [(match_dup 1)
- (set (match_operand:QI 0 "register_operand" "")
- (ge:QI (cc0) (const_int 0)))]
+ [(set (match_operand:SI 0 "register_operand" "")
+ (ge:SI (reg:CC 17) (const_int 0)))]
""
- "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+ "if (ix86_expand_setcc (GE, 0, operands[0])) DONE; else FAIL;")
(define_expand "sgeu"
- [(match_dup 1)
- (set (match_operand:QI 0 "register_operand" "")
- (geu:QI (cc0) (const_int 0)))]
+ [(set (match_operand:SI 0 "register_operand" "")
+ (geu:SI (reg:CC 17) (const_int 0)))]
""
- "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+ "if (ix86_expand_setcc (GEU, 0, operands[0])) DONE; else FAIL;")
(define_expand "sle"
- [(match_dup 1)
- (set (match_operand:QI 0 "register_operand" "")
- (le:QI (cc0) (const_int 0)))]
+ [(set (match_operand:SI 0 "register_operand" "")
+ (le:SI (reg:CC 17) (const_int 0)))]
""
- "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+ "if (ix86_expand_setcc (LE, 0, operands[0])) DONE; else FAIL;")
(define_expand "sleu"
- [(match_dup 1)
- (set (match_operand:QI 0 "register_operand" "")
- (leu:QI (cc0) (const_int 0)))]
+ [(set (match_operand:SI 0 "register_operand" "")
+ (leu:SI (reg:CC 17) (const_int 0)))]
""
- "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+ "if (ix86_expand_setcc (LEU, 0, operands[0])) DONE; else FAIL;")
-;; The 386 sCOND opcodes can write to memory. But a gcc sCOND insn may
-;; not have any input reloads. A MEM write might need an input reload
-;; for the address of the MEM. So don't allow MEM as the SET_DEST.
-
-(define_insn "*setcc"
+(define_insn "*setcc_1"
[(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
- (match_operator:QI 1 "comparison_operator" [(cc0) (const_int 0)]))]
- "reload_completed || register_operand (operands[0], QImode)"
- "*
-{
- enum rtx_code code = GET_CODE (operands[1]);
- if (cc_prev_status.flags & CC_TEST_AX)
- {
- int eq;
- HOST_WIDE_INT c;
- operands[2] = gen_rtx_REG (SImode, 0);
- switch (code)
- {
- case EQ:
- c = 0x4000;
- eq = 0;
- break;
- case NE:
- c = 0x4000;
- eq = 1;
- break;
- case GT:
- c = 0x4100;
- eq = 1;
- break;
- case LT:
- c = 0x100;
- eq = 0;
- break;
- case GE:
- c = 0x100;
- eq = 1;
- break;
- case LE:
- c = 0x4100;
- eq = 0;
- break;
- default:
- abort ();
- }
- if (!TARGET_PENTIUM || optimize_size)
- {
- operands[3] = GEN_INT (c >> 8);
- output_asm_insn (AS2 (test%B0,%3,%h2), operands);
- }
- else
- {
- operands[3] = GEN_INT (c);
- output_asm_insn (AS2 (test%L0,%3,%2), operands);
- }
- return eq ? AS1 (sete,%0) : AS1 (setne, %0);
- }
+ (match_operator:QI 1 "no_comparison_operator"
+ [(reg 17) (const_int 0)]))]
+ ""
+ "set%C1\\t%0"
+ [(set_attr "type" "setcc")])
- if ((cc_status.flags & CC_NO_OVERFLOW) && (code == LE || code == GT))
- return (char *)0;
- return AS1(set%D1,%0);
-}")
+(define_insn "*setcc_2"
+ [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm"))
+ (match_operator:QI 1 "no_comparison_operator"
+ [(reg 17) (const_int 0)]))]
+ ""
+ "set%C1\\t%0"
+ [(set_attr "type" "setcc")])
+
+(define_insn "*setcc_3"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
+ (match_operator:QI 1 "comparison_operator"
+ [(reg:CC 17) (const_int 0)]))]
+ ""
+ "set%C1\\t%0"
+ [(set_attr "type" "setcc")])
+(define_insn "*setcc_4"
+ [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm"))
+ (match_operator:QI 1 "comparison_operator"
+ [(reg:CC 17) (const_int 0)]))]
+ ""
+ "set%C1\\t%0"
+ [(set_attr "type" "setcc")])
\f
;; Basic conditional jump instructions.
;; We ignore the overflow flag for signed branch instructions.
;; For all bCOND expanders, also expand the compare or test insn that
-;; generates cc0. Generate an equality comparison if `beq' or `bne'.
+;; generates reg 17. Generate an equality comparison if `beq' or `bne'.
(define_expand "beq"
- [(match_dup 1)
- (set (pc)
- (if_then_else (eq (cc0)
- (const_int 0))
+ [(set (pc)
+ (if_then_else (match_dup 1)
(label_ref (match_operand 0 "" ""))
(pc)))]
""
- "
-{
- if (TARGET_IEEE_FP
- && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT)
- operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1);
- else
- operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
-}")
+ "ix86_expand_branch (EQ, 1, operands[0]); DONE;")
(define_expand "bne"
- [(match_dup 1)
- (set (pc)
- (if_then_else (ne (cc0)
- (const_int 0))
+ [(set (pc)
+ (if_then_else (match_dup 1)
(label_ref (match_operand 0 "" ""))
(pc)))]
""
- "
-{
- if (TARGET_IEEE_FP
- && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT)
- operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1);
- else
- operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
-}")
-
+ "ix86_expand_branch (NE, 1, operands[0]); DONE;")
(define_expand "bgt"
- [(match_dup 1)
- (set (pc)
- (if_then_else (gt (cc0)
- (const_int 0))
+ [(set (pc)
+ (if_then_else (match_dup 1)
(label_ref (match_operand 0 "" ""))
(pc)))]
""
- "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+ "ix86_expand_branch (GT, 0, operands[0]); DONE;")
(define_expand "bgtu"
- [(match_dup 1)
- (set (pc)
- (if_then_else (gtu (cc0)
- (const_int 0))
+ [(set (pc)
+ (if_then_else (match_dup 1)
(label_ref (match_operand 0 "" ""))
(pc)))]
""
- "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+ "ix86_expand_branch (GTU, 0, operands[0]); DONE;")
(define_expand "blt"
- [(match_dup 1)
- (set (pc)
- (if_then_else (lt (cc0)
- (const_int 0))
+ [(set (pc)
+ (if_then_else (match_dup 1)
(label_ref (match_operand 0 "" ""))
(pc)))]
""
- "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
-
+ "ix86_expand_branch (LT, 0, operands[0]); DONE;")
(define_expand "bltu"
- [(match_dup 1)
- (set (pc)
- (if_then_else (ltu (cc0)
- (const_int 0))
+ [(set (pc)
+ (if_then_else (match_dup 1)
(label_ref (match_operand 0 "" ""))
(pc)))]
""
- "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+ "ix86_expand_branch (LTU, 0, operands[0]); DONE;")
(define_expand "bge"
- [(match_dup 1)
- (set (pc)
- (if_then_else (ge (cc0)
- (const_int 0))
+ [(set (pc)
+ (if_then_else (match_dup 1)
(label_ref (match_operand 0 "" ""))
(pc)))]
""
- "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+ "ix86_expand_branch (GE, 0, operands[0]); DONE;")
(define_expand "bgeu"
- [(match_dup 1)
- (set (pc)
- (if_then_else (geu (cc0)
- (const_int 0))
+ [(set (pc)
+ (if_then_else (match_dup 1)
(label_ref (match_operand 0 "" ""))
(pc)))]
""
- "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+ "ix86_expand_branch (GEU, 0, operands[0]); DONE;")
(define_expand "ble"
- [(match_dup 1)
- (set (pc)
- (if_then_else (le (cc0)
- (const_int 0))
+ [(set (pc)
+ (if_then_else (match_dup 1)
(label_ref (match_operand 0 "" ""))
(pc)))]
""
- "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+ "ix86_expand_branch (LE, 0, operands[0]); DONE;")
(define_expand "bleu"
- [(match_dup 1)
- (set (pc)
- (if_then_else (leu (cc0)
- (const_int 0))
+ [(set (pc)
+ (if_then_else (match_dup 1)
(label_ref (match_operand 0 "" ""))
(pc)))]
""
- "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+ "ix86_expand_branch (LEU, 0, operands[0]); DONE;")
-(define_insn ""
+(define_insn "*jcc_1"
+ [(set (pc)
+ (if_then_else (match_operator 0 "no_comparison_operator"
+ [(reg 17) (const_int 0)])
+ (label_ref (match_operand 1 "" ""))
+ (pc)))]
+ ""
+ "j%C0\\t%l1"
+ [(set_attr "type" "ibr")
+ (set (attr "length")
+ (if_then_else (and (ge (minus (match_dup 1) (pc))
+ (const_int -128))
+ (lt (minus (match_dup 1) (pc))
+ (const_int 124)))
+ (const_int 2)
+ (const_int 6)))])
+
+(define_insn "*jcc_2"
+ [(set (pc)
+ (if_then_else (match_operator 0 "no_comparison_operator"
+ [(reg 17) (const_int 0)])
+ (pc)
+ (label_ref (match_operand 1 "" ""))))]
+ ""
+ "j%c0\\t%l1"
+ [(set_attr "type" "ibr")
+ (set (attr "length")
+ (if_then_else (and (ge (minus (match_dup 1) (pc))
+ (const_int -128))
+ (lt (minus (match_dup 1) (pc))
+ (const_int 124)))
+ (const_int 2)
+ (const_int 6)))])
+
+(define_insn "*jcc_3"
[(set (pc)
(if_then_else (match_operator 0 "comparison_operator"
- [(cc0) (const_int 0)])
+ [(reg:CC 17) (const_int 0)])
(label_ref (match_operand 1 "" ""))
(pc)))]
""
- "*
-{
- enum rtx_code code = GET_CODE (operands[0]);
- if (cc_prev_status.flags & CC_TEST_AX)
- {
- int eq;
- HOST_WIDE_INT c;
- operands[2] = gen_rtx_REG (SImode, 0);
- switch (code)
- {
- case EQ:
- c = 0x4000;
- eq = 0;
- break;
- case NE:
- c = 0x4000;
- eq = 1;
- break;
- case GT:
- c = 0x4100;
- eq = 1;
- break;
- case LT:
- c = 0x100;
- eq = 0;
- break;
- case GE:
- c = 0x100;
- eq = 1;
- break;
- case LE:
- c = 0x4100;
- eq = 0;
- break;
- default:
- abort ();
- }
- if (!TARGET_PENTIUM || optimize_size)
- {
- operands[3] = GEN_INT (c >> 8);
- output_asm_insn (AS2 (test%B0,%3,%h2), operands);
- }
- else
- {
- operands[3] = GEN_INT (c);
- output_asm_insn (AS2 (test%L0,%3,%2), operands);
- }
- return eq ? AS1 (je,%l1) : AS1 (jne, %l1);
- }
- if ((cc_status.flags & CC_NO_OVERFLOW) && (code == LE || code == GT))
- return (char *)0;
-
- return AS1(j%D0,%l1);
-}")
-
-(define_insn ""
+ "j%C0\\t%l1"
+ [(set_attr "type" "ibr")
+ (set (attr "length")
+ (if_then_else (and (ge (minus (match_dup 1) (pc))
+ (const_int -128))
+ (lt (minus (match_dup 1) (pc))
+ (const_int 124)))
+ (const_int 2)
+ (const_int 6)))])
+
+(define_insn "*jcc_4"
[(set (pc)
(if_then_else (match_operator 0 "comparison_operator"
- [(cc0) (const_int 0)])
+ [(reg:CC 17) (const_int 0)])
(pc)
(label_ref (match_operand 1 "" ""))))]
""
- "*
-{
- enum rtx_code code = GET_CODE (operands[0]);
- if (cc_prev_status.flags & CC_TEST_AX)
- {
- int eq;
- HOST_WIDE_INT c;
- operands[2] = gen_rtx_REG (SImode, 0);
- switch (code)
- {
- case EQ:
- c = 0x4000;
- eq = 1;
- break;
- case NE:
- c = 0x4000;
- eq = 0;
- break;
- case GT:
- c = 0x4100;
- eq = 0;
- break;
- case LT:
- c = 0x100;
- eq = 1;
- break;
- case GE:
- c = 0x100;
- eq = 0;
- break;
- case LE:
- c = 0x4100;
- eq = 1;
- break;
- default:
- abort ();
- }
- if (!TARGET_PENTIUM || optimize_size)
- {
- operands[3] = GEN_INT (c >> 8);
- output_asm_insn (AS2 (test%B0,%3,%h2), operands);
- }
- else
- {
- operands[3] = GEN_INT (c);
- output_asm_insn (AS2 (test%L0,%3,%2), operands);
- }
- return eq ? AS1 (je,%l1) : AS1 (jne, %l1);
- }
- if ((cc_status.flags & CC_NO_OVERFLOW) && (code == LE || code == GT))
- return (char *)0;
-
- return AS1(j%d0,%l1);
-}")
+ "j%c0\\t%l1"
+ [(set_attr "type" "ibr")
+ (set (attr "length")
+ (if_then_else (and (ge (minus (match_dup 1) (pc))
+ (const_int -128))
+ (lt (minus (match_dup 1) (pc))
+ (const_int 124)))
+ (const_int 2)
+ (const_int 6)))])
\f
;; Unconditional and other jump instructions
[(set (pc)
(label_ref (match_operand 0 "" "")))]
""
- "jmp %l0"
- [(set_attr "memory" "none")])
+ "jmp\\t%l0"
+ [(set_attr "type" "ibr")
+ (set (attr "length")
+ (if_then_else (and (ge (minus (match_dup 0) (pc))
+ (const_int -128))
+ (lt (minus (match_dup 0) (pc))
+ (const_int 124)))
+ (const_int 2)
+ (const_int 5)))])
(define_insn "indirect_jump"
[(set (pc) (match_operand:SI 0 "nonimmediate_operand" "rm"))]
""
- "*
-{
- CC_STATUS_INIT;
-
- return AS1 (jmp,%*%0);
-}"
- [(set_attr "memory" "none")])
-
-;; ??? could transform while(--i > 0) S; to if (--i > 0) do S; while(--i);
-;; if S does not change i
-
-(define_expand "decrement_and_branch_until_zero"
- [(parallel [(set (pc)
- (if_then_else (ge (plus:SI (match_operand:SI 0 "general_operand" "")
- (const_int -1))
- (const_int 0))
- (label_ref (match_operand 1 "" ""))
- (pc)))
- (set (match_dup 0)
- (plus:SI (match_dup 0)
- (const_int -1)))])]
- ""
- "")
-
-(define_insn ""
- [(set (pc)
- (if_then_else (match_operator 0 "arithmetic_comparison_operator"
- [(plus:SI (match_operand:SI 1 "nonimmediate_operand" "+c*r,m")
- (match_operand:SI 2 "general_operand" "rmi,ri"))
- (const_int 0)])
- (label_ref (match_operand 3 "" ""))
- (pc)))
- (set (match_dup 1)
- (plus:SI (match_dup 1)
- (match_dup 2)))]
- ""
- "*
-{
- CC_STATUS_INIT;
-
- if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) == 2 &&
- operands[2] == constm1_rtx && ix86_cpu == PROCESSOR_K6)
- return \"loop %l3\";
-
- if (operands[2] == constm1_rtx)
- output_asm_insn (AS1 (dec%L1,%1), operands);
-
- else if (operands[2] == const1_rtx)
- output_asm_insn (AS1 (inc%L1,%1), operands);
-
- else
- output_asm_insn (AS2 (add%L1,%2,%1), operands);
-
- return AS1 (%J0,%l3);
-}")
-
-(define_insn ""
- [(set (pc)
- (if_then_else (match_operator 0 "arithmetic_comparison_operator"
- [(minus:SI (match_operand:SI 1 "nonimmediate_operand" "+r,m")
- (match_operand:SI 2 "general_operand" "rmi,ri"))
- (const_int 0)])
- (label_ref (match_operand 3 "" ""))
- (pc)))
- (set (match_dup 1)
- (minus:SI (match_dup 1)
- (match_dup 2)))]
- ""
- "*
-{
- CC_STATUS_INIT;
- if (operands[2] == const1_rtx)
- output_asm_insn (AS1 (dec%L1,%1), operands);
-
- else if (operands[1] == constm1_rtx)
- output_asm_insn (AS1 (inc%L1,%1), operands);
-
- else
- output_asm_insn (AS2 (sub%L1,%2,%1), operands);
-
- return AS1 (%J0,%l3);
-}")
-
-(define_insn ""
- [(set (pc)
- (if_then_else (ne (match_operand:SI 0 "general_operand" "+g")
- (const_int 0))
- (label_ref (match_operand 1 "" ""))
- (pc)))
- (set (match_dup 0)
- (plus:SI (match_dup 0)
- (const_int -1)))]
- ""
- "*
-{
- CC_STATUS_INIT;
- operands[2] = const1_rtx;
- output_asm_insn (AS2 (sub%L0,%2,%0), operands);
- return \"jnc %l1\";
-}")
-
-(define_insn ""
- [(set (pc)
- (if_then_else (eq (match_operand:SI 0 "general_operand" "+g")
- (const_int 0))
- (label_ref (match_operand 1 "" ""))
- (pc)))
- (set (match_dup 0)
- (plus:SI (match_dup 0)
- (const_int -1)))]
- ""
- "*
-{
- CC_STATUS_INIT;
- operands[2] = const1_rtx;
- output_asm_insn (AS2 (sub%L0,%2,%0), operands);
- return \"jc %l1\";
-}")
-
-(define_insn ""
- [(set (pc)
- (if_then_else (ne (match_operand:SI 0 "general_operand" "+g")
- (const_int 1))
- (label_ref (match_operand 1 "" ""))
- (pc)))
- (set (match_dup 0)
- (plus:SI (match_dup 0)
- (const_int -1)))]
- ""
- "*
-{
- CC_STATUS_INIT;
- output_asm_insn (AS1 (dec%L0,%0), operands);
- return \"jnz %l1\";
-}")
-
-(define_insn ""
- [(set (pc)
- (if_then_else (eq (match_operand:SI 0 "general_operand" "+g")
- (const_int 1))
- (label_ref (match_operand 1 "" ""))
- (pc)))
- (set (match_dup 0)
- (plus:SI (match_dup 0)
- (const_int -1)))]
- ""
- "*
-{
- CC_STATUS_INIT;
- output_asm_insn (AS1 (dec%L0,%0), operands);
- return \"jz %l1\";
-}")
-
-(define_insn ""
- [(set (pc)
- (if_then_else (ne (match_operand:SI 0 "general_operand" "+g")
- (const_int -1))
- (label_ref (match_operand 1 "" ""))
- (pc)))
- (set (match_dup 0)
- (plus:SI (match_dup 0)
- (const_int 1)))]
- ""
- "*
-{
- CC_STATUS_INIT;
- output_asm_insn (AS1 (inc%L0,%0), operands);
- return \"jnz %l1\";
-}")
+ "jmp\\t%*%0"
+ [(set_attr "type" "ibr")])
-(define_insn ""
- [(set (pc)
- (if_then_else (eq (match_operand:SI 0 "general_operand" "+g")
- (const_int -1))
- (label_ref (match_operand 1 "" ""))
- (pc)))
- (set (match_dup 0)
- (plus:SI (match_dup 0)
- (const_int 1)))]
- ""
- "*
-{
- CC_STATUS_INIT;
- output_asm_insn (AS1 (inc%L0,%0), operands);
- return \"jz %l1\";
-}")
+(define_insn "tablejump"
+ [(set (pc) (match_operand:SI 0 "nonimmediate_operand" "rm"))
+ (use (label_ref (match_operand 1 "" "")))]
+ "! flag_pic"
+ "jmp\\t%*%0"
+ [(set_attr "type" "ibr")])
;; Implement switch statements when generating PIC code. Switches are
;; implemented by `tablejump' when not using -fpic.
-
+;;
;; Emit code here to do the range checking and make the index zero based.
-
-(define_expand "casesi"
- [(set (match_dup 5)
- (match_operand:SI 0 "general_operand" ""))
- (set (match_dup 6)
- (minus:SI (match_dup 5)
- (match_operand:SI 1 "general_operand" "")))
- (set (cc0)
- (compare:CC (match_dup 6)
- (match_operand:SI 2 "general_operand" "")))
- (set (pc)
- (if_then_else (gtu (cc0)
- (const_int 0))
- (label_ref (match_operand 4 "" ""))
- (pc)))
- (parallel
- [(set (pc)
- (minus:SI (reg:SI 3)
- (mem:SI (plus:SI (mult:SI (match_dup 6)
- (const_int 4))
- (label_ref (match_operand 3 "" ""))))))
- (clobber (match_scratch:SI 7 ""))])]
- "flag_pic"
- "
-{
- operands[5] = gen_reg_rtx (SImode);
- operands[6] = gen_reg_rtx (SImode);
- current_function_uses_pic_offset_table = 1;
-}")
-
-;; Implement a casesi insn.
-
+;;
;; Each entry in the "addr_diff_vec" looks like this as the result of the
;; two rules below:
;;
;; The subl above calculates "GOT - (( GOT - . ) + [ . - .L2 ])", which
;; evaluates to just ".L2".
-(define_insn ""
- [(set (pc)
- (minus:SI (reg:SI 3)
- (mem:SI (plus:SI
- (mult:SI (match_operand:SI 0 "register_operand" "r")
- (const_int 4))
- (label_ref (match_operand 1 "" ""))))))
- (clobber (match_scratch:SI 2 "=&r"))]
- ""
- "*
+(define_expand "casesi"
+ [(set (match_dup 5)
+ (match_operand:SI 0 "general_operand" ""))
+ (parallel [(set (match_dup 6)
+ (minus:SI (match_dup 5)
+ (match_operand:SI 1 "general_operand" "")))
+ (clobber (reg:CC 17))])
+ (set (reg:CC 17)
+ (compare:CC (match_dup 6)
+ (match_operand:SI 2 "general_operand" "")))
+ (set (pc)
+ (if_then_else (gtu (reg:CC 17)
+ (const_int 0))
+ (label_ref (match_operand 4 "" ""))
+ (pc)))
+ (parallel
+ [(set (match_dup 7)
+ (minus:SI (match_dup 8)
+ (mem:SI (plus:SI (plus:SI (mult:SI (match_dup 6) (const_int 4))
+ (match_dup 8))
+ (const (unspec [(label_ref (match_operand 3 "" ""))] 7))))))
+ (clobber (reg:CC 17))])
+ (parallel [(set (pc) (match_dup 7))
+ (use (label_ref (match_dup 3)))])]
+ "flag_pic"
+ "
{
- rtx xops[4];
-
- xops[0] = operands[0];
- xops[1] = operands[1];
- xops[2] = operands[2];
- xops[3] = pic_offset_table_rtx;
-
- output_asm_insn (AS2 (mov%L2,%3,%2), xops);
- output_asm_insn (\"sub%L2 %l1@GOTOFF(%3,%0,4),%2\", xops);
- output_asm_insn (AS1 (jmp,%*%2), xops);
- ASM_OUTPUT_ALIGN (asm_out_file, i386_align_jumps);
- RET;
+ operands[5] = gen_reg_rtx (SImode);
+ operands[6] = gen_reg_rtx (SImode);
+ operands[7] = gen_reg_rtx (SImode);
+ operands[8] = pic_offset_table_rtx;
+ current_function_uses_pic_offset_table = 1;
}")
-(define_insn "tablejump"
+(define_insn ""
[(set (pc) (match_operand:SI 0 "nonimmediate_operand" "rm"))
(use (label_ref (match_operand 1 "" "")))]
""
+ "jmp\\t%*%0"
+ [(set_attr "type" "ibr")])
+\f
+;; Loop instruction
+;;
+;; This is all complicated by the fact that since this is a jump insn
+;; we must handle our own reloads.
+
+(define_expand "decrement_and_branch_on_count"
+ [(parallel [(set (pc) (if_then_else
+ (ne (match_operand:SI 0 "register_operand" "")
+ (const_int 1))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:SI (match_dup 0)
+ (const_int -1)))
+ (clobber (match_scratch:SI 2 ""))
+ (clobber (reg:CC 17))])]
+ "TARGET_USE_LOOP"
+ "")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (ne (match_operand:SI 1 "register_operand" "c,*r,*r")
+ (const_int 1))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))
+ (set (match_operand:SI 2 "register_operand" "=1,*r,*m*r")
+ (plus:SI (match_dup 1)
+ (const_int -1)))
+ (clobber (match_scratch:SI 3 "=X,X,r"))
+ (clobber (reg:CC 17))]
+ "TARGET_USE_LOOP"
+ "*
+{
+ if (which_alternative != 0)
+ return \"#\";
+ if (get_attr_length (insn) == 2)
+ return \"loop\\t%l0\";
+ else
+ return \"dec{l}\\t%1\;jne\\t%l0\";
+}"
+ [(set_attr "type" "ibr")
+ (set_attr "ppro_uops" "many")
+ (set (attr "length")
+ (if_then_else (and (eq_attr "alternative" "0")
+ (and (ge (minus (match_dup 0) (pc))
+ (const_int -128))
+ (lt (minus (match_dup 0) (pc))
+ (const_int 124))))
+ (const_int 2)
+ (const_int 16)))])
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (ge (match_operand:SI 1 "register_operand" "c,*r,*r")
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))
+ (set (match_operand:SI 2 "register_operand" "=1,*r,*m*r")
+ (plus:SI (match_dup 1)
+ (const_int -1)))
+ (clobber (match_scratch:SI 3 "=X,X,r"))
+ (clobber (reg:CC 17))]
+ "TARGET_USE_LOOP && find_reg_note (insn, REG_NONNEG, 0)"
"*
{
- CC_STATUS_INIT;
+ if (which_alternative != 0)
+ return \"#\";
+ if (get_attr_length (insn) == 2)
+ return \"loop\\t%l0\";
+ else
+ return \"dec{l}\\t%1\;jne\\t%l0\";
+}"
+ [(set_attr "type" "ibr")
+ (set_attr "ppro_uops" "many")
+ (set (attr "length")
+ (if_then_else (and (eq_attr "alternative" "0")
+ (and (ge (minus (match_dup 0) (pc))
+ (const_int -128))
+ (lt (minus (match_dup 0) (pc))
+ (const_int 124))))
+ (const_int 2)
+ (const_int 16)))])
- return AS1 (jmp,%*%0);
-}")
+(define_split
+ [(set (pc)
+ (if_then_else (ne (match_operand:SI 1 "register_operand" "")
+ (const_int 1))
+ (match_operand 0 "" "")
+ (pc)))
+ (set (match_operand:SI 2 "register_operand" "")
+ (plus:SI (match_dup 1)
+ (const_int -1)))
+ (clobber (match_scratch:SI 3 ""))
+ (clobber (reg:CC 17))]
+ "TARGET_USE_LOOP && reload_completed
+ && ! (REGNO (operands[1]) == 2 && rtx_equal_p (operands[1], operands[2]))"
+ [(set (match_dup 2) (match_dup 1))
+ (parallel [(set (reg:CCNO 17)
+ (compare:CCNO (plus:SI (match_dup 2) (const_int -1))
+ (const_int 0)))
+ (set (match_dup 2) (plus:SI (match_dup 2) (const_int -1)))])
+ (set (pc) (if_then_else (ne (reg:CCNO 17) (const_int 0))
+ (match_dup 0)
+ (pc)))]
+ "")
+
+(define_split
+ [(set (pc)
+ (if_then_else (ne (match_operand:SI 1 "register_operand" "")
+ (const_int 1))
+ (match_operand 0 "" "")
+ (pc)))
+ (set (match_operand:SI 2 "memory_operand" "")
+ (plus:SI (match_dup 1)
+ (const_int -1)))
+ (clobber (match_scratch:SI 3 ""))
+ (clobber (reg:CC 17))]
+ "TARGET_USE_LOOP && reload_completed"
+ [(set (match_dup 3) (match_dup 1))
+ (parallel [(set (reg:CCNO 17)
+ (compare:CCNO (plus:SI (match_dup 3) (const_int -1))
+ (const_int 0)))
+ (set (match_dup 3) (plus:SI (match_dup 3) (const_int -1)))])
+ (set (match_dup 2) (match_dup 3))
+ (set (pc) (if_then_else (ne (reg:CCNO 17) (const_int 0))
+ (match_dup 0)
+ (pc)))]
+ "")
-;; Call insns.
+(define_split
+ [(set (pc)
+ (if_then_else (ge (match_operand:SI 1 "register_operand" "")
+ (const_int 0))
+ (match_operand 0 "" "")
+ (pc)))
+ (set (match_operand:SI 2 "register_operand" "")
+ (plus:SI (match_dup 1)
+ (const_int -1)))
+ (clobber (match_scratch:SI 3 ""))
+ (clobber (reg:CC 17))]
+ "TARGET_USE_LOOP && reload_completed
+ && ! (REGNO (operands[1]) == 2 && rtx_equal_p (operands[1], operands[2]))"
+ [(set (match_dup 2) (match_dup 1))
+ (parallel [(set (reg:CCNO 17)
+ (compare:CCNO (plus:SI (match_dup 2) (const_int -1))
+ (const_int 0)))
+ (set (match_dup 2) (plus:SI (match_dup 2) (const_int -1)))])
+ (set (pc) (if_then_else (lt (reg:CCNO 17) (const_int 0))
+ (match_dup 0)
+ (pc)))]
+ "")
+
+(define_split
+ [(set (pc)
+ (if_then_else (ge (match_operand:SI 1 "register_operand" "")
+ (const_int 0))
+ (match_operand 0 "" "")
+ (pc)))
+ (set (match_operand:SI 2 "memory_operand" "")
+ (plus:SI (match_dup 1)
+ (const_int -1)))
+ (clobber (match_scratch:SI 3 ""))
+ (clobber (reg:CC 17))]
+ "TARGET_USE_LOOP && reload_completed"
+ [(set (match_dup 3) (match_dup 1))
+ (parallel [(set (reg:CCNO 17)
+ (compare:CCNO (plus:SI (match_dup 3) (const_int -1))
+ (const_int 0)))
+ (set (match_dup 3) (plus:SI (match_dup 3) (const_int -1)))])
+ (set (match_dup 2) (match_dup 3))
+ (set (pc) (if_then_else (lt (reg:CCNO 17) (const_int 0))
+ (match_dup 0)
+ (pc)))]
+ "")
+\f
+;; Call instructions.
;; If generating PIC code, the predicate indirect_operand will fail
;; for operands[0] containing symbolic references on all of the named
""
"*
{
- if (GET_CODE (operands[0]) == MEM
- && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
- {
- operands[0] = XEXP (operands[0], 0);
- return AS1 (call,%*%0);
- }
- else
- return AS1 (call,%P0);
-}")
+ if (constant_call_address_operand (operands[0]))
+ return \"call\\t%P0\";
+
+ operands[0] = XEXP (operands[0], 0);
+ return \"call\\t%*%0\";
+}"
+ [(set_attr "type" "call")])
(define_insn ""
- [(call (mem:QI (match_operand:SI 0 "symbolic_operand" ""))
- (match_operand:SI 1 "general_operand" "g"))
+ [(call (match_operand:QI 0 "constant_call_address_operand" "")
+ (match_operand:SI 1 "general_operand" "g"))
(set (reg:SI 7) (plus:SI (reg:SI 7)
- (match_operand:SI 3 "immediate_operand" "i")))]
+ (match_operand:SI 3 "immediate_operand" "i")))]
"!HALF_PIC_P ()"
- "call %P0")
+ "call\\t%P0"
+ [(set_attr "type" "call")])
(define_expand "call"
[(call (match_operand:QI 0 "indirect_operand" "")
""
"*
{
- if (GET_CODE (operands[0]) == MEM
- && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
- {
- operands[0] = XEXP (operands[0], 0);
- return AS1 (call,%*%0);
- }
- else
- return AS1 (call,%P0);
-}")
+ if (constant_call_address_operand (operands[0]))
+ return \"call\\t%P0\";
+
+ operands[0] = XEXP (operands[0], 0);
+ return \"call\\t%*%0\";
+}"
+ [(set_attr "type" "call")])
(define_insn ""
- [(call (mem:QI (match_operand:SI 0 "symbolic_operand" ""))
- (match_operand:SI 1 "general_operand" "g"))]
- ;; Operand 1 not used on the i386.
+ [(call (match_operand:QI 0 "constant_call_address_operand" "")
+ (match_operand:SI 1 "general_operand" "g"))]
"!HALF_PIC_P ()"
- "call %P0")
+ "call\\t%P0"
+ [(set_attr "type" "call")])
;; Call subroutine, returning value in operand 0
;; (which must be a hard register).
""
"*
{
- if (GET_CODE (operands[1]) == MEM
- && ! CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
- {
- operands[1] = XEXP (operands[1], 0);
- output_asm_insn (AS1 (call,%*%1), operands);
- }
- else
- output_asm_insn (AS1 (call,%P1), operands);
-
- RET;
-}")
+ if (constant_call_address_operand (operands[1]))
+ return \"call\\t%P1\";
+
+ operands[1] = XEXP (operands[1], 0);
+ return \"call\\t%*%1\";
+}"
+ [(set_attr "type" "callv")])
(define_insn ""
[(set (match_operand 0 "" "=rf")
- (call (mem:QI (match_operand:SI 1 "symbolic_operand" ""))
+ (call (match_operand:QI 1 "constant_call_address_operand" "")
(match_operand:SI 2 "general_operand" "g")))
(set (reg:SI 7) (plus:SI (reg:SI 7)
(match_operand:SI 4 "immediate_operand" "i")))]
"!HALF_PIC_P ()"
- "call %P1")
+ "call\\t%P1"
+ [(set_attr "type" "callv")])
(define_expand "call_value"
[(set (match_operand 0 "" "")
""
"*
{
- if (GET_CODE (operands[1]) == MEM
- && ! CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
- {
- operands[1] = XEXP (operands[1], 0);
- output_asm_insn (AS1 (call,%*%1), operands);
- }
- else
- output_asm_insn (AS1 (call,%P1), operands);
-
- RET;
-}")
+ if (constant_call_address_operand (operands[1]))
+ return \"call\\t%P1\";
+
+ operands[1] = XEXP (operands[1], 0);
+ return \"call\\t%*%1\";
+}"
+ [(set_attr "type" "callv")])
(define_insn ""
[(set (match_operand 0 "" "=rf")
- (call (mem:QI (match_operand:SI 1 "symbolic_operand" ""))
+ (call (match_operand:QI 1 "constant_call_address_operand" "")
(match_operand:SI 2 "general_operand" "g")))]
- ;; Operand 2 not used on the i386.
"!HALF_PIC_P ()"
- "call %P1")
+ "call\\t%P1"
+ [(set_attr "type" "callv")])
;; Call subroutine returning any type.
DONE;
}")
+\f
+;; Prologue and epilogue instructions
;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and
;; all of memory. This blocks insns from being moved across this point.
[(unspec_volatile [(const_int 0)] 0)]
""
""
- [(set_attr "memory" "none")])
+ [(set_attr "length" "0")])
;; Insn emitted into the body of a function to return from a function.
;; This is only done if the function's epilogue is known to be simple.
[(return)]
"reload_completed"
"ret"
- [(set_attr "memory" "none")])
+ [(set_attr "length" "1")])
(define_insn "return_pop_internal"
[(return)
(use (match_operand:SI 0 "const_int_operand" ""))]
"reload_completed"
- "ret %0"
- [(set_attr "memory" "none")])
+ "ret\\t%0"
+ [(set_attr "length" "3")])
(define_insn "nop"
[(const_int 0)]
""
"nop"
- [(set_attr "memory" "none")])
+ [(set_attr "length" "1")
+ (set_attr "ppro_uops" "one")])
(define_expand "prologue"
[(const_int 1)]
""
- "
-{
- ix86_expand_prologue ();
- DONE;
-}")
+ "ix86_expand_prologue (); DONE;")
-;; The use of UNSPEC here is currently not necessary - a simple SET of ebp
-;; to itself would be enough. But this way we are safe even if some optimizer
-;; becomes too clever in the future.
-(define_insn "prologue_set_stack_ptr"
- [(set (reg:SI 7)
- (minus:SI (reg:SI 7) (match_operand:SI 0 "immediate_operand" "i")))
- (set (reg:SI 6) (unspec:SI [(reg:SI 6)] 4))]
+(define_insn "prologue_set_got"
+ [(set (match_operand:SI 0 "" "")
+ (unspec_volatile
+ [(plus:SI (match_dup 0)
+ (plus:SI (match_operand:SI 1 "symbolic_operand" "")
+ (minus:SI (pc) (match_operand 2 "" ""))))] 1))
+ (clobber (reg:CC 17))]
""
"*
{
- rtx xops [2];
-
- xops[0] = operands[0];
- xops[1] = stack_pointer_rtx;
- output_asm_insn (AS2 (sub%L1,%0,%1), xops);
- RET;
+ if (GET_CODE (operands[2]) == LABEL_REF)
+ operands[2] = XEXP (operands[2], 0);
+ if (TARGET_DEEP_BRANCH_PREDICTION)
+ return \"add{l}\\t{%1, %0|%0, %1}\";
+ else
+ return \"add{l}\\t{%1+[.-%X2], %0|%0, %a1+(.-%X2)}\";
}"
- [(set_attr "memory" "none")])
+ [(set_attr "type" "alu")])
-(define_insn "prologue_set_got"
+(define_insn "prologue_get_pc"
[(set (match_operand:SI 0 "" "")
- (unspec_volatile
- [(plus:SI (match_dup 0)
- (plus:SI (match_operand:SI 1 "symbolic_operand" "")
- (minus:SI (pc) (match_operand 2 "" ""))))] 1))]
+ (unspec_volatile [(plus:SI (pc) (match_operand 1 "" ""))] 2))]
""
"*
{
- char buffer[64];
+ if (GET_CODE (operands[1]) == LABEL_REF)
+ operands[1] = XEXP (operands[1], 0);
+ output_asm_insn (\"call\\t%X1\", operands);
+ if (! TARGET_DEEP_BRANCH_PREDICTION)
+ {
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\",
+ CODE_LABEL_NUMBER (operands[1]));
+ }
+ RET;
+}"
+ [(set_attr "type" "multi")])
- if (TARGET_DEEP_BRANCH_PREDICTION)
+(define_expand "epilogue"
+ [(const_int 1)]
+ ""
+ "ix86_expand_epilogue (); DONE;")
+
+(define_insn "leave"
+ [(set (reg:SI 7) (reg:SI 6))
+ (set (reg:SI 6) (mem:SI (pre_dec:SI (reg:SI 7))))]
+ ""
+ "leave"
+ [(set_attr "length" "1")
+ (set_attr "ppro_uops" "few")])
+\f
+(define_expand "ffssi2"
+ [(set (match_operand:SI 0 "general_operand" "")
+ (ffs:SI (match_operand:SI 1 "general_operand" "")))]
+ ""
+ "
+{
+ rtx out = gen_reg_rtx (SImode), tmp = gen_reg_rtx (SImode);
+ rtx in = operands[1];
+
+ if (TARGET_CMOVE)
{
- sprintf (buffer, \"addl %s,%%0\", XSTR (operands[1], 0));
- output_asm_insn (buffer, operands);
+ emit_move_insn (tmp, constm1_rtx);
+ emit_insn (gen_ffssi_1 (out, in));
+ emit_insn (gen_rtx_SET (VOIDmode, out,
+ gen_rtx_IF_THEN_ELSE (SImode,
+ gen_rtx_EQ (VOIDmode, gen_rtx_REG (CCmode, FLAGS_REG),
+ const0_rtx),
+ tmp,
+ out)));
}
- else
+ else
{
- sprintf (buffer, \"addl %s+[.-%%X2],%%0\", XSTR (operands[1], 0));
- output_asm_insn (buffer, operands);
- }
- RET;
+ emit_move_insn (tmp, const0_rtx);
+ emit_insn (gen_ffssi_1 (out, in));
+ emit_insn (gen_rtx_SET (VOIDmode,
+ gen_rtx_STRICT_LOW_PART (VOIDmode, gen_lowpart (QImode, tmp)),
+ gen_rtx_EQ (QImode, gen_rtx_REG (CCmode, FLAGS_REG),
+ const0_rtx)));
+ emit_insn (gen_negsi2 (tmp, tmp));
+ emit_insn (gen_iorsi3 (out, out, tmp));
+ }
+ emit_insn (gen_addsi3 (out, out, const1_rtx));
+
+ emit_move_insn (operands[0], out);
+ DONE;
}")
-(define_insn "prologue_get_pc"
- [(set (match_operand:SI 0 "" "")
- (unspec_volatile [(plus:SI (pc) (match_operand 1 "" ""))] 2))]
- ""
- "*
-{
- output_asm_insn (AS1 (call,%X1), operands);
- if (! TARGET_DEEP_BRANCH_PREDICTION)
- {
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", CODE_LABEL_NUMBER (operands[1]));
- }
- RET;
-}"
- [(set_attr "memory" "none")])
+;; %%% The CCmode here is not strictly correct -- only Z is defined.
+;; But I don't think this can be used except for from above.
+(define_insn "ffssi_1"
+ [(set (reg:CC 17)
+ (compare:CC (match_operand:SI 1 "nonimmediate_operand" "rm")
+ (const_int 0)))
+ (set (match_operand:SI 0 "register_operand" "=r")
+ (unspec:SI [(match_dup 1)] 5))]
+ ""
+ "bsf{l}\\t{%1, %0|%0, %1}"
+ [(set_attr "length_opcode" "3")
+ (set_attr "ppro_uops" "few")])
+
+;; ffshi2 is not useful -- 4 word prefix ops are needed, which is larger
+;; and slower than the two-byte movzx insn needed to do the work in SImode.
+\f
+;; These patterns match the binary 387 instructions for addM3, subM3,
+;; mulM3 and divM3. There are three patterns for each of DFmode and
+;; SFmode. The first is the normal insn, the second the same insn but
+;; with one operand a conversion, and the third the same insn but with
+;; the other operand a conversion. The conversion may be SFmode or
+;; SImode if the target mode DFmode, but only SImode if the target mode
+;; is SFmode.
+
+(define_insn "*fop_sf_1"
+ [(set (match_operand:SF 0 "register_operand" "=f,f")
+ (match_operator:SF 3 "binary_fp_operator"
+ [(match_operand:SF 1 "nonimmediate_operand" "0,fm")
+ (match_operand:SF 2 "nonimmediate_operand" "fm,0")]))]
+ "TARGET_80387"
+ "* return output_387_binary_op (insn, operands);"
+ [(set (attr "type")
+ (cond [(match_operand:SF 3 "mult_operator" "")
+ (const_string "fmul")
+ (match_operand:SF 3 "div_operator" "")
+ (const_string "fdiv")
+ ]
+ (const_string "fop")))])
+
+(define_insn "*fop_sf_2"
+ [(set (match_operand:SF 0 "register_operand" "=f,f")
+ (match_operator:SF 3 "binary_fp_operator"
+ [(float:SF (match_operand:SI 1 "nonimmediate_operand" "m,?r"))
+ (match_operand:SF 2 "register_operand" "0,0")]))]
+ "TARGET_80387 && TARGET_USE_FIOP"
+ "* return which_alternative ? \"#\" : output_387_binary_op (insn, operands);"
+ [(set (attr "type")
+ (cond [(match_operand:SF 3 "mult_operator" "")
+ (const_string "fmul")
+ (match_operand:SF 3 "div_operator" "")
+ (const_string "fdiv")
+ ]
+ (const_string "fop")))
+ (set_attr "fp_int_src" "true")
+ (set_attr "ppro_uops" "many")])
+
+(define_insn "*fop_sf_3"
+ [(set (match_operand:SF 0 "register_operand" "=f,f")
+ (match_operator:SF 3 "binary_fp_operator"
+ [(match_operand:SF 1 "register_operand" "0,0")
+ (float:SF (match_operand:SI 2 "nonimmediate_operand" "m,?r"))]))]
+ "TARGET_80387 && TARGET_USE_FIOP"
+ "* return which_alternative ? \"#\" : output_387_binary_op (insn, operands);"
+ [(set (attr "type")
+ (cond [(match_operand:SF 3 "mult_operator" "")
+ (const_string "fmul")
+ (match_operand:SF 3 "div_operator" "")
+ (const_string "fdiv")
+ ]
+ (const_string "fop")))
+ (set_attr "fp_int_src" "true")
+ (set_attr "ppro_uops" "many")])
+
+(define_insn "*fop_df_1"
+ [(set (match_operand:DF 0 "register_operand" "=f,f")
+ (match_operator:DF 3 "binary_fp_operator"
+ [(match_operand:DF 1 "nonimmediate_operand" "0,fm")
+ (match_operand:DF 2 "nonimmediate_operand" "fm,0")]))]
+ "TARGET_80387"
+ "* return output_387_binary_op (insn, operands);"
+ [(set (attr "type")
+ (cond [(match_operand:DF 3 "mult_operator" "")
+ (const_string "fmul")
+ (match_operand:DF 3 "div_operator" "")
+ (const_string "fdiv")
+ ]
+ (const_string "fop")))])
+
+(define_insn "*fop_df_2"
+ [(set (match_operand:DF 0 "register_operand" "=f,f")
+ (match_operator:DF 3 "binary_fp_operator"
+ [(float:DF (match_operand:SI 1 "nonimmediate_operand" "m,?r"))
+ (match_operand:DF 2 "register_operand" "0,0")]))]
+ "TARGET_80387 && TARGET_USE_FIOP"
+ "* return which_alternative ? \"#\" : output_387_binary_op (insn, operands);"
+ [(set (attr "type")
+ (cond [(match_operand:DF 3 "mult_operator" "")
+ (const_string "fmul")
+ (match_operand:DF 3 "div_operator" "")
+ (const_string "fdiv")
+ ]
+ (const_string "fop")))
+ (set_attr "fp_int_src" "true")
+ (set_attr "ppro_uops" "many")])
+
+(define_insn "*fop_df_3"
+ [(set (match_operand:DF 0 "register_operand" "=f,f")
+ (match_operator:DF 3 "binary_fp_operator"
+ [(match_operand:DF 1 "register_operand" "0,0")
+ (float:DF (match_operand:SI 2 "nonimmediate_operand" "m,?r"))]))]
+ "TARGET_80387 && TARGET_USE_FIOP"
+ "* return which_alternative ? \"#\" : output_387_binary_op (insn, operands);"
+ [(set (attr "type")
+ (cond [(match_operand:DF 3 "mult_operator" "")
+ (const_string "fmul")
+ (match_operand:DF 3 "div_operator" "")
+ (const_string "fdiv")
+ ]
+ (const_string "fop")))
+ (set_attr "fp_int_src" "true")
+ (set_attr "ppro_uops" "many")])
+
+(define_insn "*fop_df_4"
+ [(set (match_operand:DF 0 "register_operand" "=f,f")
+ (match_operator:DF 3 "binary_fp_operator"
+ [(float_extend:DF (match_operand:SF 1 "nonimmediate_operand" "fm,0"))
+ (match_operand:DF 2 "register_operand" "0,f")]))]
+ "TARGET_80387"
+ "* return output_387_binary_op (insn, operands);"
+ [(set (attr "type")
+ (cond [(match_operand:DF 3 "mult_operator" "")
+ (const_string "fmul")
+ (match_operand:DF 3 "div_operator" "")
+ (const_string "fdiv")
+ ]
+ (const_string "fop")))])
+
+(define_insn "*fop_df_5"
+ [(set (match_operand:DF 0 "register_operand" "=f,f")
+ (match_operator:DF 3 "binary_fp_operator"
+ [(match_operand:DF 1 "register_operand" "0,f")
+ (float_extend:DF
+ (match_operand:SF 2 "nonimmediate_operand" "fm,0"))]))]
+ "TARGET_80387"
+ "* return output_387_binary_op (insn, operands);"
+ [(set (attr "type")
+ (cond [(match_operand:DF 3 "mult_operator" "")
+ (const_string "fmul")
+ (match_operand:DF 3 "div_operator" "")
+ (const_string "fdiv")
+ ]
+ (const_string "fop")))])
+
+(define_insn "*fop_xf_1"
+ [(set (match_operand:XF 0 "register_operand" "=f,f")
+ (match_operator:XF 3 "binary_fp_operator"
+ [(match_operand:XF 1 "register_operand" "0,f")
+ (match_operand:XF 2 "register_operand" "f,0")]))]
+ "TARGET_80387"
+ "* return output_387_binary_op (insn, operands);"
+ [(set (attr "type")
+ (cond [(match_operand:DF 3 "mult_operator" "")
+ (const_string "fmul")
+ (match_operand:DF 3 "div_operator" "")
+ (const_string "fdiv")
+ ]
+ (const_string "fop")))])
+
+(define_insn "*fop_xf_2"
+ [(set (match_operand:XF 0 "register_operand" "=f,f")
+ (match_operator:XF 3 "binary_fp_operator"
+ [(float:XF (match_operand:SI 1 "nonimmediate_operand" "m,?r"))
+ (match_operand:XF 2 "register_operand" "0,0")]))]
+ "TARGET_80387 && TARGET_USE_FIOP"
+ "* return which_alternative ? \"#\" : output_387_binary_op (insn, operands);"
+ [(set (attr "type")
+ (cond [(match_operand:DF 3 "mult_operator" "")
+ (const_string "fmul")
+ (match_operand:DF 3 "div_operator" "")
+ (const_string "fdiv")
+ ]
+ (const_string "fop")))
+ (set_attr "fp_int_src" "true")
+ (set_attr "ppro_uops" "many")])
+
+(define_insn "*fop_xf_3"
+ [(set (match_operand:XF 0 "register_operand" "=f,f")
+ (match_operator:XF 3 "binary_fp_operator"
+ [(match_operand:XF 1 "register_operand" "0,0")
+ (float:XF (match_operand:SI 2 "nonimmediate_operand" "m,?r"))]))]
+ "TARGET_80387 && TARGET_USE_FIOP"
+ "* return which_alternative ? \"#\" : output_387_binary_op (insn, operands);"
+ [(set (attr "type")
+ (cond [(match_operand:DF 3 "mult_operator" "")
+ (const_string "fmul")
+ (match_operand:DF 3 "div_operator" "")
+ (const_string "fdiv")
+ ]
+ (const_string "fop")))
+ (set_attr "fp_int_src" "true")
+ (set_attr "ppro_uops" "many")])
+
+(define_insn "*fop_xf_4"
+ [(set (match_operand:XF 0 "register_operand" "=f,f")
+ (match_operator:XF 3 "binary_fp_operator"
+ [(float_extend:XF (match_operand:SF 1 "nonimmediate_operand" "fm,0"))
+ (match_operand:XF 2 "register_operand" "0,f")]))]
+ "TARGET_80387"
+ "* return output_387_binary_op (insn, operands);"
+ [(set (attr "type")
+ (cond [(match_operand:DF 3 "mult_operator" "")
+ (const_string "fmul")
+ (match_operand:DF 3 "div_operator" "")
+ (const_string "fdiv")
+ ]
+ (const_string "fop")))])
+
+(define_insn "*fop_xf_5"
+ [(set (match_operand:XF 0 "register_operand" "=f,f")
+ (match_operator:XF 3 "binary_fp_operator"
+ [(match_operand:XF 1 "register_operand" "0,f")
+ (float_extend:XF
+ (match_operand:SF 2 "nonimmediate_operand" "fm,0"))]))]
+ "TARGET_80387"
+ "* return output_387_binary_op (insn, operands);"
+ [(set (attr "type")
+ (cond [(match_operand:DF 3 "mult_operator" "")
+ (const_string "fmul")
+ (match_operand:DF 3 "div_operator" "")
+ (const_string "fdiv")
+ ]
+ (const_string "fop")))])
+
+(define_insn "*fop_xf_6"
+ [(set (match_operand:XF 0 "register_operand" "=f,f")
+ (match_operator:XF 3 "binary_fp_operator"
+ [(float_extend:XF (match_operand:DF 1 "nonimmediate_operand" "fm,0"))
+ (match_operand:XF 2 "register_operand" "0,f")]))]
+ "TARGET_80387"
+ "* return output_387_binary_op (insn, operands);"
+ [(set (attr "type")
+ (cond [(match_operand:DF 3 "mult_operator" "")
+ (const_string "fmul")
+ (match_operand:DF 3 "div_operator" "")
+ (const_string "fdiv")
+ ]
+ (const_string "fop")))])
+
+(define_insn "*fop_xf_7"
+ [(set (match_operand:XF 0 "register_operand" "=f,f")
+ (match_operator:XF 3 "binary_fp_operator"
+ [(match_operand:XF 1 "register_operand" "0,f")
+ (float_extend:XF
+ (match_operand:DF 2 "nonimmediate_operand" "fm,0"))]))]
+ "TARGET_80387"
+ "* return output_387_binary_op (insn, operands);"
+ [(set (attr "type")
+ (cond [(match_operand:DF 3 "mult_operator" "")
+ (const_string "fmul")
+ (match_operand:DF 3 "div_operator" "")
+ (const_string "fdiv")
+ ]
+ (const_string "fop")))])
+
+(define_split
+ [(set (match_operand 0 "register_operand" "")
+ (match_operator 3 "binary_fp_operator"
+ [(float (match_operand:SI 1 "register_operand" ""))
+ (match_operand 2 "register_operand" "")]))]
+ "TARGET_80387 && reload_completed
+ && FLOAT_MODE_P (GET_MODE (operands[0]))"
+ [(set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 1))
+ (set (match_dup 0)
+ (match_op_dup 3 [(match_dup 4) (match_dup 2)]))
+ (parallel [(set (match_dup 1) (mem:SI (reg:SI 7)))
+ (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4)))])]
+ "operands[4] = gen_rtx_FLOAT (GET_MODE (operands[0]),
+ gen_rtx_MEM (SImode, stack_pointer_rtx));")
+
+(define_split
+ [(set (match_operand 0 "register_operand" "")
+ (match_operator 3 "binary_fp_operator"
+ [(match_operand 1 "register_operand" "")
+ (float (match_operand:SI 2 "register_operand" ""))]))]
+ "TARGET_80387 && reload_completed
+ && FLOAT_MODE_P (GET_MODE (operands[0]))"
+ [(set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 2))
+ (set (match_dup 0)
+ (match_op_dup 3 [(match_dup 1) (match_dup 4)]))
+ (parallel [(set (match_dup 2) (mem:SI (reg:SI 7)))
+ (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4)))])]
+ "operands[4] = gen_rtx_FLOAT (GET_MODE (operands[0]),
+ gen_rtx_MEM (SImode, stack_pointer_rtx));")
+\f
+;; FPU special functions.
+
+(define_insn "sqrtsf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (sqrt:SF (match_operand:SF 1 "register_operand" "0")))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387"
+ "fsqrt"
+ [(set_attr "type" "fpspc")])
+
+(define_insn "sqrtdf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (sqrt:DF (match_operand:DF 1 "register_operand" "0")))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) "
+ "fsqrt"
+ [(set_attr "type" "fpspc")])
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (sqrt:DF (float_extend:DF
+ (match_operand:SF 1 "register_operand" "0"))))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387"
+ "fsqrt"
+ [(set_attr "type" "fpspc")])
+
+(define_insn "sqrtxf2"
+ [(set (match_operand:XF 0 "register_operand" "=f")
+ (sqrt:XF (match_operand:XF 1 "register_operand" "0")))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) "
+ "fsqrt"
+ [(set_attr "type" "fpspc")])
+
+(define_insn ""
+ [(set (match_operand:XF 0 "register_operand" "=f")
+ (sqrt:XF (float_extend:XF
+ (match_operand:DF 1 "register_operand" "0"))))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387"
+ "fsqrt"
+ [(set_attr "type" "fpspc")])
+
+(define_insn ""
+ [(set (match_operand:XF 0 "register_operand" "=f")
+ (sqrt:XF (float_extend:XF
+ (match_operand:SF 1 "register_operand" "0"))))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387"
+ "fsqrt"
+ [(set_attr "type" "fpspc")])
+
+(define_insn "sindf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (unspec:DF [(match_operand:DF 1 "register_operand" "0")] 1))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
+ "fsin"
+ [(set_attr "type" "fpspc")])
+
+(define_insn "sinsf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (unspec:SF [(match_operand:SF 1 "register_operand" "0")] 1))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
+ "fsin"
+ [(set_attr "type" "fpspc")])
-(define_insn "prologue_get_pc_and_set_got"
- [(unspec_volatile [(match_operand:SI 0 "" "")] 3)]
- ""
- "*
-{
- operands[1] = gen_label_rtx ();
- output_asm_insn (AS1 (call,%X1), operands);
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\",
- CODE_LABEL_NUMBER (operands[1]));
- output_asm_insn (AS1 (pop%L0,%0), operands);
- output_asm_insn (\"addl $%__GLOBAL_OFFSET_TABLE_+[.-%X1],%0\", operands);
- RET;
-}"
- [(set_attr "memory" "none")])
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (unspec:DF [(float_extend:DF
+ (match_operand:SF 1 "register_operand" "0"))] 1))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
+ "fsin"
+ [(set_attr "type" "fpspc")])
-(define_expand "epilogue"
- [(const_int 1)]
- ""
- "
-{
- ix86_expand_epilogue ();
- DONE;
-}")
+(define_insn "sinxf2"
+ [(set (match_operand:XF 0 "register_operand" "=f")
+ (unspec:XF [(match_operand:XF 1 "register_operand" "0")] 1))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
+ "fsin"
+ [(set_attr "type" "fpspc")])
-(define_insn "epilogue_set_stack_ptr"
- [(set (reg:SI 7) (reg:SI 6))
- (clobber (reg:SI 6))]
- ""
- "*
-{
- rtx xops [2];
+(define_insn "cosdf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (unspec:DF [(match_operand:DF 1 "register_operand" "0")] 2))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
+ "fcos"
+ [(set_attr "type" "fpspc")])
- xops[0] = frame_pointer_rtx;
- xops[1] = stack_pointer_rtx;
- output_asm_insn (AS2 (mov%L0,%0,%1), xops);
- RET;
-}"
- [(set_attr "memory" "none")])
+(define_insn "cossf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (unspec:SF [(match_operand:SF 1 "register_operand" "0")] 2))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
+ "fcos"
+ [(set_attr "type" "fpspc")])
-(define_insn "leave"
- [(const_int 2)
- (clobber (reg:SI 6))
- (clobber (reg:SI 7))]
- ""
- "leave"
- [(set_attr "memory" "none")])
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (unspec:DF [(float_extend:DF
+ (match_operand:SF 1 "register_operand" "0"))] 2))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
+ "fcos"
+ [(set_attr "type" "fpspc")])
-(define_insn "pop"
- [(set (match_operand:SI 0 "register_operand" "r")
- (mem:SI (reg:SI 7)))
- (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4)))]
- ""
- "*
-{
- output_asm_insn (AS1 (pop%L0,%P0), operands);
- RET;
-}"
- [(set_attr "memory" "load")])
+(define_insn "cosxf2"
+ [(set (match_operand:XF 0 "register_operand" "=f")
+ (unspec:XF [(match_operand:XF 1 "register_operand" "0")] 2))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
+ "fcos"
+ [(set_attr "type" "fpspc")])
+\f
+;; Block operation instructions
(define_expand "movstrsi"
[(parallel [(set (match_operand:BLK 0 "memory_operand" "")
;; But strength reduction might offset the MEM expression. So we let
;; reload put the address into %edi & %esi.
-(define_insn ""
+(define_insn "*movstrsi_1"
[(set (mem:BLK (match_operand:SI 0 "address_operand" "D"))
(mem:BLK (match_operand:SI 1 "address_operand" "S")))
(use (match_operand:SI 2 "const_int_operand" "n"))
xops[0] = GEN_INT ((INTVAL (operands[2]) >> 2) & 0x3fffffff);
xops[1] = operands[4];
- output_asm_insn (AS2 (mov%L1,%0,%1), xops);
-#ifdef INTEL_SYNTAX
- output_asm_insn (\"rep movsd\", xops);
-#else
- output_asm_insn (\"rep\;movsl\", xops);
-#endif
+ output_asm_insn (\"mov{l}\\t{%0, %1|%1,%0}\", xops);
+ output_asm_insn (\"{rep\;movsl|rep movsd}\", xops);
}
if (INTVAL (operands[2]) & 0x02)
output_asm_insn (\"movsw\", operands);
else
abort ();
RET;
-}")
+}"
+ [(set_attr "type" "multi")])
(define_expand "clrstrsi"
[(set (match_dup 3) (const_int 0))
;; But strength reduction might offset the MEM expression. So we let
;; reload put the address into %edi.
-(define_insn "*bzero"
+(define_insn "*clrstrsi_1"
[(set (mem:BLK (match_operand:SI 0 "address_operand" "D"))
(const_int 0))
(use (match_operand:SI 1 "const_int_operand" "n"))
if (count / 4 < ((int) ix86_cpu < (int)PROCESSOR_PENTIUM ? 4 : 6))
{
do
-#ifdef INTEL_SYNTAX
- output_asm_insn (\"stosd\", xops);
-#else
- output_asm_insn (\"stosl\", xops);
-#endif
+ output_asm_insn (\"{stosl|stosd}\", xops);
while ((count -= 4) > 3);
}
else
{
- output_asm_insn (AS2 (mov%L1,%0,%1), xops);
-#ifdef INTEL_SYNTAX
- output_asm_insn (\"rep stosd\", xops);
-#else
- output_asm_insn (\"rep\;stosl\", xops);
-#endif
+ output_asm_insn (\"mov{l}\\t{%0, %1|%1, %0}\", xops);
+ output_asm_insn (\"{rep\;stosl|rep stosd}\", xops);
}
}
if (INTVAL (operands[1]) & 0x02)
else
abort ();
RET;
-}")
+}"
+ [(set_attr "type" "multi")])
(define_expand "cmpstrsi"
- [(parallel [(set (match_operand:SI 0 "general_operand" "")
- (compare:SI (match_operand:BLK 1 "general_operand" "")
- (match_operand:BLK 2 "general_operand" "")))
- (use (match_operand:SI 3 "general_operand" ""))
- (use (match_operand:SI 4 "immediate_operand" ""))
- (clobber (match_dup 5))
- (clobber (match_dup 6))
- (clobber (match_dup 3))])]
+ [(set (match_operand:SI 0 "register_operand" "")
+ (compare:SI (match_operand:BLK 1 "general_operand" "")
+ (match_operand:BLK 2 "general_operand" "")))
+ (use (match_operand:SI 3 "general_operand" ""))
+ (use (match_operand:SI 4 "immediate_operand" ""))]
""
"
{
- rtx addr1, addr2;
+ rtx addr1, addr2, out, outlow, count, countreg, align;
+
+ out = operands[0];
+ if (GET_CODE (out) != REG)
+ out = gen_reg_rtx (SImode);
addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
addr2 = copy_to_mode_reg (Pmode, XEXP (operands[2], 0));
- operands[3] = copy_to_mode_reg (SImode, operands[3]);
+
+ count = operands[3];
+ countreg = copy_to_mode_reg (SImode, count);
+
+ /* %%% Iff we are testing strict equality, we can use known alignment
+ to good advantage. This may be possible with combine, particularly
+ once cc0 is dead. */
+ align = operands[4];
- operands[5] = addr1;
- operands[6] = addr2;
+ if (GET_CODE (count) == CONST_INT)
+ {
+ if (INTVAL (count) == 0)
+ {
+ emit_move_insn (operands[0], const0_rtx);
+ DONE;
+ }
+ emit_insn (gen_cmpstrsi_nz_1 (addr1, addr2, countreg, align));
+ }
+ else
+ emit_insn (gen_cmpstrsi_1 (addr1, addr2, countreg, align));
+
+ outlow = gen_lowpart (QImode, out);
+ emit_insn (gen_cmpintqi (outlow));
+ emit_move_insn (out, gen_rtx_SIGN_EXTEND (SImode, outlow));
- operands[1] = gen_rtx_MEM (BLKmode, addr1);
- operands[2] = gen_rtx_MEM (BLKmode, addr2);
+ if (operands[0] != out)
+ emit_move_insn (operands[0], out);
+ DONE;
}")
+;; Produce a tri-state integer (-1, 0, 1) from condition codes.
+
+(define_expand "cmpintqi"
+ [(set (match_dup 1)
+ (gtu:QI (reg:CC 17) (const_int 0)))
+ (set (match_dup 2)
+ (ltu:QI (reg:CC 17) (const_int 0)))
+ (parallel [(set (match_operand:QI 0 "register_operand" "")
+ (minus:QI (match_dup 1)
+ (match_dup 2)))
+ (clobber (reg:CC 17))])]
+ ""
+ "operands[1] = gen_reg_rtx (QImode);
+ operands[2] = gen_reg_rtx (QImode);")
+
;; memcmp recognizers. The `cmpsb' opcode does nothing if the count is
;; zero. Emit extra code to make sure that a zero-length compare is EQ.
-
+;;
;; It might seem that operands 0 & 1 could use predicate register_operand.
;; But strength reduction might offset the MEM expression. So we let
;; reload put the address into %edi & %esi.
-;; ??? Most comparisons have a constant length, and it's therefore
-;; possible to know that the length is non-zero, and to avoid the extra
-;; code to handle zero-length compares.
-
-(define_insn ""
- [(set (match_operand:SI 0 "register_operand" "=&r")
- (compare:SI (mem:BLK (match_operand:SI 1 "address_operand" "S"))
- (mem:BLK (match_operand:SI 2 "address_operand" "D"))))
- (use (match_operand:SI 3 "register_operand" "c"))
- (use (match_operand:SI 4 "immediate_operand" "i"))
- (clobber (match_dup 1))
- (clobber (match_dup 2))
- (clobber (match_dup 3))]
- ""
- "*
-{
- rtx xops[2], label;
-
- label = gen_label_rtx ();
-
- output_asm_insn (\"cld\", operands);
- output_asm_insn (AS2 (xor%L0,%0,%0), operands);
- output_asm_insn (\"repz\;cmps%B2\", operands);
- output_asm_insn (\"je %l0\", &label);
-
- xops[0] = operands[0];
- xops[1] = const1_rtx;
- output_asm_insn (AS2 (sbb%L0,%0,%0), xops);
- if (QI_REG_P (xops[0]))
- output_asm_insn (AS2 (or%B0,%1,%b0), xops);
- else
- output_asm_insn (AS2 (or%L0,%1,%0), xops);
-
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", CODE_LABEL_NUMBER (label));
- RET;
-}")
-
-(define_insn ""
- [(set (cc0)
- (compare:SI (mem:BLK (match_operand:SI 0 "address_operand" "S"))
+(define_insn "cmpstrsi_nz_1"
+ [(set (reg:CC 17)
+ (compare:CC (mem:BLK (match_operand:SI 0 "address_operand" "S"))
(mem:BLK (match_operand:SI 1 "address_operand" "D"))))
(use (match_operand:SI 2 "register_operand" "c"))
(use (match_operand:SI 3 "immediate_operand" "i"))
(clobber (match_dup 1))
(clobber (match_dup 2))]
""
- "*
-{
- rtx xops[2];
-
- cc_status.flags |= CC_NOT_SIGNED;
-
- xops[0] = gen_rtx_REG (QImode, 0);
- xops[1] = CONST0_RTX (QImode);
-
- output_asm_insn (\"cld\", operands);
- output_asm_insn (AS2 (test%B0,%1,%0), xops);
- return \"repz\;cmps%B2\";
-}")
-
-\f
-;; Note, you cannot optimize away the branch following the bsfl by assuming
-;; that the destination is not modified if the input is 0, since not all
-;; x86 implementations do this.
-
-(define_expand "ffssi2"
- [(set (match_operand:SI 0 "general_operand" "")
- (ffs:SI (match_operand:SI 1 "general_operand" "")))]
- ""
- "
-{
- rtx label = gen_label_rtx (), temp = gen_reg_rtx (SImode);
+ "cld\;repz{\;| }cmpsb"
+ [(set_attr "type" "multi")
+ (set_attr "length" "3")])
- emit_insn (gen_ffssi_1 (temp, operands[1]));
- emit_cmp_insn (operands[1], const0_rtx, NE, NULL_RTX, SImode, 0, 0);
- emit_jump_insn (gen_bne (label));
- emit_move_insn (temp, constm1_rtx);
- emit_label (label);
- temp = expand_binop (SImode, add_optab, temp, const1_rtx,
- operands[0], 0, OPTAB_WIDEN);
-
- if (temp != operands[0])
- emit_move_insn (operands[0], temp);
- DONE;
-}")
+;; The same, but the count is not known to not be zero.
-(define_insn "ffssi_1"
- [(set (match_operand:SI 0 "register_operand" "=r")
- (unspec:SI [(match_operand:SI 1 "nonimmediate_operand" "rm")] 5))]
+(define_insn "cmpstrsi_1"
+ [(set (reg:CC 17)
+ (if_then_else:CC (ne (match_operand:SI 2 "register_operand" "c")
+ (const_int 0))
+ (compare:SI (mem:BLK (match_operand:SI 0 "address_operand" "S"))
+ (mem:BLK (match_operand:SI 1 "address_operand" "D")))
+ (const_int 0)))
+ (use (match_operand:SI 3 "immediate_operand" "i"))
+ (clobber (match_dup 0))
+ (clobber (match_dup 1))
+ (clobber (match_dup 2))]
""
- "* return AS2 (bsf%L0,%1,%0);")
+ ;; The initial compare sets the zero flag.
+ "cmp{l}\\t%2, %2\;cld\;repz{\;| }cmpsb"
+ [(set_attr "type" "multi")
+ (set_attr "length" "5")])
-(define_expand "ffshi2"
- [(set (match_operand:SI 0 "general_operand" "")
- (ffs:HI (match_operand:HI 1 "general_operand" "")))]
+(define_expand "strlensi"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (unspec:SI [(match_operand:BLK 1 "general_operand" "")
+ (match_operand:QI 2 "immediate_operand" "")
+ (match_operand:SI 3 "immediate_operand" "")] 0))]
""
"
{
- rtx label = gen_label_rtx (), temp = gen_reg_rtx (HImode);
-
- emit_insn (gen_ffshi_1 (temp, operands[1]));
- emit_cmp_insn (operands[1], const0_rtx, NE, NULL_RTX, HImode, 0, 0);
- emit_jump_insn (gen_bne (label));
- emit_move_insn (temp, constm1_rtx);
- emit_label (label);
- temp = expand_binop (HImode, add_optab, temp, const1_rtx,
- operands[0], 0, OPTAB_WIDEN);
-
- if (temp != operands[0])
- emit_move_insn (operands[0], temp);
- DONE;
-}")
-
-(define_insn "ffshi_1"
- [(set (match_operand:HI 0 "register_operand" "=r")
- (unspec:HI [(match_operand:SI 1 "nonimmediate_operand" "rm")] 5))]
- ""
- "* return AS2 (bsf%W0,%1,%0);")
-\f
-;; These patterns match the binary 387 instructions for addM3, subM3,
-;; mulM3 and divM3. There are three patterns for each of DFmode and
-;; SFmode. The first is the normal insn, the second the same insn but
-;; with one operand a conversion, and the third the same insn but with
-;; the other operand a conversion.
-
-(define_insn ""
- [(set (match_operand:DF 0 "register_operand" "=f,f")
- (match_operator:DF 3 "binary_387_op"
- [(match_operand:DF 1 "nonimmediate_operand" "0,fm")
- (match_operand:DF 2 "nonimmediate_operand" "fm,0")]))]
- "TARGET_80387"
- "* return output_387_binary_op (insn, operands);"
- [(set (attr "type")
- (cond [(match_operand:DF 3 "is_mul" "")
- (const_string "fpmul")
- (match_operand:DF 3 "is_div" "")
- (const_string "fpdiv")
- ]
- (const_string "fpop")
- )
- )])
-
-(define_insn ""
- [(set (match_operand:XF 0 "register_operand" "=f,f")
- (match_operator:XF 3 "binary_387_op"
- [(match_operand:XF 1 "register_operand" "0,f")
- (match_operand:XF 2 "register_operand" "f,0")]))]
- "TARGET_80387"
- "* return output_387_binary_op (insn, operands);"
- [(set (attr "type")
- (cond [(match_operand:DF 3 "is_mul" "")
- (const_string "fpmul")
- (match_operand:DF 3 "is_div" "")
- (const_string "fpdiv")
- ]
- (const_string "fpop")
- )
- )])
-
-(define_insn ""
- [(set (match_operand:XF 0 "register_operand" "=f,f")
- (match_operator:XF 3 "binary_387_op"
- [(float_extend:XF (match_operand:SF 1 "nonimmediate_operand" "fm,0"))
- (match_operand:XF 2 "register_operand" "0,f")]))]
- "TARGET_80387"
- "* return output_387_binary_op (insn, operands);"
- [(set (attr "type")
- (cond [(match_operand:DF 3 "is_mul" "")
- (const_string "fpmul")
- (match_operand:DF 3 "is_div" "")
- (const_string "fpdiv")
- ]
- (const_string "fpop")
- )
- )])
-
-(define_insn ""
- [(set (match_operand:XF 0 "register_operand" "=f,f")
- (match_operator:XF 3 "binary_387_op"
- [(match_operand:XF 1 "register_operand" "0,f")
- (float_extend:XF
- (match_operand:SF 2 "nonimmediate_operand" "fm,0"))]))]
- "TARGET_80387"
- "* return output_387_binary_op (insn, operands);"
- [(set (attr "type")
- (cond [(match_operand:DF 3 "is_mul" "")
- (const_string "fpmul")
- (match_operand:DF 3 "is_div" "")
- (const_string "fpdiv")
- ]
- (const_string "fpop")
- )
- )])
-
-(define_insn ""
- [(set (match_operand:DF 0 "register_operand" "=f,f")
- (match_operator:DF 3 "binary_387_op"
- [(float_extend:DF (match_operand:SF 1 "nonimmediate_operand" "fm,0"))
- (match_operand:DF 2 "register_operand" "0,f")]))]
- "TARGET_80387"
- "* return output_387_binary_op (insn, operands);"
- [(set (attr "type")
- (cond [(match_operand:DF 3 "is_mul" "")
- (const_string "fpmul")
- (match_operand:DF 3 "is_div" "")
- (const_string "fpdiv")
- ]
- (const_string "fpop")
- )
- )])
+ rtx out, addr, eoschar, align, scratch1, scratch2, scratch3;
-(define_insn ""
- [(set (match_operand:DF 0 "register_operand" "=f,f")
- (match_operator:DF 3 "binary_387_op"
- [(match_operand:DF 1 "register_operand" "0,f")
- (float_extend:DF
- (match_operand:SF 2 "nonimmediate_operand" "fm,0"))]))]
- "TARGET_80387"
- "* return output_387_binary_op (insn, operands);"
- [(set (attr "type")
- (cond [(match_operand:DF 3 "is_mul" "")
- (const_string "fpmul")
- (match_operand:DF 3 "is_div" "")
- (const_string "fpdiv")
- ]
- (const_string "fpop")
- )
- )])
+ out = operands[0];
+ addr = force_reg (Pmode, XEXP (operands[1], 0));
+ eoschar = operands[2];
+ align = operands[3];
+ scratch1 = gen_reg_rtx (SImode);
-(define_insn ""
- [(set (match_operand:SF 0 "register_operand" "=f,f")
- (match_operator:SF 3 "binary_387_op"
- [(match_operand:SF 1 "nonimmediate_operand" "0,fm")
- (match_operand:SF 2 "nonimmediate_operand" "fm,0")]))]
- "TARGET_80387"
- "* return output_387_binary_op (insn, operands);"
- [(set (attr "type")
- (cond [(match_operand:DF 3 "is_mul" "")
- (const_string "fpmul")
- (match_operand:DF 3 "is_div" "")
- (const_string "fpdiv")
- ]
- (const_string "fpop")
- )
- )])
-\f
-(define_expand "strlensi"
- [(parallel [(set (match_dup 4)
- (unspec:SI [(mem:BLK (match_operand:BLK 1 "general_operand" ""))
- (match_operand:QI 2 "immediate_operand" "")
- (match_operand:SI 3 "immediate_operand" "")] 0))
- (clobber (match_dup 1))])
- (set (match_dup 5)
- (not:SI (match_dup 4)))
- (set (match_operand:SI 0 "register_operand" "")
- (plus:SI (match_dup 5)
- (const_int -1)))]
- ""
- "
-{
- if (TARGET_UNROLL_STRLEN && operands[2] == const0_rtx && optimize > 1)
+ if (TARGET_UNROLL_STRLEN && eoschar == const0_rtx && optimize > 1)
{
- rtx address;
- rtx scratch;
-
- /* well it seems that some optimizer does not combine a call like
+ /* Well it seems that some optimizer does not combine a call like
foo(strlen(bar), strlen(bar));
- when the move and the subtraction is done here. It does calculate
- the length just once when these instructions are done inside of
- output_strlen_unroll(). But I think since &bar[strlen(bar)] is
- often used and I use one fewer register for the lifetime of
- output_strlen_unroll() this is better. */
- scratch = gen_reg_rtx (SImode);
- address = force_reg (SImode, XEXP (operands[1], 0));
-
- /* move address to scratch-register
- this is done here because the i586 can do the following and
- in the same cycle with the following move. */
- if (GET_CODE (operands[3]) != CONST_INT || INTVAL (operands[3]) < 4)
- emit_insn (gen_movsi (scratch, address));
-
- emit_insn (gen_movsi (operands[0], address));
-
- if(TARGET_USE_Q_REG)
- emit_insn (gen_strlensi_unroll5 (operands[0],
- operands[3],
- scratch,
- operands[0]));
- else
- emit_insn (gen_strlensi_unroll4 (operands[0],
- operands[3],
- scratch,
- operands[0]));
-
- /* gen_strlensi_unroll[45] returns the address of the zero
- at the end of the string, like memchr(), so compute the
- length by subtracting the startaddress. */
- emit_insn (gen_subsi3 (operands[0], operands[0], address));
- DONE;
+ when the move and the subtraction is done here. It does calculate
+ the length just once when these instructions are done inside of
+ output_strlen_unroll(). But I think since &bar[strlen(bar)] is
+ often used and I use one fewer register for the lifetime of
+ output_strlen_unroll() this is better. */
+
+ if (GET_CODE (align) != CONST_INT || INTVAL (align) < 4)
+ emit_move_insn (scratch1, addr);
+ emit_move_insn (out, addr);
+
+ ix86_expand_strlensi_unroll_1 (out, align, scratch1);
+
+ /* strlensi_unroll_1 returns the address of the zero at the end of
+ the string, like memchr(), so compute the length by subtracting
+ the start address. */
+ emit_insn (gen_subsi3 (out, out, addr));
}
+ else
+ {
+ scratch2 = gen_reg_rtx (SImode);
+ scratch3 = gen_reg_rtx (SImode);
- operands[1] = copy_to_mode_reg (SImode, XEXP (operands[1], 0));
- operands[4] = gen_reg_rtx (SImode);
- operands[5] = gen_reg_rtx (SImode);
+ emit_move_insn (scratch3, addr);
+
+ emit_insn (gen_strlensi_1 (scratch1, scratch3, eoschar,
+ align, constm1_rtx));
+ emit_insn (gen_one_cmplsi2 (scratch2, scratch1));
+ emit_insn (gen_addsi3 (out, scratch2, constm1_rtx));
+ }
+ DONE;
}")
;; It might seem that operands 0 & 1 could use predicate register_operand.
;; But strength reduction might offset the MEM expression. So we let
;; reload put the address into %edi.
-(define_insn ""
- [(set (match_operand:SI 0 "register_operand" "=&c")
- (unspec:SI [(mem:BLK (match_operand:SI 1 "address_operand" "D"))
- (match_operand:QI 2 "immediate_operand" "a")
- (match_operand:SI 3 "immediate_operand" "i")] 0))
- (clobber (match_dup 1))]
- ""
- "*
-{
- rtx xops[2];
-
- xops[0] = operands[0];
- xops[1] = constm1_rtx;
- output_asm_insn (\"cld\", operands);
- output_asm_insn (AS2 (mov%L0,%1,%0), xops);
- return \"repnz\;scas%B2\";
-}")
-
-/* Conditional move define_insns. */
-
-(define_expand "movsicc"
- [(set (match_operand:SI 0 "register_operand" "")
- (if_then_else:SI (match_operand 1 "comparison_operator" "")
- (match_operand:SI 2 "nonimmediate_operand" "")
- (match_operand:SI 3 "nonimmediate_operand" "")))]
- "TARGET_CMOVE"
- "
-{
- if (GET_MODE_CLASS (GET_MODE (i386_compare_op0)) != MODE_INT)
- FAIL;
-
- operands[1] = gen_rtx_fmt_ee (GET_CODE (operands[1]),
- GET_MODE (i386_compare_op0),
- i386_compare_op0, i386_compare_op1);
-}")
-
-(define_insn ""
- [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
- (if_then_else:SI (match_operator 1 "comparison_operator"
- [(match_operand:QI 2 "nonimmediate_operand" "q,m,q,m")
- (match_operand:QI 3 "general_operand" "qmn,qn,qmn,qn")])
- (match_operand:SI 4 "nonimmediate_operand" "rm,rm,0,0")
- (match_operand:SI 5 "nonimmediate_operand" "0,0,rm,rm")))]
- "TARGET_CMOVE"
- "#")
-
-(define_insn ""
- [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
- (if_then_else:SI (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "r,m,r,m")
- (match_operand 3 "general_operand" "rmi,ri,rmi,ri")])
- (match_operand:SI 4 "nonimmediate_operand" "rm,rm,0,0")
- (match_operand:SI 5 "nonimmediate_operand" "0,0,rm,rm")))]
- "TARGET_CMOVE && GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT"
- "#")
+(define_insn "strlensi_1"
+ [(set (match_operand:SI 0 "register_operand" "=&c")
+ (unspec:SI [(mem:BLK (match_operand:SI 1 "address_operand" "D"))
+ (match_operand:QI 2 "general_operand" "a")
+ (match_operand:SI 3 "immediate_operand" "i")
+ (match_operand:SI 4 "immediate_operand" "0")] 0))
+ (clobber (match_dup 1))
+ (clobber (reg:CC 17))]
+ ""
+ "cld\;repnz{\;| }scasb"
+ [(set_attr "type" "multi")
+ (set_attr "length" "3")])
+\f
+;; Conditional move instructions.
-(define_split
+(define_expand "movsicc"
[(set (match_operand:SI 0 "register_operand" "")
- (if_then_else:SI (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "")
- (const_int 0)])
- (match_operand:SI 3 "nonimmediate_operand" "")
- (match_operand:SI 4 "nonimmediate_operand" "")))]
- "TARGET_CMOVE && reload_completed"
- [(set (cc0)
- (match_dup 2))
- (set (match_dup 0)
- (if_then_else:SI (match_op_dup 1 [(cc0) (const_int 0)])
- (match_dup 3) (match_dup 4)))]
- "")
+ (if_then_else:SI (match_operand 1 "comparison_operator" "")
+ (match_operand:SI 2 "general_operand" "")
+ (match_operand:SI 3 "general_operand" "")))]
+ ""
+ "if (!ix86_expand_int_movcc (operands)) FAIL; DONE;")
-(define_split
- [(set (match_operand:SI 0 "register_operand" "")
- (if_then_else:SI (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "")
- (match_operand 3 "general_operand" "")])
- (match_operand:SI 4 "nonimmediate_operand" "")
- (match_operand:SI 5 "nonimmediate_operand" "")))]
- "TARGET_CMOVE && reload_completed"
- [(set (cc0) (compare (match_dup 2) (match_dup 3)))
- (set (match_dup 0)
- (if_then_else:SI (match_op_dup 1 [(cc0) (const_int 0)])
- (match_dup 4) (match_dup 5)))]
- "")
+;; Data flow gets confused by our desire for `sbbl reg,reg', and clearing
+;; the register first winds up with `sbbl $0,reg', which is also weird.
+;; So just document what we're doing explicitly.
+
+(define_insn "x86_movsicc_0_m1"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (if_then_else:SI (ltu (reg:CC 17) (const_int 0))
+ (const_int -1)
+ (const_int 0)))
+ (clobber (reg:CC 17))]
+ ""
+ "sbb{l}\\t%0, %0"
+ ; Since we don't have the proper number of operands for an alu insn,
+ ; fill in all the blanks.
+ [(set_attr "type" "alu")
+ (set_attr "memory" "none")
+ (set_attr "imm_disp" "false")
+ (set_attr "length" "2")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (if_then_else:SI (match_operator 1 "no_comparison_operator"
+ [(reg 17) (const_int 0)])
+ (match_operand:SI 2 "nonimmediate_operand" "rm,0")
+ (match_operand:SI 3 "nonimmediate_operand" "0,rm")))]
+ "TARGET_CMOVE"
+ "@
+ cmov%C1\\t{%2, %0|%0, %2}
+ cmov%c1\\t{%3, %0|%0, %3}"
+ [(set_attr "type" "icmov")])
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(if_then_else:SI (match_operator 1 "comparison_operator"
- [(cc0) (const_int 0)])
+ [(reg:CC 17) (const_int 0)])
(match_operand:SI 2 "nonimmediate_operand" "rm,0")
(match_operand:SI 3 "nonimmediate_operand" "0,rm")))]
- "TARGET_CMOVE && reload_completed"
- "* return output_int_conditional_move (which_alternative, operands);")
+ "TARGET_CMOVE"
+ "@
+ cmov%C1\\t{%2, %0|%0, %2}
+ cmov%c1\\t{%3, %0|%0, %3}"
+ [(set_attr "type" "icmov")])
(define_expand "movhicc"
[(set (match_operand:HI 0 "register_operand" "")
(match_operand:HI 2 "nonimmediate_operand" "")
(match_operand:HI 3 "nonimmediate_operand" "")))]
"TARGET_CMOVE"
- "
-{
- if (GET_MODE_CLASS (GET_MODE (i386_compare_op0)) != MODE_INT)
- FAIL;
-
- operands[1] = gen_rtx_fmt_ee (GET_CODE (operands[1]),
- GET_MODE (i386_compare_op0),
- i386_compare_op0, i386_compare_op1);
-}")
+ "if (!ix86_expand_int_movcc (operands)) FAIL; DONE;")
(define_insn ""
- [(set (match_operand:HI 0 "register_operand" "=r,r,r,r")
- (if_then_else:HI (match_operator 1 "comparison_operator"
- [(match_operand:QI 2 "nonimmediate_operand" "q,m,q,m")
- (match_operand:QI 3 "general_operand" "qmn,qn,qmn,qn")])
- (match_operand:HI 4 "nonimmediate_operand" "rm,rm,0,0")
- (match_operand:HI 5 "nonimmediate_operand" "0,0,rm,rm")))]
+ [(set (match_operand:HI 0 "register_operand" "=r,r")
+ (if_then_else:HI (match_operator 1 "no_comparison_operator"
+ [(reg 17) (const_int 0)])
+ (match_operand:HI 2 "nonimmediate_operand" "rm,0")
+ (match_operand:HI 3 "nonimmediate_operand" "0,rm")))]
"TARGET_CMOVE"
- "#")
-
-(define_insn ""
- [(set (match_operand:HI 0 "register_operand" "=r,r,r,r")
- (if_then_else:HI (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "r,m,r,m")
- (match_operand 3 "general_operand" "rmi,ri,rmi,ri")])
- (match_operand:HI 4 "nonimmediate_operand" "rm,rm,0,0")
- (match_operand:HI 5 "nonimmediate_operand" "0,0,rm,rm")))]
- "TARGET_CMOVE && GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT"
- "#")
-
-(define_split
- [(set (match_operand:HI 0 "register_operand" "")
- (if_then_else:HI (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "")
- (const_int 0)])
- (match_operand:HI 3 "nonimmediate_operand" "")
- (match_operand:HI 4 "nonimmediate_operand" "")))]
- "TARGET_CMOVE && reload_completed"
- [(set (cc0)
- (match_dup 2))
- (set (match_dup 0)
- (if_then_else:HI (match_op_dup 1 [(cc0) (const_int 0)])
- (match_dup 3) (match_dup 4)))]
- "")
-
-(define_split
- [(set (match_operand:HI 0 "register_operand" "")
- (if_then_else:HI (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "")
- (match_operand 3 "general_operand" "")])
- (match_operand:HI 4 "nonimmediate_operand" "")
- (match_operand:HI 5 "nonimmediate_operand" "")))]
- "TARGET_CMOVE && reload_completed"
- [(set (cc0)
- (compare (match_dup 2) (match_dup 3)))
- (set (match_dup 0)
- (if_then_else:HI (match_op_dup 1 [(cc0) (const_int 0)])
- (match_dup 4) (match_dup 5)))]
- "")
+ "@
+ cmov%C1\\t{%2, %0|%0, %2}
+ cmov%c1\\t{%3, %0|%0, %3}"
+ [(set_attr "type" "icmov")])
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r,r")
(if_then_else:HI (match_operator 1 "comparison_operator"
- [(cc0) (const_int 0)])
+ [(reg:CC 17) (const_int 0)])
(match_operand:HI 2 "nonimmediate_operand" "rm,0")
(match_operand:HI 3 "nonimmediate_operand" "0,rm")))]
- "TARGET_CMOVE && reload_completed"
- "* return output_int_conditional_move (which_alternative, operands);")
+ "TARGET_CMOVE"
+ "@
+ cmov%C1\\t{%2, %0|%0, %2}
+ cmov%c1\\t{%3, %0|%0, %3}"
+ [(set_attr "type" "icmov")])
+(define_split
+ [(set (match_operand:HI 0 "register_operand" "")
+ (if_then_else:HI (match_operator 1 "comparison_operator"
+ [(reg 17) (const_int 0)])
+ (match_operand:HI 2 "register_operand" "")
+ (match_operand:HI 3 "register_operand" "")))]
+ "! TARGET_PARTIAL_REG_STALL && TARGET_CMOVE"
+ [(set (match_dup 0)
+ (if_then_else:SI (match_dup 1) (match_dup 2) (match_dup 3)))]
+ "operands[0] = gen_lowpart (SImode, operands[0]);
+ operands[2] = gen_lowpart (SImode, operands[2]);
+ operands[3] = gen_lowpart (SImode, operands[3]);")
+
(define_expand "movsfcc"
[(set (match_operand:SF 0 "register_operand" "")
(if_then_else:SF (match_operand 1 "comparison_operator" "")
(match_operand:SF 2 "register_operand" "")
(match_operand:SF 3 "register_operand" "")))]
"TARGET_CMOVE"
- "
-{
- rtx temp;
-
- if (GET_MODE_CLASS (GET_MODE (i386_compare_op0)) != MODE_INT)
- FAIL;
-
- /* The floating point conditional move instructions don't directly
- support conditions resulting from a signed integer comparison. */
-
- switch (GET_CODE (operands[1]))
- {
- case LT:
- case LE:
- case GE:
- case GT:
- temp = emit_store_flag (gen_reg_rtx (QImode),
- GET_CODE (operands[1]), i386_compare_op0, i386_compare_op1,
- VOIDmode, 0, 0);
-
- if (!temp)
- FAIL;
-
- operands[1] = gen_rtx_fmt_ee (NE, QImode, temp, const0_rtx);
- break;
-
- default:
- operands[1] = gen_rtx_fmt_ee (GET_CODE (operands[1]),
- GET_MODE (i386_compare_op0),
- i386_compare_op0, i386_compare_op1);
- break;
- }
-}")
-
-(define_insn ""
- [(set (match_operand:SF 0 "register_operand" "=f,f,f,f")
- (if_then_else:SF (match_operator 1 "comparison_operator"
- [(match_operand:QI 2 "nonimmediate_operand" "q,m,q,m")
- (match_operand:QI 3 "general_operand" "qmn,qn,qmn,qn")])
- (match_operand:SF 4 "register_operand" "f,f,0,0")
- (match_operand:SF 5 "register_operand" "0,0,f,f")))]
- "TARGET_CMOVE
- && GET_CODE (operands[1]) != LT && GET_CODE (operands[1]) != LE
- && GET_CODE (operands[1]) != GE && GET_CODE (operands[1]) != GT"
- "#")
-
-(define_insn ""
- [(set (match_operand:SF 0 "register_operand" "=f,f,f,f")
- (if_then_else:SF (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "r,m,r,m")
- (match_operand 3 "general_operand" "rmi,ri,rmi,ri")])
- (match_operand:SF 4 "register_operand" "f,f,0,0")
- (match_operand:SF 5 "register_operand" "0,0,f,f")))]
- "TARGET_CMOVE && GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT
- && GET_CODE (operands[1]) != LT && GET_CODE (operands[1]) != LE
- && GET_CODE (operands[1]) != GE && GET_CODE (operands[1]) != GT"
- "#")
-
-(define_split
- [(set (match_operand:SF 0 "register_operand" "")
- (if_then_else:SF (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "")
- (const_int 0)])
- (match_operand:SF 3 "register_operand" "")
- (match_operand:SF 4 "register_operand" "")))]
- "TARGET_CMOVE && reload_completed"
- [(set (cc0)
- (match_dup 2))
- (set (match_dup 0)
- (if_then_else:SF (match_op_dup 1 [(cc0) (const_int 0)])
- (match_dup 3) (match_dup 4)))]
- "")
-
-(define_split
- [(set (match_operand:SF 0 "register_operand" "")
- (if_then_else:SF (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "")
- (match_operand 3 "general_operand" "")])
- (match_operand:SF 4 "register_operand" "")
- (match_operand:SF 5 "register_operand" "")))]
- "TARGET_CMOVE && reload_completed"
- [(set (cc0) (compare (match_dup 2) (match_dup 3)))
- (set (match_dup 0)
- (if_then_else:SF (match_op_dup 1 [(cc0) (const_int 0)])
- (match_dup 4) (match_dup 5)))]
- "")
+ "if (! ix86_expand_fp_movcc (operands)) FAIL; DONE;")
(define_insn ""
[(set (match_operand:SF 0 "register_operand" "=f,f")
- (if_then_else:SF (match_operator 1 "comparison_operator"
- [(cc0) (const_int 0)])
+ (if_then_else:SF (match_operator 1 "fcmov_comparison_operator"
+ [(reg 17) (const_int 0)])
(match_operand:SF 2 "register_operand" "f,0")
(match_operand:SF 3 "register_operand" "0,f")))]
- "TARGET_CMOVE && reload_completed"
- "* return output_fp_conditional_move (which_alternative, operands);")
+ "TARGET_CMOVE"
+ "@
+ fcmov%F1\\t{%2, %0|%0, %2}
+ fcmov%f1\\t{%3, %0|%0, %3}"
+ [(set_attr "type" "fcmov")])
(define_expand "movdfcc"
[(set (match_operand:DF 0 "register_operand" "")
(match_operand:DF 2 "register_operand" "")
(match_operand:DF 3 "register_operand" "")))]
"TARGET_CMOVE"
- "
-{
- rtx temp;
-
- if (GET_MODE_CLASS (GET_MODE (i386_compare_op0)) != MODE_INT)
- FAIL;
-
- /* The floating point conditional move instructions don't directly
- support conditions resulting from a signed integer comparison. */
-
- switch (GET_CODE (operands[1]))
- {
- case LT:
- case LE:
- case GE:
- case GT:
- temp = emit_store_flag (gen_reg_rtx (QImode),
- GET_CODE (operands[1]), i386_compare_op0, i386_compare_op1,
- VOIDmode, 0, 0);
-
- if (!temp)
- FAIL;
-
- operands[1] = gen_rtx_fmt_ee (NE, QImode, temp, const0_rtx);
- break;
-
- default:
- operands[1] = gen_rtx_fmt_ee (GET_CODE (operands[1]),
- GET_MODE (i386_compare_op0),
- i386_compare_op0, i386_compare_op1);
- break;
- }
-}")
-
-(define_insn ""
- [(set (match_operand:DF 0 "register_operand" "=f,f,f,f")
- (if_then_else:DF (match_operator 1 "comparison_operator"
- [(match_operand:QI 2 "nonimmediate_operand" "q,m,q,m")
- (match_operand:QI 3 "general_operand" "qmn,qn,qmn,qn")])
- (match_operand:DF 4 "register_operand" "f,f,0,0")
- (match_operand:DF 5 "register_operand" "0,0,f,f")))]
- "TARGET_CMOVE
- && GET_CODE (operands[1]) != LT && GET_CODE (operands[1]) != LE
- && GET_CODE (operands[1]) != GE && GET_CODE (operands[1]) != GT"
- "#")
-
-(define_insn ""
- [(set (match_operand:DF 0 "register_operand" "=f,f,f,f")
- (if_then_else:DF (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "r,m,r,m")
- (match_operand 3 "general_operand" "rmi,ri,rmi,ri")])
- (match_operand:DF 4 "register_operand" "f,f,0,0")
- (match_operand:DF 5 "register_operand" "0,0,f,f")))]
- "TARGET_CMOVE && GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT
- && GET_CODE (operands[1]) != LT && GET_CODE (operands[1]) != LE
- && GET_CODE (operands[1]) != GE && GET_CODE (operands[1]) != GT"
- "#")
-
-(define_split
- [(set (match_operand:DF 0 "register_operand" "")
- (if_then_else:DF (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "")
- (const_int 0)])
- (match_operand:DF 3 "register_operand" "")
- (match_operand:DF 4 "register_operand" "")))]
- "TARGET_CMOVE && reload_completed"
- [(set (cc0)
- (match_dup 2))
- (set (match_dup 0)
- (if_then_else:DF (match_op_dup 1 [(cc0) (const_int 0)])
- (match_dup 3) (match_dup 4)))]
- "")
-
-(define_split
- [(set (match_operand:DF 0 "register_operand" "")
- (if_then_else:DF (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "")
- (match_operand 3 "general_operand" "")])
- (match_operand:DF 4 "register_operand" "")
- (match_operand:DF 5 "register_operand" "")))]
- "TARGET_CMOVE && reload_completed"
- [(set (cc0) (compare (match_dup 2) (match_dup 3)))
- (set (match_dup 0)
- (if_then_else:DF (match_op_dup 1 [(cc0) (const_int 0)])
- (match_dup 4) (match_dup 5)))]
- "")
+ "if (! ix86_expand_fp_movcc (operands)) FAIL; DONE;")
(define_insn ""
[(set (match_operand:DF 0 "register_operand" "=f,f")
- (if_then_else:DF (match_operator 1 "comparison_operator"
- [(cc0) (const_int 0)])
+ (if_then_else:DF (match_operator 1 "fcmov_comparison_operator"
+ [(reg 17) (const_int 0)])
(match_operand:DF 2 "register_operand" "f,0")
(match_operand:DF 3 "register_operand" "0,f")))]
- "TARGET_CMOVE && reload_completed"
- "* return output_fp_conditional_move (which_alternative, operands);")
+ "TARGET_CMOVE"
+ "@
+ fcmov%F1\\t{%2, %0|%0, %2}
+ fcmov%f1\\t{%3, %0|%0, %3}"
+ [(set_attr "type" "fcmov")])
(define_expand "movxfcc"
[(set (match_operand:XF 0 "register_operand" "")
(match_operand:XF 2 "register_operand" "")
(match_operand:XF 3 "register_operand" "")))]
"TARGET_CMOVE"
- "
-{
- rtx temp;
-
- if (GET_MODE_CLASS (GET_MODE (i386_compare_op0)) != MODE_INT)
- FAIL;
-
- /* The floating point conditional move instructions don't directly
- support conditions resulting from a signed integer comparison. */
-
- switch (GET_CODE (operands[1]))
- {
- case LT:
- case LE:
- case GE:
- case GT:
- temp = emit_store_flag (gen_reg_rtx (QImode),
- GET_CODE (operands[1]), i386_compare_op0, i386_compare_op1,
- VOIDmode, 0, 0);
-
- if (!temp)
- FAIL;
-
- operands[1] = gen_rtx_fmt_ee (NE, QImode, temp, const0_rtx);
- break;
-
- default:
- operands[1] = gen_rtx_fmt_ee (GET_CODE (operands[1]),
- GET_MODE (i386_compare_op0),
- i386_compare_op0, i386_compare_op1);
- break;
- }
-}")
-
-(define_insn ""
- [(set (match_operand:XF 0 "register_operand" "=f,f,f,f")
- (if_then_else:XF (match_operator 1 "comparison_operator"
- [(match_operand:QI 2 "nonimmediate_operand" "q,m,q,m")
- (match_operand:QI 3 "general_operand" "qmn,qn,qmn,qn")])
- (match_operand:XF 4 "register_operand" "f,f,0,0")
- (match_operand:XF 5 "register_operand" "0,0,f,f")))]
- "TARGET_CMOVE
- && GET_CODE (operands[1]) != LT && GET_CODE (operands[1]) != LE
- && GET_CODE (operands[1]) != GE && GET_CODE (operands[1]) != GT"
- "#")
-
-(define_insn ""
- [(set (match_operand:XF 0 "register_operand" "=f,f,f,f")
- (if_then_else:XF (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "r,m,r,m")
- (match_operand 3 "general_operand" "rmi,ri,rmi,ri")])
- (match_operand:XF 4 "register_operand" "f,f,0,0")
- (match_operand:XF 5 "register_operand" "0,0,f,f")))]
- "TARGET_CMOVE && GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT
- && GET_CODE (operands[1]) != LT && GET_CODE (operands[1]) != LE
- && GET_CODE (operands[1]) != GE && GET_CODE (operands[1]) != GT"
- "#")
-
-(define_split
- [(set (match_operand:XF 0 "register_operand" "")
- (if_then_else:XF (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "")
- (const_int 0)])
- (match_operand:XF 3 "register_operand" "")
- (match_operand:XF 4 "register_operand" "")))]
- "TARGET_CMOVE && reload_completed"
- [(set (cc0)
- (match_dup 2))
- (set (match_dup 0)
- (if_then_else:XF (match_op_dup 1 [(cc0) (const_int 0)])
- (match_dup 3) (match_dup 4)))]
- "")
-
-(define_split
- [(set (match_operand:XF 0 "register_operand" "")
- (if_then_else:XF (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "")
- (match_operand 3 "general_operand" "")])
- (match_operand:XF 4 "register_operand" "")
- (match_operand:XF 5 "register_operand" "")))]
- "TARGET_CMOVE && reload_completed"
- [(set (cc0) (compare (match_dup 2) (match_dup 3)))
- (set (match_dup 0)
- (if_then_else:XF (match_op_dup 1 [(cc0) (const_int 0)])
- (match_dup 4) (match_dup 5)))]
- "")
+ "if (! ix86_expand_fp_movcc (operands)) FAIL; DONE;")
(define_insn ""
[(set (match_operand:XF 0 "register_operand" "=f,f")
- (if_then_else:XF (match_operator 1 "comparison_operator"
- [(cc0) (const_int 0)])
+ (if_then_else:XF (match_operator 1 "fcmov_comparison_operator"
+ [(reg 17) (const_int 0)])
(match_operand:XF 2 "register_operand" "f,0")
(match_operand:XF 3 "register_operand" "0,f")))]
- "TARGET_CMOVE && reload_completed"
- "* return output_fp_conditional_move (which_alternative, operands);")
-
-(define_expand "movdicc"
- [(set (match_operand:DI 0 "register_operand" "")
- (if_then_else:DI (match_operand 1 "comparison_operator" "")
- (match_operand:DI 2 "nonimmediate_operand" "")
- (match_operand:DI 3 "nonimmediate_operand" "")))]
- "TARGET_CMOVE"
- "
-{
- if (GET_MODE_CLASS (GET_MODE (i386_compare_op0)) != MODE_INT)
- FAIL;
-
- operands[1] = gen_rtx_fmt_ee (GET_CODE (operands[1]),
- GET_MODE (i386_compare_op0),
- i386_compare_op0, i386_compare_op1);
-}")
-
-(define_insn ""
- [(set (match_operand:DI 0 "register_operand" "=&r,&r,&r,&r")
- (if_then_else:DI (match_operator 1 "comparison_operator"
- [(match_operand:QI 2 "nonimmediate_operand" "q,m,q,m")
- (match_operand:QI 3 "general_operand" "qmn,qn,qmn,qn")])
- (match_operand:DI 4 "nonimmediate_operand" "ro,ro,0,0")
- (match_operand:DI 5 "nonimmediate_operand" "0,0,ro,ro")))]
"TARGET_CMOVE"
- "#")
-
-(define_insn ""
- [(set (match_operand:DI 0 "register_operand" "=&r,&r,&r,&r")
- (if_then_else:DI (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "r,m,r,m")
- (match_operand 3 "general_operand" "rmi,ri,rmi,ri")])
- (match_operand:DI 4 "nonimmediate_operand" "ro,ro,0,0")
- (match_operand:DI 5 "nonimmediate_operand" "0,0,ro,ro")))]
- "TARGET_CMOVE && GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT"
- "#")
+ "@
+ fcmov%F1\\t{%2, %0|%0, %2}
+ fcmov%f1\\t{%3, %0|%0, %3}"
+ [(set_attr "type" "fcmov")])
+\f
+;; Misc patterns (?)
-(define_split
- [(set (match_operand:DI 0 "register_operand" "")
- (if_then_else:DI (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "")
- (const_int 0)])
- (match_operand:DI 3 "nonimmediate_operand" "")
- (match_operand:DI 4 "nonimmediate_operand" "")))]
- "TARGET_CMOVE && reload_completed"
- [(set (cc0)
- (match_dup 2))
- (set (match_dup 5)
- (if_then_else:SI (match_op_dup 1 [(cc0) (const_int 0)])
- (match_dup 7) (match_dup 9)))
- (set (match_dup 6)
- (if_then_else:SI (match_op_dup 1 [(cc0) (const_int 0)])
- (match_dup 8) (match_dup 10)))]
- "split_di (&operands[0], 1, &operands[5], &operands[6]);
- split_di (&operands[3], 1, &operands[7], &operands[8]);
- split_di (&operands[4], 1, &operands[9], &operands[10]);")
+;; This pattern exists to put a dependancy on all ebp-based memory accesses.
+;; Otherwise there will be nothing to keep
+;;
+;; [(set (reg ebp) (reg esp))]
+;; [(set (reg esp) (plus (reg esp) (const_int -160000)))
+;; (clobber (eflags)]
+;; [(set (mem (plus (reg ebp) (const_int -160000))) (const_int 0))]
+;;
+;; in proper program order.
-(define_split
- [(set (match_operand:DI 0 "register_operand" "")
- (if_then_else:DI (match_operator 1 "comparison_operator"
- [(match_operand 2 "nonimmediate_operand" "")
- (match_operand 3 "general_operand" "")])
- (match_operand:DI 4 "nonimmediate_operand" "")
- (match_operand:DI 5 "nonimmediate_operand" "")))]
- "TARGET_CMOVE && reload_completed"
- [(set (cc0) (compare (match_dup 2) (match_dup 3)))
- (set (match_dup 6)
- (if_then_else:SI (match_op_dup 1 [(cc0) (const_int 0)])
- (match_dup 8) (match_dup 10)))
- (set (match_dup 7)
- (if_then_else:SI (match_op_dup 1 [(cc0) (const_int 0)])
- (match_dup 9) (match_dup 11)))]
- "split_di (&operands[0], 1, &operands[6], &operands[7]);
- split_di (&operands[4], 1, &operands[8], &operands[9]);
- split_di (&operands[5], 1, &operands[10], &operands[11]);")
-
-(define_insn "strlensi_unroll"
- [(set (match_operand:SI 0 "register_operand" "=&r,&r")
- (unspec:SI [(mem:BLK (match_operand:SI 1 "address_operand" "r,r"))
- (match_operand:SI 2 "immediate_operand" "i,i")] 0))
- (clobber (match_scratch:SI 3 "=&q,&r"))]
- "optimize > 1"
- "* return output_strlen_unroll (operands);")
-
-;; the only difference between the following patterns is the register preference
-;; on a pentium using a q-register saves one clock cycle per 4 characters
-
-(define_insn "strlensi_unroll4"
- [(set (match_operand:SI 0 "register_operand" "=r,r")
- (unspec:SI [(mem:BLK (match_operand:SI 3 "register_operand" "0,0"))
- (match_operand:SI 1 "immediate_operand" "i,i")
- (match_operand:SI 2 "register_operand" "+q,!r")] 0))
- (clobber (match_dup 2))]
- "(TARGET_USE_ANY_REG && optimize > 1)"
- "* return output_strlen_unroll (operands);")
+(define_insn "prologue_allocate_stack"
+ [(set (match_operand:SI 0 "register_operand" "r")
+ (plus:SI (match_operand:SI 1 "register_operand" "0")
+ (match_operand:SI 2 "nonmemory_operand" "ri")))
+ (set (match_operand:SI 3 "register_operand" "r")
+ (match_dup 3))
+ (clobber (reg:CC 17))]
+ ""
+ "*
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+ && (INTVAL (operands[2]) == 128
+ || (INTVAL (operands[2]) < 0
+ && INTVAL (operands[2]) != -128)))
+ {
+ operands[2] = GEN_INT (-INTVAL (operands[2]));
+ return \"sub{l}\\t{%2, %0|%0, %2}\";
+ }
+ return \"add{l}\\t{%2, %0|%0, %2}\";
+}"
+ [(set_attr "type" "alu")])
-(define_insn "strlensi_unroll5"
- [(set (match_operand:SI 0 "register_operand" "=r")
- (unspec:SI [(mem:BLK (match_operand:SI 3 "register_operand" "0"))
- (match_operand:SI 1 "immediate_operand" "i")
- (match_operand:SI 2 "register_operand" "+q")] 0))
- (clobber (match_dup 2))]
- "(TARGET_USE_Q_REG && optimize > 1)"
- "* return output_strlen_unroll (operands);"
-)
+(define_insn "epilogue_deallocate_stack"
+ [(set (match_operand:SI 0 "register_operand" "r")
+ (match_operand:SI 1 "register_operand" "r"))
+ (set (match_dup 1) (match_dup 1))]
+ ""
+ "mov{l}\\t{%1, %0|%0, %1}"
+ [(set_attr "type" "imov")])
(define_insn "allocate_stack_worker"
[(unspec:SI [(match_operand:SI 0 "register_operand" "a")] 3)
(set (reg:SI 7) (minus:SI (reg:SI 7) (match_dup 0)))
- (clobber (match_dup 0))]
+ (clobber (match_dup 0))
+ (clobber (reg:CC 17))]
"TARGET_STACK_PROBE"
- "* return AS1(call,__alloca);"
- [(set_attr "memory" "none")])
+ "call\\t__alloca"
+ [(set_attr "type" "multi")
+ (set_attr "length" "5")])
(define_expand "allocate_stack"
- [(set (match_operand:SI 0 "register_operand" "=r")
- (minus:SI (reg:SI 7) (match_operand:SI 1 "general_operand" "")))
- (set (reg:SI 7) (minus:SI (reg:SI 7) (match_dup 1)))]
- "TARGET_STACK_PROBE"
+ [(parallel [(set (match_operand:SI 0 "register_operand" "=r")
+ (minus:SI (reg:SI 7)
+ (match_operand:SI 1 "general_operand" "")))
+ (clobber (reg:CC 17))])
+ (parallel [(set (reg:SI 7)
+ (minus:SI (reg:SI 7) (match_dup 1)))
+ (clobber (reg:CC 17))])]
+ "TARGET_STACK_PROBE"
"
{
#ifdef CHECK_STACK_LIMIT
load_pic_register (1);
DONE;
}")
+\f
+;; RTL Peephole optimizations, run before sched2. These primarily look to
+;; transform a complex memory operation into two memory to register operations.
+
+;; Don't push memory operands
+(define_peephole2
+ [(match_scratch:SI 2 "r")
+ (set (match_operand:SI 0 "push_operand" "")
+ (match_operand:SI 1 "memory_operand" ""))]
+ "! optimize_size && ! TARGET_PUSH_MEMORY"
+ [(set (match_dup 2) (match_dup 1))
+ (set (match_dup 0) (match_dup 2))]
+ "")
+
+(define_peephole2
+ [(match_scratch:HI 2 "r")
+ (set (match_operand:HI 0 "push_operand" "")
+ (match_operand:HI 1 "memory_operand" ""))]
+ "! optimize_size && ! TARGET_PUSH_MEMORY"
+ [(set (match_dup 2) (match_dup 1))
+ (set (match_dup 0) (match_dup 2))]
+ "")
+
+(define_peephole2
+ [(match_scratch:QI 2 "q")
+ (set (match_operand:QI 0 "push_operand" "")
+ (match_operand:QI 1 "memory_operand" ""))]
+ "! optimize_size && ! TARGET_PUSH_MEMORY"
+ [(set (match_dup 2) (match_dup 1))
+ (set (match_dup 0) (match_dup 2))]
+ "")
+
+;; Don't move an immediate directly to memory when the instruction
+;; gets too big.
+(define_peephole2
+ [(match_scratch:SI 1 "r")
+ (set (match_operand:SI 0 "memory_operand" "")
+ (const_int 0))]
+ "! optimize_size && get_attr_length (insn) >= ix86_cost->large_insn
+ && reg_dead_p (insn, gen_rtx_REG (CCmode, FLAGS_REG))
+ && TARGET_SPLIT_LONG_MOVES"
+ [(parallel [(set (match_dup 1) (const_int 0))
+ (clobber (reg:CC 17))])
+ (set (match_dup 0) (match_dup 1))]
+ "")
+
+(define_peephole2
+ [(match_scratch:HI 1 "r")
+ (set (match_operand:HI 0 "memory_operand" "")
+ (const_int 0))]
+ "! optimize_size && get_attr_length (insn) >= ix86_cost->large_insn
+ && reg_dead_p (insn, gen_rtx_REG (CCmode, FLAGS_REG))
+ && TARGET_SPLIT_LONG_MOVES"
+ [(parallel [(set (match_dup 1) (const_int 0))
+ (clobber (reg:CC 17))])
+ (set (match_dup 0) (match_dup 1))]
+ "")
+
+(define_peephole2
+ [(match_scratch:QI 1 "q")
+ (set (match_operand:QI 0 "memory_operand" "")
+ (const_int 0))]
+ "! optimize_size && get_attr_length (insn) >= ix86_cost->large_insn
+ && reg_dead_p (insn, gen_rtx_REG (CCmode, FLAGS_REG))
+ && TARGET_SPLIT_LONG_MOVES"
+ [(parallel [(set (match_dup 1) (const_int 0))
+ (clobber (reg:CC 17))])
+ (set (match_dup 0) (match_dup 1))]
+ "")
+
+(define_peephole2
+ [(match_scratch:SI 2 "r")
+ (set (match_operand:SI 0 "memory_operand" "")
+ (match_operand:SI 1 "immediate_operand" ""))]
+ "! optimize_size && get_attr_length (insn) >= ix86_cost->large_insn
+ && TARGET_SPLIT_LONG_MOVES"
+ [(set (match_dup 2) (match_dup 1))
+ (set (match_dup 0) (match_dup 2))]
+ "")
+
+(define_peephole2
+ [(match_scratch:HI 2 "r")
+ (set (match_operand:HI 0 "memory_operand" "")
+ (match_operand:HI 1 "immediate_operand" ""))]
+ "! optimize_size && get_attr_length (insn) >= ix86_cost->large_insn
+ && TARGET_SPLIT_LONG_MOVES"
+ [(set (match_dup 2) (match_dup 1))
+ (set (match_dup 0) (match_dup 2))]
+ "")
+
+(define_peephole2
+ [(match_scratch:QI 2 "q")
+ (set (match_operand:QI 0 "memory_operand" "")
+ (match_operand:QI 1 "immediate_operand" ""))]
+ "! optimize_size && get_attr_length (insn) >= ix86_cost->large_insn
+ && TARGET_SPLIT_LONG_MOVES"
+ [(set (match_dup 2) (match_dup 1))
+ (set (match_dup 0) (match_dup 2))]
+ "")
+
+;; Don't compare memory with zero, load and use a test instead.
+(define_peephole2
+ [(match_scratch:SI 3 "r")
+ (set (reg:CCNO 17)
+ (compare:CCNO (match_operand:SI 0 "memory_operand" "")
+ (const_int 0)))]
+ "! optimize_size"
+ [(set (match_dup 3) (match_dup 0))
+ (set (reg:CCNO 17) (compare:CCNO (match_dup 3) (const_int 0)))]
+ "")
+
+;; NOT is not pairable on Pentium, while XOR is, but one byte longer.
+;; Don't split NOTs with a displacement operand, because resulting XOR
+;; will not be pariable anyway.
+;;
+;; On AMD K6, NOT is vector decoded with memory operand that can not be
+;; represented using a modRM byte. The XOR replacement is long decoded,
+;; so this split helps here as well.
+;;
+;; Note: Can't do this as a regular split because reg_dead_p assumes
+;; resource info is live.
+
+(define_peephole2
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
+ (not:SI (match_operand:SI 1 "nonimmediate_operand" "0")))]
+ "!optimize_size
+ && reg_dead_p (insn, gen_rtx_REG (CCmode, FLAGS_REG))
+ && ((TARGET_PENTIUM
+ && (GET_CODE (operands[0]) != MEM
+ || !memory_displacement_operand (operands[0], SImode)))
+ || (TARGET_K6 && long_memory_operand (operands[0], SImode)))"
+ [(parallel [(set (match_dup 0)
+ (xor:SI (match_dup 1) (const_int -1)))
+ (clobber (reg:CC 17))])]
+ "")
+
+(define_peephole2
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=rm")
+ (not:HI (match_operand:HI 1 "nonimmediate_operand" "0")))]
+ "!optimize_size
+ && reg_dead_p (insn, gen_rtx_REG (CCmode, FLAGS_REG))
+ && ((TARGET_PENTIUM
+ && (GET_CODE (operands[0]) != MEM
+ || !memory_displacement_operand (operands[0], HImode)))
+ || (TARGET_K6 && long_memory_operand (operands[0], HImode)))"
+ [(parallel [(set (match_dup 0)
+ (xor:HI (match_dup 1) (const_int -1)))
+ (clobber (reg:CC 17))])]
+ "")
+
+(define_peephole2
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=rm")
+ (not:QI (match_operand:QI 1 "nonimmediate_operand" "0")))]
+ "!optimize_size
+ && reg_dead_p (insn, gen_rtx_REG (CCmode, FLAGS_REG))
+ && ((TARGET_PENTIUM
+ && (GET_CODE (operands[0]) != MEM
+ || !memory_displacement_operand (operands[0], QImode)))
+ || (TARGET_K6 && long_memory_operand (operands[0], QImode)))"
+ [(parallel [(set (match_dup 0)
+ (xor:QI (match_dup 1) (const_int -1)))
+ (clobber (reg:CC 17))])]
+ "")
+
+;; Non pairable "test imm, reg" instructions can be translated to
+;; "and imm, reg" if reg dies. The "and" form is also shorter (one
+;; byte opcode instead of two, have a short form for byte operands),
+;; so do it for other CPUs as well. Given that the value was dead,
+;; this should not create any new dependancies. Pass on the sub-word
+;; versions if we're concerned about partial register stalls.
+
+(define_peephole2
+ [(set (reg:CCNO 17)
+ (compare:CCNO (and:SI (match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "immediate_operand" ""))
+ (const_int 0)))]
+ "(true_regnum (operands[0]) != 0
+ || CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'K'))
+ && find_regno_note (insn, REG_DEAD, true_regnum (operands[0]))"
+ [(parallel
+ [(set (reg:CCNO 17)
+ (compare:CCNO (and:SI (match_dup 0)
+ (match_dup 1))
+ (const_int 0)))
+ (set (match_dup 0)
+ (and:SI (match_dup 0) (match_dup 1)))])]
+ "")
+
+(define_peephole2
+ [(set (reg:CCNO 17)
+ (compare:CCNO (and:HI (match_operand:HI 0 "register_operand" "")
+ (match_operand:HI 1 "immediate_operand" ""))
+ (const_int 0)))]
+ "! TARGET_PARTIAL_REG_STALL
+ && (true_regnum (operands[0]) != 0
+ || CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'K'))
+ && find_regno_note (insn, REG_DEAD, true_regnum (operands[0]))"
+ [(parallel
+ [(set (reg:CCNO 17)
+ (compare:CCNO (and:HI (match_dup 0)
+ (match_dup 1))
+ (const_int 0)))
+ (set (match_dup 0)
+ (and:HI (match_dup 0) (match_dup 1)))])]
+ "")
+
+(define_peephole2
+ [(set (reg:CCNO 17)
+ (compare:CCNO (and:QI (match_operand:QI 0 "register_operand" "")
+ (match_operand:QI 1 "immediate_operand" ""))
+ (const_int 0)))]
+ "! TARGET_PARTIAL_REG_STALL
+ && true_regnum (operands[0]) != 0
+ && find_regno_note (insn, REG_DEAD, true_regnum (operands[0]))"
+ [(parallel
+ [(set (reg:CCNO 17)
+ (compare:CCNO (and:QI (match_dup 0)
+ (match_dup 1))
+ (const_int 0)))
+ (set (match_dup 0)
+ (and:QI (match_dup 0) (match_dup 1)))])]
+ "")
+
+(define_peephole2
+ [(set (reg:CCNO 17)
+ (compare:CCNO
+ (and:SI
+ (zero_extract:SI
+ (match_operand 0 "ext_register_operand" "q")
+ (const_int 8)
+ (const_int 8))
+ (match_operand 1 "const_int_operand" "n"))
+ (const_int 0)))]
+ "! TARGET_PARTIAL_REG_STALL
+ && true_regnum (operands[0]) != 0
+ && find_regno_note (insn, REG_DEAD, true_regnum (operands[0]))"
+ [(parallel [(set (reg:CCNO 17)
+ (compare:CCNO
+ (and:SI
+ (zero_extract:SI
+ (match_dup 0)
+ (const_int 8)
+ (const_int 8))
+ (match_dup 1))
+ (const_int 0)))
+ (set (zero_extract:SI (match_dup 0)
+ (const_int 8)
+ (const_int 8))
+ (and:SI
+ (zero_extract:SI
+ (match_dup 0)
+ (const_int 8)
+ (const_int 8))
+ (match_dup 1)))])]
+ "")
+
+;; Don't do logical operations with memory inputs.
+(define_peephole2
+ [(match_scratch:SI 2 "r")
+ (parallel [(set (match_operand:SI 0 "register_operand" "")
+ (match_operator:SI 3 "arith_or_logical_operator"
+ [(match_dup 0)
+ (match_operand:SI 1 "memory_operand" "")]))
+ (clobber (reg:CC 17))])]
+ "! optimize_size && ! TARGET_READ_MODIFY"
+ [(set (match_dup 2) (match_dup 1))
+ (parallel [(set (match_dup 0)
+ (match_op_dup 3 [(match_dup 0) (match_dup 2)]))
+ (clobber (reg:CC 17))])]
+ "")
+
+(define_peephole2
+ [(match_scratch:SI 2 "r")
+ (parallel [(set (match_operand:SI 0 "register_operand" "")
+ (match_operator:SI 3 "arith_or_logical_operator"
+ [(match_operand:SI 1 "memory_operand" "")
+ (match_dup 0)]))
+ (clobber (reg:CC 17))])]
+ "! optimize_size && ! TARGET_READ_MODIFY"
+ [(set (match_dup 2) (match_dup 1))
+ (parallel [(set (match_dup 0)
+ (match_op_dup 3 [(match_dup 2) (match_dup 0)]))
+ (clobber (reg:CC 17))])]
+ "")
+
+; Don't do logical operations with memory outputs
+;
+; These two don't make sense for PPro/PII -- we're expanding a 4-uop
+; instruction into two 1-uop insns plus a 2-uop insn. That last has
+; the same decoder scheduling characteristics as the original.
+
+(define_peephole2
+ [(match_scratch:SI 2 "r")
+ (parallel [(set (match_operand:SI 0 "memory_operand" "")
+ (match_operator:SI 3 "arith_or_logical_operator"
+ [(match_dup 0)
+ (match_operand:SI 1 "nonmemory_operand" "")]))
+ (clobber (reg:CC 17))])]
+ "! optimize_size && ! TARGET_READ_MODIFY_WRITE"
+ [(set (match_dup 2) (match_dup 0))
+ (parallel [(set (match_dup 2)
+ (match_op_dup 3 [(match_dup 2) (match_dup 1)]))
+ (clobber (reg:CC 17))])
+ (set (match_dup 0) (match_dup 2))]
+ "")
+
+(define_peephole2
+ [(match_scratch:SI 2 "r")
+ (parallel [(set (match_operand:SI 0 "memory_operand" "")
+ (match_operator:SI 3 "arith_or_logical_operator"
+ [(match_operand:SI 1 "nonmemory_operand" "")
+ (match_dup 0)]))
+ (clobber (reg:CC 17))])]
+ "! optimize_size && ! TARGET_READ_MODIFY_WRITE"
+ [(set (match_dup 2) (match_dup 0))
+ (parallel [(set (match_dup 2)
+ (match_op_dup 3 [(match_dup 1) (match_dup 2)]))
+ (clobber (reg:CC 17))])
+ (set (match_dup 0) (match_dup 2))]
+ "")
+
+;; Attempt to always use XOR for zeroing registers.
+(define_peephole2
+ [(set (match_operand 0 "register_operand" "")
+ (const_int 0))]
+ "(GET_MODE (operands[0]) == QImode
+ || GET_MODE (operands[0]) == HImode
+ || GET_MODE (operands[0]) == SImode)
+ && (! TARGET_USE_MOV0 || optimize_size)
+ && reg_dead_p (insn, gen_rtx_REG (CCmode, FLAGS_REG))"
+ [(parallel [(set (match_dup 0) (const_int 0))
+ (clobber (reg:CC 17))])]
+ "")
+
+;; For HI and SI modes, or $-1,reg is smaller than mov $-1,reg.
+(define_peephole2
+ [(set (match_operand:SI 0 "register_operand" "")
+ (const_int -1))]
+ "optimize_size && reg_dead_p (insn, gen_rtx_REG (CCmode, FLAGS_REG))"
+ [(parallel [(set (match_dup 0)
+ (ior:SI (match_dup 0) (const_int -1)))
+ (clobber (reg:CC 17))])]
+ "")
+
+(define_peephole2
+ [(set (match_operand:HI 0 "register_operand" "")
+ (const_int -1))]
+ "optimize_size && reg_dead_p (insn, gen_rtx_REG (CCmode, FLAGS_REG))"
+ [(parallel [(set (match_dup 0)
+ (ior:HI (match_dup 0) (const_int -1)))
+ (clobber (reg:CC 17))])]
+ "")