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;
+const int x86_double_with_add = ~(m_386 | m_PENT | m_PPRO);
const int x86_use_bit_test = m_386;
const int x86_unroll_strlen = m_486 | m_PENT | m_PPRO;
const int x86_use_q_reg = m_PENT | m_PPRO | m_K6;
return 0;
}
\f
-/* Output an insn whose source is a 386 integer register. SRC is the
- rtx for the register, and TEMPLATE is the op-code template. SRC may
- be either SImode or DImode.
-
- The template will be output with operands[0] as SRC, and operands[1]
- as a pointer to the top of the 386 stack. So a call from floatsidf2
- would look like this:
-
- output_op_from_reg (operands[1], AS1 (fild%z0,%1));
-
- where %z0 corresponds to the caller's operands[1], and is used to
- emit the proper size suffix.
-
- ??? Extend this to handle HImode - a 387 can load and store HImode
- values directly. */
-
-void
-output_op_from_reg (src, template)
- rtx src;
- char *template;
-{
- rtx xops[4];
- int size = GET_MODE_SIZE (GET_MODE (src));
-
- xops[0] = src;
- xops[1] = AT_SP (Pmode);
- xops[2] = GEN_INT (size);
- xops[3] = stack_pointer_rtx;
-
- if (size > UNITS_PER_WORD)
- {
- rtx high;
-
- if (size > 2 * UNITS_PER_WORD)
- {
- high = gen_rtx_REG (SImode, REGNO (src) + 2);
- output_asm_insn (AS1 (push%L0,%0), &high);
- }
-
- high = gen_rtx_REG (SImode, REGNO (src) + 1);
- output_asm_insn (AS1 (push%L0,%0), &high);
- }
-
- output_asm_insn (AS1 (push%L0,%0), &src);
- output_asm_insn (template, xops);
- output_asm_insn (AS2 (add%L3,%2,%3), xops);
-}
-\f
/* Output an insn to pop an value from the 387 top-of-stack to 386
register DEST. The 387 register stack is popped if DIES is true. If
the mode of DEST is an integer mode, a `fist' integer store is done,
return "";
}
\f
-/* Output the appropriate code to move data between two memory locations */
-
-char *
-output_move_memory (operands, insn, length, tmp_start, n_operands)
- rtx operands[];
- rtx insn;
- int length;
- int tmp_start;
- int n_operands;
-{
- struct
- {
- char *load;
- char *store;
- rtx xops[3];
- } tmp_info[MAX_TMPS];
-
- rtx dest = operands[0];
- rtx src = operands[1];
- rtx qi_tmp = NULL_RTX;
- int max_tmps = 0;
- int offset = 0;
- int i, num_tmps;
- rtx xops[3];
-
- if (GET_CODE (dest) == MEM
- && GET_CODE (XEXP (dest, 0)) == PRE_INC
- && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx)
- return output_move_pushmem (operands, insn, length, tmp_start, n_operands);
-
- if (! offsettable_memref_p (src))
- fatal_insn ("Source is not offsettable", insn);
-
- if (! offsettable_memref_p (dest))
- fatal_insn ("Destination is not offsettable", insn);
-
- /* Figure out which temporary registers we have available */
- for (i = tmp_start; i < n_operands; i++)
- {
- if (GET_CODE (operands[i]) == REG)
- {
- if ((length & 1) != 0 && qi_tmp == 0 && QI_REG_P (operands[i]))
- qi_tmp = operands[i];
-
- if (reg_overlap_mentioned_p (operands[i], dest))
- fatal_insn ("Temporary register overlaps the destination", insn);
-
- if (reg_overlap_mentioned_p (operands[i], src))
- fatal_insn ("Temporary register overlaps the source", insn);
-
- tmp_info[max_tmps++].xops[2] = operands[i];
- if (max_tmps == MAX_TMPS)
- break;
- }
- }
-
- if (max_tmps == 0)
- fatal_insn ("No scratch registers were found to do memory->memory moves",
- insn);
-
- if ((length & 1) != 0)
- {
- if (qi_tmp == 0)
- fatal_insn ("No byte register found when moving odd # of bytes.",
- insn);
- }
-
- while (length > 1)
- {
- for (num_tmps = 0; num_tmps < max_tmps; num_tmps++)
- {
- if (length >= 4)
- {
- tmp_info[num_tmps].load = AS2(mov%L0,%1,%2);
- tmp_info[num_tmps].store = AS2(mov%L0,%2,%0);
- tmp_info[num_tmps].xops[0]
- = adj_offsettable_operand (dest, offset);
- tmp_info[num_tmps].xops[1]
- = adj_offsettable_operand (src, offset);
-
- offset += 4;
- length -= 4;
- }
-
- else if (length >= 2)
- {
- tmp_info[num_tmps].load = AS2(mov%W0,%1,%2);
- tmp_info[num_tmps].store = AS2(mov%W0,%2,%0);
- tmp_info[num_tmps].xops[0]
- = adj_offsettable_operand (dest, offset);
- tmp_info[num_tmps].xops[1]
- = adj_offsettable_operand (src, offset);
-
- offset += 2;
- length -= 2;
- }
- else
- break;
- }
-
- for (i = 0; i < num_tmps; i++)
- output_asm_insn (tmp_info[i].load, tmp_info[i].xops);
-
- for (i = 0; i < num_tmps; i++)
- output_asm_insn (tmp_info[i].store, tmp_info[i].xops);
- }
-
- if (length == 1)
- {
- xops[0] = adj_offsettable_operand (dest, offset);
- xops[1] = adj_offsettable_operand (src, offset);
- xops[2] = qi_tmp;
- output_asm_insn (AS2(mov%B0,%1,%2), xops);
- output_asm_insn (AS2(mov%B0,%2,%0), xops);
- }
-
- return "";
-}
-\f
int
standard_80387_constant_p (x)
rtx x;
}
}
+/* Return nonzero if OP is a constant shift count small enough to
+ encode into an lea instruction. */
+
+int
+small_shift_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return (GET_CODE (op) == CONST_INT && INTVAL (op) > 0 && INTVAL (op) < 4);
+}
+
/* 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
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);
ireg = XEXP (addr, 0);
}
- output_addr_const (file, const0_rtx);
- PRINT_B_I_S (NULL_RTX, ireg, scale, file);
+ /* (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;
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
- && GET_MODE_CLASS (GET_MODE (SET_DEST (exp))) == MODE_INT)))
+ || GET_CODE (SET_SRC (exp)) == IF_THEN_ELSE))
{
if (cc_status.value1
&& reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1))
switch (GET_CODE (operands[3]))
{
case PLUS:
- if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
- || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
- base_op = "fiadd";
- else
- base_op = "fadd";
+ base_op = "fadd";
break;
case MINUS:
- if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
- || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
- base_op = "fisub";
- else
- base_op = "fsub";
+ base_op = "fsub";
break;
case MULT:
- if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
- || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
- base_op = "fimul";
- else
- base_op = "fmul";
+ base_op = "fmul";
break;
case DIV:
- if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
- || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
- base_op = "fidiv";
- else
- base_op = "fdiv";
+ base_op = "fdiv";
break;
default:
if (GET_CODE (operands[2]) == MEM)
return strcat (buf, AS1 (%z2,%2));
- if (NON_STACK_REG_P (operands[1]))
- {
- output_op_from_reg (operands[1], strcat (buf, AS1 (%z0,%1)));
- return "";
- }
-
- else if (NON_STACK_REG_P (operands[2]))
- {
- output_op_from_reg (operands[2], strcat (buf, AS1 (%z0,%1)));
- return "";
- }
+ if (! STACK_REG_P (operands[1]) || ! STACK_REG_P (operands[2]))
+ abort ();
if (find_regno_note (insn, REG_DEAD, REGNO (operands[2])))
{
if (GET_CODE (operands[2]) == MEM)
return strcat (buf, AS1 (%z2,%2));
- if (NON_STACK_REG_P (operands[1]))
- {
- output_op_from_reg (operands[1], strcat (buf, AS1 (r%z0,%1)));
- return "";
- }
-
- else if (NON_STACK_REG_P (operands[2]))
- {
- output_op_from_reg (operands[2], strcat (buf, AS1 (%z0,%1)));
- return "";
- }
-
if (! STACK_REG_P (operands[1]) || ! STACK_REG_P (operands[2]))
abort ();
return AS1 (fldc%W2,%2);
}
\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)
+ rtx insn;
+ rtx *operands;
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+ rtx xops[2];
+
+ if (! STACK_TOP_P (operands[0]) && ! STACK_TOP_P (operands[1]))
+ abort ();
+
+ if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1]) && stack_top_dies)
+ return;
+
+ if (STACK_TOP_P (operands[0]) )
+ {
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ if (GET_MODE (operands[1]) == SFmode)
+ output_asm_insn (AS2 (mov%L0,%1,%2), operands);
+ else
+ {
+ xops[0] = operands[2];
+ xops[1] = operands[1];
+ output_asm_insn (output_move_double (xops), xops);
+ }
+ }
+
+ 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];
+
+ 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);
+
+ if (NON_STACK_REG_P (operands[0]))
+ {
+ xops[0] = operands[0];
+ xops[1] = operands[3];
+ output_asm_insn (output_move_double (xops), xops);
+ }
+ }
+}
+\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
{
static char buf[100];
- /* Decide if this is the integer or float compare opcode, or the
- unordered float compare. */
+ /* Decide if this is a float compare or an unordered float compare. */
if (unordered_compare)
strcpy (buf, (cc_status.flags & CC_FCOMI) ? "fucomi" : "fucom");
- else if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_FLOAT)
- strcpy (buf, (cc_status.flags & CC_FCOMI) ? "fcomi" : "fcom");
else
- strcpy (buf, "ficom");
+ strcpy (buf, (cc_status.flags & CC_FCOMI) ? "fcomi" : "fcom");
/* Modify the opcode if the 387 stack is to be popped. */
if (stack_top_dies)
strcat (buf, "p");
- if (NON_STACK_REG_P (operands[1]))
- output_op_from_reg (operands[1], strcat (buf, AS1 (%z0,%1)));
- else if (cc_status.flags & CC_FCOMI)
+ if (cc_status.flags & CC_FCOMI)
{
output_asm_insn (strcat (buf, AS2 (%z1,%y1,%0)), operands);
return "";
agi_dependent (insn, dep_insn)
rtx insn, dep_insn;
{
+ 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)
- return reg_mentioned_in_mem (SET_DEST (PATTERN (dep_insn)), insn);
+ && GET_CODE (SET_DEST (PATTERN (dep_insn))) == REG
+ && reg_mentioned_in_mem (SET_DEST (PATTERN (dep_insn)), insn))
+ return 1;
+
+ 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 (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)))))
- return reg_mentioned_in_mem (stack_pointer_rtx, insn);
+ push_dep = 1;
+
+ /* CPUs contain special hardware to allow two pushes. */
+ if (push && push_dep)
+ return 0;
+
+ /* Push operation implicitly change stack pointer causing AGI stalls. */
+ if (push_dep && reg_mentioned_in_mem (stack_pointer_rtx, insn))
+ return 1;
+
+ /* Push also implicitly read stack pointer. */
+ if (push && modified_in_p (stack_pointer_rtx, dep_insn))
+ return 1;
return 0;
}
int which_alternative;
rtx operands[];
{
+ 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)
{
case 0:
int which_alternative;
rtx operands[];
{
- int code = GET_CODE (operands[1]);
- enum machine_mode mode;
- rtx xops[4];
+ enum rtx_code code = GET_CODE (operands[1]);
/* This is very tricky. We have to do it right. For a code segement
like:
&& (cc_prev_status.flags & CC_NO_OVERFLOW))
return NULL_PTR;
- mode = GET_MODE (operands [0]);
- if (mode == DImode)
- {
- xops [0] = gen_rtx_SUBREG (SImode, operands [0], 1);
- xops [1] = operands [1];
- xops [2] = gen_rtx_SUBREG (SImode, operands [2], 1);
- xops [3] = gen_rtx_SUBREG (SImode, operands [3], 1);
- }
-
switch (which_alternative)
{
case 0:
/* r <- cond ? arg : r */
output_asm_insn (AS2 (cmov%C1,%2,%0), operands);
- if (mode == DImode)
- output_asm_insn (AS2 (cmov%C1,%2,%0), xops);
break;
case 1:
/* r <- cond ? r : arg */
output_asm_insn (AS2 (cmov%c1,%3,%0), operands);
- if (mode == DImode)
- output_asm_insn (AS2 (cmov%c1,%3,%0), xops);
break;
default:
return 0;
if (agi_dependent (insn, dep_insn))
- return 3;
+ return cost ? cost + 1 : 2;
if (GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) == SET
return 0;
break;
+ /* Stores stalls one cycle longer than other insns. */
+ if (is_fp_insn (insn) && cost && is_fp_store (dep_insn))
+ cost++;
+
case PROCESSOR_K6:
default:
if (!is_fp_dest (dep_insn))
return cost;
}
+
+/* Output assembly code for a left shift.
+
+ Always use "sal" when shifting a memory operand or for a non constant
+ shift count.
+
+ 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):
+
+ 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.
+
+ 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. */
+
+char *
+output_ashl (insn, operands)
+ rtx insn, *operands;
+{
+ /* Handle case where srcreg != dstreg. */
+ if (REG_P (operands[0]) && REGNO (operands[0]) != REGNO (operands[1]))
+ {
+ 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;
+
+ /* 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];
+ }
+
+ /* 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);
+ }
+ }
+
+ /* Source and destination match. */
+
+ /* Handle variable shift. */
+ if (REG_P (operands[2]))
+ switch (GET_MODE (operands[0]))
+ {
+ case SImode:
+ return AS2 (sal%L0,%b2,%0);
+ case HImode:
+ if (REG_P (operands[0]) && i386_cc_probably_useless_p (insn))
+ {
+ CC_STATUS_INIT;
+ return AS2 (sal%L0,%b2,%k0);
+ }
+ else
+ return AS2 (sal%W0,%b2,%0);
+ case QImode:
+ return AS2 (sal%B0,%b2,%0);
+ default:
+ abort ();
+ }
+
+ /* Always perform shift by 1 using an add instruction. */
+ if (REG_P (operands[0]) && operands[2] == const1_rtx)
+ switch (GET_MODE (operands[0]))
+ {
+ case SImode:
+ return AS2 (add%L0,%0,%0);
+ case HImode:
+ if (REG_P (operands[0]) && i386_cc_probably_useless_p (insn))
+ {
+ CC_STATUS_INIT;
+ return AS2 (add%L0,%k0,%k0);
+ }
+ else
+ return AS2 (add%W0,%0,%0);
+ case QImode:
+ return AS2 (add%B0,%0,%0);
+ default:
+ abort ();
+ }
+
+#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
+
+ /* 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 ();
+ }
+}
+
+/* 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. */
+
+int
+memory_address_info (addr, disp_length)
+ rtx addr;
+ int disp_length;
+{
+ rtx base, index, disp, scale;
+ rtx op0, op1;
+ int len;
+
+ if (GET_CODE (addr) == PRE_DEC
+ || GET_CODE (addr) == POST_INC)
+ return 0;
+
+ /* Register Indirect. */
+ if (register_operand (addr, Pmode))
+ {
+ /* Special cases: ebp and esp need the two-byte modrm form.
+
+ 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;
+ }
+
+ /* Direct Addressing. */
+ if (CONSTANT_P (addr))
+ return 4;
+
+ index = base = disp = scale = NULL_RTX;
+ op0 = XEXP (addr, 0);
+ op1 = XEXP (addr, 1);
+
+ 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;
+ }
+
+ /* Special case: ebp cannot be encoded as a base without a displacement. */
+ if (base == frame_pointer_rtx && !disp)
+ disp = const0_rtx;
+
+ /* 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 != 1)
+ disp = const0_rtx;
+
+ /* Find the length of the displacement constant. */
+ len = 0;
+ if (disp)
+ {
+ if (GET_CODE (disp) == CONST_INT
+ && CONST_OK_FOR_LETTER_P (INTVAL (disp), 'K'))
+ len = 1;
+ else
+ len = 4;
+ }
+
+ /* 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;
+
+ return len;
+}