summaryrefslogtreecommitdiffstats
path: root/gnu/usr.bin/cc/cc_int/aux-output.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin/cc/cc_int/aux-output.c')
-rw-r--r--gnu/usr.bin/cc/cc_int/aux-output.c1065
1 files changed, 894 insertions, 171 deletions
diff --git a/gnu/usr.bin/cc/cc_int/aux-output.c b/gnu/usr.bin/cc/cc_int/aux-output.c
index bc498e0..830f3dd 100644
--- a/gnu/usr.bin/cc/cc_int/aux-output.c
+++ b/gnu/usr.bin/cc/cc_int/aux-output.c
@@ -1,5 +1,5 @@
-/* Subroutines for insn-output.c for Intel 80386.
- Copyright (C) 1988, 1992 Free Software Foundation, Inc.
+/* Subroutines for insn-output.c for Intel X86.
+ Copyright (C) 1988, 1992, 1994 Free Software Foundation, Inc.
This file is part of GNU CC.
@@ -74,8 +74,151 @@ enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] =
/* Test and compare insns in i386.md store the information needed to
generate branch and scc insns here. */
-struct rtx_def *i386_compare_op0, *i386_compare_op1;
+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)();
+
+/* Register allocation order */
+char *i386_reg_alloc_order = (char *)0;
+static char regs_allocated[FIRST_PSEUDO_REGISTER];
+
+
+/* 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
+ defined, is executed once just after all the command options have
+ been parsed.
+
+ Don't use this macro to turn on various extra optimizations for
+ `-O'. That is what `OPTIMIZATION_OPTIONS' is for. */
+
+void
+override_options ()
+{
+ int ch, i, regno;
+
+#ifdef SUBTARGET_OVERRIDE_OPTIONS
+ SUBTARGET_OVERRIDE_OPTIONS;
+#endif
+
+ /* Validate registers in register allocation order */
+ if (i386_reg_alloc_order)
+ {
+ for (i = 0; (ch = i386_reg_alloc_order[i]) != '\0'; i++)
+ {
+ switch (ch)
+ {
+ case 'a': regno = 0; break;
+ case 'd': regno = 1; break;
+ case 'c': regno = 2; break;
+ case 'b': regno = 3; break;
+ case 'S': regno = 4; break;
+ case 'D': regno = 5; break;
+ case 'B': regno = 6; break;
+
+ default: fatal ("Register '%c' is unknown", ch);
+ }
+
+ if (regs_allocated[regno])
+ fatal ("Register '%c' was already specified in the allocation order", ch);
+
+ regs_allocated[regno] = 1;
+ }
+ }
+}
+
+/* A C statement (sans semicolon) to choose the order in which to
+ allocate hard registers for pseudo-registers local to a basic
+ block.
+
+ Store the desired register order in the array `reg_alloc_order'.
+ Element 0 should be the register to allocate first; element 1, the
+ next register; and so on.
+
+ The macro body should not assume anything about the contents of
+ `reg_alloc_order' before execution of the macro.
+
+ On most machines, it is not necessary to define this macro. */
+
+void
+order_regs_for_local_alloc ()
+{
+ int i, ch, order, regno;
+
+ /* User specified the register allocation order */
+ if (i386_reg_alloc_order)
+ {
+ for (i = order = 0; (ch = i386_reg_alloc_order[i]) != '\0'; i++)
+ {
+ switch (ch)
+ {
+ case 'a': regno = 0; break;
+ case 'd': regno = 1; break;
+ case 'c': regno = 2; break;
+ case 'b': regno = 3; break;
+ case 'S': regno = 4; break;
+ case 'D': regno = 5; break;
+ case 'B': regno = 6; break;
+ }
+
+ reg_alloc_order[order++] = regno;
+ }
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ if (!regs_allocated[i])
+ reg_alloc_order[order++] = i;
+ }
+ }
+
+ /* If users did not specify a register allocation order, favor eax
+ normally except if DImode variables are used, in which case
+ favor edx before eax, which seems to cause less spill register
+ not found messages. */
+ else
+ {
+ rtx insn;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ reg_alloc_order[i] = i;
+
+ if (optimize)
+ {
+ int use_dca = FALSE;
+
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == INSN)
+ {
+ rtx set = NULL_RTX;
+ rtx pattern = PATTERN (insn);
+
+ if (GET_CODE (pattern) == SET)
+ set = pattern;
+
+ else if ((GET_CODE (pattern) == PARALLEL
+ || GET_CODE (pattern) == SEQUENCE)
+ && GET_CODE (XVECEXP (pattern, 0, 0)) == SET)
+ set = XVECEXP (pattern, 0, 0);
+
+ if (set && GET_MODE (SET_SRC (set)) == DImode)
+ {
+ use_dca = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (use_dca)
+ {
+ reg_alloc_order[0] = 1; /* edx */
+ reg_alloc_order[1] = 2; /* ecx */
+ reg_alloc_order[2] = 0; /* eax */
+ }
+ }
+ }
+}
+
/* 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
@@ -235,6 +378,7 @@ find_addr_reg (addr)
abort ();
}
+
/* Output an insn to add the constant N to the register X. */
static void
@@ -243,19 +387,25 @@ asm_add (n, x)
rtx x;
{
rtx xops[2];
- xops[1] = x;
- if (n < 0)
+ xops[0] = x;
+
+ 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)
{
- xops[0] = GEN_INT (-n);
- output_asm_insn (AS2 (sub%L0,%0,%1), xops);
+ xops[1] = GEN_INT (-n);
+ output_asm_insn (AS2 (sub%L0,%1,%0), xops);
}
else if (n > 0)
{
- xops[0] = GEN_INT (n);
- output_asm_insn (AS2 (add%L0,%0,%1), xops);
+ xops[1] = GEN_INT (n);
+ output_asm_insn (AS2 (add%L0,%1,%0), xops);
}
}
+
/* Output assembler code to perform a doubleword move insn
with operands OPERANDS. */
@@ -587,6 +737,199 @@ compadr:
return "";
}
+
+
+#define MAX_TMPS 2 /* max temporary registers used */
+
+/* Output the appropriate code to move push memory on the stack */
+
+char *
+output_move_pushmem (operands, insn, length, tmp_start, n_operands)
+ rtx operands[];
+ rtx insn;
+ int length;
+ int tmp_start;
+ int n_operands;
+{
+
+ 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);
+
+ /* 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;
+
+ tmp_info[ max_tmps++ ].xops[1] = operands[i];
+ if (max_tmps == MAX_TMPS)
+ break;
+ }
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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].push, tmp_info[i].xops);
+
+ if (stack_p)
+ stack_offset += 4*num_tmps;
+ }
+
+ return "";
+}
+
+
+
+/* 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 && 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)
+ 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 "";
+}
+
int
standard_80387_constant_p (x)
@@ -741,111 +1084,6 @@ symbolic_reference_mentioned_p (op)
return 0;
}
-/* Return a legitimate reference for ORIG (an address) using the
- register REG. If REG is 0, a new pseudo is generated.
-
- There are three types of references that must be handled:
-
- 1. Global data references must load the address from the GOT, via
- the PIC reg. An insn is emitted to do this load, and the reg is
- returned.
-
- 2. Static data references must compute the address as an offset
- from the GOT, whose base is in the PIC reg. An insn is emitted to
- compute the address into a reg, and the reg is returned. Static
- data objects have SYMBOL_REF_FLAG set to differentiate them from
- global data objects.
-
- 3. Constant pool addresses must be handled special. They are
- considered legitimate addresses, but only if not used with regs.
- When printed, the output routines know to print the reference with the
- PIC reg, even though the PIC reg doesn't appear in the RTL.
-
- GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC
- reg also appears in the address (except for constant pool references,
- noted above).
-
- "switch" statements also require special handling when generating
- PIC code. See comments by the `casesi' insn in i386.md for details. */
-
-rtx
-legitimize_pic_address (orig, reg)
- rtx orig;
- rtx reg;
-{
- rtx addr = orig;
- rtx new = orig;
-
- if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
- {
- if (GET_CODE (addr) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (addr))
- reg = new = orig;
- else
- {
- if (reg == 0)
- reg = gen_reg_rtx (Pmode);
-
- if (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_FLAG (addr))
- new = gen_rtx (PLUS, Pmode, pic_offset_table_rtx, orig);
- else
- new = gen_rtx (MEM, Pmode,
- gen_rtx (PLUS, Pmode,
- pic_offset_table_rtx, orig));
-
- emit_move_insn (reg, new);
- }
- current_function_uses_pic_offset_table = 1;
- return reg;
- }
- else if (GET_CODE (addr) == CONST || GET_CODE (addr) == PLUS)
- {
- rtx base;
-
- if (GET_CODE (addr) == CONST)
- {
- addr = XEXP (addr, 0);
- if (GET_CODE (addr) != PLUS)
- abort ();
- }
-
- if (XEXP (addr, 0) == pic_offset_table_rtx)
- return orig;
-
- if (reg == 0)
- reg = gen_reg_rtx (Pmode);
-
- base = legitimize_pic_address (XEXP (addr, 0), reg);
- addr = legitimize_pic_address (XEXP (addr, 1),
- base == reg ? NULL_RTX : reg);
-
- if (GET_CODE (addr) == CONST_INT)
- return plus_constant (base, INTVAL (addr));
-
- if (GET_CODE (addr) == PLUS && CONSTANT_P (XEXP (addr, 1)))
- {
- base = gen_rtx (PLUS, Pmode, base, XEXP (addr, 0));
- addr = XEXP (addr, 1);
- }
- return gen_rtx (PLUS, Pmode, base, addr);
- }
- return new;
-}
-
-/* Emit insns to move operands[1] into operands[0]. */
-
-void
-emit_pic_move (operands, mode)
- rtx *operands;
- enum machine_mode mode;
-{
- rtx temp = reload_in_progress ? operands[0] : gen_reg_rtx (Pmode);
-
- if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1]))
- operands[1] = (rtx) force_reg (SImode, operands[1]);
- else
- operands[1] = legitimize_pic_address (operands[1], temp);
-}
-
/* This function generates 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. */
@@ -859,8 +1097,7 @@ function_prologue (file, size)
int limit;
rtx xops[4];
int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
- || current_function_uses_const_pool
- || profile_flag || profile_block_flag);
+ || current_function_uses_const_pool);
xops[0] = stack_pointer_rtx;
xops[1] = frame_pointer_rtx;
@@ -921,16 +1158,8 @@ simple_386_epilogue ()
int nregs = 0;
int reglimit = (frame_pointer_needed
? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
-
-#ifdef FUNCTION_PROFILER_EPILOGUE
- if (profile_flag)
- return 0;
-#endif
-
- if (flag_pic && (current_function_uses_pic_offset_table
- || current_function_uses_const_pool
- || profile_flag || profile_block_flag))
- return 0;
+ 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)
@@ -941,12 +1170,14 @@ simple_386_epilogue ()
return 0;
for (regno = reglimit - 1; regno >= 0; regno--)
- if (regs_ever_live[regno] && ! call_used_regs[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. */
@@ -963,11 +1194,6 @@ function_epilogue (file, size)
int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
|| current_function_uses_const_pool);
-#ifdef FUNCTION_PROFILER_EPILOGUE
- if (profile_flag)
- FUNCTION_PROFILER_EPILOGUE (file);
-#endif
-
/* Compute the number of registers to pop */
limit = (frame_pointer_needed
@@ -1025,7 +1251,7 @@ function_epilogue (file, size)
{
/* On i486, mov & pop is faster than "leave". */
- if (TARGET_486)
+ if (!TARGET_386)
{
xops[0] = frame_pointer_rtx;
output_asm_insn (AS2 (mov%L2,%0,%2), xops);
@@ -1064,6 +1290,527 @@ function_epilogue (file, size)
else
output_asm_insn ("ret", xops);
}
+
+
+/* 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. */
+
+#define ADDR_INVALID(msg,insn) \
+do { \
+ if (TARGET_DEBUG_ADDR) \
+ { \
+ fprintf (stderr, msg); \
+ debug_rtx (insn); \
+ } \
+} while (0)
+
+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;
+
+ 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; /* base reg */
+
+ 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)
+ {
+ indx = XEXP (addr, 0); /* index*scale */
+ scale = XEXP (addr, 1);
+ }
+
+ 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;
+ }
+
+ /* 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
+ as a DImode int. */
+ if (base)
+ {
+ if (GET_CODE (base) != REG)
+ {
+ ADDR_INVALID ("Base is not a register.\n", base);
+ return FALSE;
+ }
+
+ 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;
+ }
+ }
+
+ /* 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 (GET_CODE (indx) != REG)
+ {
+ ADDR_INVALID ("Index is not a register.\n", indx);
+ return FALSE;
+ }
+
+ if ((strict && !REG_OK_FOR_INDEX_STRICT_P (indx))
+ || (!strict && !REG_OK_FOR_INDEX_NONSTRICT_P (indx)))
+ {
+ ADDR_INVALID ("Index is not valid.\n", indx);
+ return FALSE;
+ }
+ }
+ else if (scale)
+ abort (); /* scale w/o index illegal */
+
+ /* Validate scale factor */
+ if (scale)
+ {
+ HOST_WIDE_INT value;
+
+ if (GET_CODE (scale) != CONST_INT)
+ {
+ ADDR_INVALID ("Scale is not valid.\n", scale);
+ return FALSE;
+ }
+
+ value = INTVAL (scale);
+ if (value != 1 && value != 2 && value != 4 && value != 8)
+ {
+ ADDR_INVALID ("Scale is not a good multiplier.\n", scale);
+ return FALSE;
+ }
+ }
+
+ /* Validate displacement */
+ if (disp)
+ {
+ if (!CONSTANT_ADDRESS_P (disp))
+ {
+ ADDR_INVALID ("Displacement is not valid.\n", disp);
+ return FALSE;
+ }
+
+ if (GET_CODE (disp) == CONST_DOUBLE)
+ {
+ ADDR_INVALID ("Displacement is a const_double.\n", disp);
+ return FALSE;
+ }
+
+ if (flag_pic && SYMBOLIC_CONST (disp) && base != pic_offset_table_rtx
+ && (indx != pic_offset_table_rtx || scale != NULL_RTX))
+ {
+ ADDR_INVALID ("Displacement is an invalid pic reference.\n", disp);
+ return FALSE;
+ }
+
+ if (HALF_PIC_P () && HALF_PIC_ADDRESS_P (disp)
+ && (base != NULL_RTX || indx != NULL_RTX))
+ {
+ ADDR_INVALID ("Displacement is an invalid half-pic reference.\n", disp);
+ return FALSE;
+ }
+ }
+
+ if (TARGET_DEBUG_ADDR)
+ fprintf (stderr, "Address is valid.\n");
+
+ /* Everything looks valid, return true */
+ return TRUE;
+}
+
+
+/* Return a legitimate reference for ORIG (an address) using the
+ register REG. If REG is 0, a new pseudo is generated.
+
+ There are three types of references that must be handled:
+
+ 1. Global data references must load the address from the GOT, via
+ the PIC reg. An insn is emitted to do this load, and the reg is
+ returned.
+
+ 2. Static data references must compute the address as an offset
+ from the GOT, whose base is in the PIC reg. An insn is emitted to
+ compute the address into a reg, and the reg is returned. Static
+ data objects have SYMBOL_REF_FLAG set to differentiate them from
+ global data objects.
+
+ 3. Constant pool addresses must be handled special. They are
+ considered legitimate addresses, but only if not used with regs.
+ When printed, the output routines know to print the reference with the
+ PIC reg, even though the PIC reg doesn't appear in the RTL.
+
+ GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC
+ reg also appears in the address (except for constant pool references,
+ noted above).
+
+ "switch" statements also require special handling when generating
+ PIC code. See comments by the `casesi' insn in i386.md for details. */
+
+rtx
+legitimize_pic_address (orig, reg)
+ rtx orig;
+ rtx reg;
+{
+ rtx addr = orig;
+ rtx new = orig;
+
+ if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
+ {
+ if (GET_CODE (addr) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (addr))
+ reg = new = orig;
+ else
+ {
+ if (reg == 0)
+ reg = gen_reg_rtx (Pmode);
+
+ if ((GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_FLAG (addr))
+ || GET_CODE (addr) == LABEL_REF)
+ new = gen_rtx (PLUS, Pmode, pic_offset_table_rtx, orig);
+ else
+ new = gen_rtx (MEM, Pmode,
+ gen_rtx (PLUS, Pmode,
+ pic_offset_table_rtx, orig));
+
+ emit_move_insn (reg, new);
+ }
+ current_function_uses_pic_offset_table = 1;
+ return reg;
+ }
+ else if (GET_CODE (addr) == CONST || GET_CODE (addr) == PLUS)
+ {
+ rtx base;
+
+ if (GET_CODE (addr) == CONST)
+ {
+ addr = XEXP (addr, 0);
+ if (GET_CODE (addr) != PLUS)
+ abort ();
+ }
+
+ if (XEXP (addr, 0) == pic_offset_table_rtx)
+ return orig;
+
+ if (reg == 0)
+ reg = gen_reg_rtx (Pmode);
+
+ base = legitimize_pic_address (XEXP (addr, 0), reg);
+ addr = legitimize_pic_address (XEXP (addr, 1),
+ base == reg ? NULL_RTX : reg);
+
+ if (GET_CODE (addr) == CONST_INT)
+ return plus_constant (base, INTVAL (addr));
+
+ if (GET_CODE (addr) == PLUS && CONSTANT_P (XEXP (addr, 1)))
+ {
+ base = gen_rtx (PLUS, Pmode, base, XEXP (addr, 0));
+ addr = XEXP (addr, 1);
+ }
+ return gen_rtx (PLUS, Pmode, base, addr);
+ }
+ return new;
+}
+
+
+/* Emit insns to move operands[1] into operands[0]. */
+
+void
+emit_pic_move (operands, mode)
+ rtx *operands;
+ enum machine_mode mode;
+{
+ rtx temp = reload_in_progress ? operands[0] : gen_reg_rtx (Pmode);
+
+ if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1]))
+ operands[1] = (rtx) force_reg (SImode, operands[1]);
+ else
+ operands[1] = legitimize_pic_address (operands[1], temp);
+}
+
+
+/* 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.
+
+ OLDX is the address as it was before break_out_memory_refs was called.
+ In some cases it is useful to look at this to decide what needs to be done.
+
+ MODE and WIN are passed so that this macro can use
+ GO_IF_LEGITIMATE_ADDRESS.
+
+ It is always safe for this macro to do nothing. It exists to recognize
+ opportunities to optimize the output.
+
+ For the 80386, we handle X+REG by loading X into a register R and
+ using R+REG. R will go in a general reg and indexing will be used.
+ However, if REG is a broken-out memory address or multiplication,
+ nothing needs to be done because REG can certainly go in a general reg.
+
+ When -fpic is used, special handling is needed for symbolic references.
+ See comments by legitimize_pic_address in i386.c for details. */
+
+rtx
+legitimize_address (x, oldx, mode)
+ register rtx x;
+ register rtx oldx;
+ enum machine_mode mode;
+{
+ int changed = 0;
+ unsigned log;
+
+ if (TARGET_DEBUG_ADDR)
+ {
+ fprintf (stderr, "\n==========\nLEGITIMIZE_ADDRESS, mode = %s\n", GET_MODE_NAME (mode));
+ debug_rtx (x);
+ }
+
+ if (flag_pic && SYMBOLIC_CONST (x))
+ return legitimize_pic_address (x, 0);
+
+ /* Canonicalize shifts by 0, 1, 2, 3 into multiply */
+ if (GET_CODE (x) == ASHIFT
+ && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && (log = (unsigned)exact_log2 (INTVAL (XEXP (x, 1)))) < 4)
+ {
+ changed = 1;
+ x = gen_rtx (MULT, Pmode,
+ force_reg (Pmode, XEXP (x, 0)),
+ GEN_INT (1 << log));
+ }
+
+ if (GET_CODE (x) == PLUS)
+ {
+ /* Canonicalize shifts by 0, 1, 2, 3 into multiply */
+ if (GET_CODE (XEXP (x, 0)) == ASHIFT
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+ && (log = (unsigned)exact_log2 (INTVAL (XEXP (XEXP (x, 0), 1)))) < 4)
+ {
+ changed = 1;
+ XEXP (x, 0) = gen_rtx (MULT, Pmode,
+ force_reg (Pmode, XEXP (XEXP (x, 0), 0)),
+ GEN_INT (1 << log));
+ }
+
+ if (GET_CODE (XEXP (x, 1)) == ASHIFT
+ && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT
+ && (log = (unsigned)exact_log2 (INTVAL (XEXP (XEXP (x, 1), 1)))) < 4)
+ {
+ changed = 1;
+ XEXP (x, 1) = gen_rtx (MULT, Pmode,
+ force_reg (Pmode, XEXP (XEXP (x, 1), 0)),
+ GEN_INT (1 << log));
+ }
+
+ /* Put multiply first if it isn't already */
+ if (GET_CODE (XEXP (x, 1)) == MULT)
+ {
+ rtx tmp = XEXP (x, 0);
+ XEXP (x, 0) = XEXP (x, 1);
+ XEXP (x, 1) = tmp;
+ changed = 1;
+ }
+
+ /* Canonicalize (plus (mult (reg) (const)) (plus (reg) (const)))
+ into (plus (plus (mult (reg) (const)) (reg)) (const)). This can be
+ created by virtual register instantiation, register elimination, and
+ similar optimizations. */
+ if (GET_CODE (XEXP (x, 0)) == MULT && GET_CODE (XEXP (x, 1)) == PLUS)
+ {
+ changed = 1;
+ x = gen_rtx (PLUS, Pmode,
+ gen_rtx (PLUS, Pmode, XEXP (x, 0), XEXP (XEXP (x, 1), 0)),
+ XEXP (XEXP (x, 1), 1));
+ }
+
+ /* Canonicalize (plus (plus (mult (reg) (const)) (plus (reg) (const))) const)
+ into (plus (plus (mult (reg) (const)) (reg)) (const)). */
+ else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == PLUS
+ && CONSTANT_P (XEXP (x, 1)))
+ {
+ rtx constant, other;
+
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+ {
+ constant = XEXP (x, 1);
+ other = XEXP (XEXP (XEXP (x, 0), 1), 1);
+ }
+ else if (GET_CODE (XEXP (XEXP (XEXP (x, 0), 1), 1)) == CONST_INT)
+ {
+ constant = XEXP (XEXP (XEXP (x, 0), 1), 1);
+ other = XEXP (x, 1);
+ }
+ else
+ constant = 0;
+
+ if (constant)
+ {
+ changed = 1;
+ x = gen_rtx (PLUS, Pmode,
+ gen_rtx (PLUS, Pmode, XEXP (XEXP (x, 0), 0),
+ XEXP (XEXP (XEXP (x, 0), 1), 0)),
+ plus_constant (other, INTVAL (constant)));
+ }
+ }
+
+ if (changed && legitimate_address_p (mode, x, FALSE))
+ return x;
+
+ if (GET_CODE (XEXP (x, 0)) == MULT)
+ {
+ changed = 1;
+ XEXP (x, 0) = force_operand (XEXP (x, 0), 0);
+ }
+
+ if (GET_CODE (XEXP (x, 1)) == MULT)
+ {
+ changed = 1;
+ XEXP (x, 1) = force_operand (XEXP (x, 1), 0);
+ }
+
+ if (changed
+ && GET_CODE (XEXP (x, 1)) == REG
+ && GET_CODE (XEXP (x, 0)) == REG)
+ return x;
+
+ if (flag_pic && SYMBOLIC_CONST (XEXP (x, 1)))
+ {
+ changed = 1;
+ x = legitimize_pic_address (x, 0);
+ }
+
+ if (changed && legitimate_address_p (mode, x, FALSE))
+ return x;
+
+ if (GET_CODE (XEXP (x, 0)) == REG)
+ {
+ register rtx temp = gen_reg_rtx (Pmode);
+ register rtx val = force_operand (XEXP (x, 1), temp);
+ if (val != temp)
+ emit_move_insn (temp, val);
+
+ XEXP (x, 1) = temp;
+ return x;
+ }
+
+ else if (GET_CODE (XEXP (x, 1)) == REG)
+ {
+ register rtx temp = gen_reg_rtx (Pmode);
+ register rtx val = force_operand (XEXP (x, 0), temp);
+ if (val != temp)
+ emit_move_insn (temp, val);
+
+ XEXP (x, 0) = temp;
+ return x;
+ }
+ }
+
+ return x;
+}
+
/* Print an integer constant expression in assembler syntax. Addition
and subtraction are the only arithmetic that may appear in these
@@ -1102,7 +1849,9 @@ output_pic_addr_const (file, x, code)
fprintf (file, "@GOTOFF(%%ebx)");
else if (code == 'P')
fprintf (file, "@PLT");
- else if (GET_CODE (x) == LABEL_REF || ! SYMBOL_REF_FLAG (x))
+ else if (GET_CODE (x) == LABEL_REF)
+ fprintf (file, "@GOTOFF");
+ else if (! SYMBOL_REF_FLAG (x))
fprintf (file, "@GOT");
else
fprintf (file, "@GOTOFF");
@@ -1424,15 +2173,14 @@ print_operand_address (file, addr)
if (addr != 0)
{
- if (GET_CODE (addr) == LABEL_REF)
+ if (flag_pic)
+ output_pic_addr_const (file, addr, 0);
+
+ else if (GET_CODE (addr) == LABEL_REF)
output_asm_label (addr);
+
else
- {
- if (flag_pic)
- output_pic_addr_const (file, addr, 0);
- else
- output_addr_const (file, addr);
- }
+ output_addr_const (file, addr);
}
if (ireg != 0 && GET_CODE (ireg) == MULT)
@@ -1666,32 +2414,7 @@ binary_387_op (op, mode)
}
}
-/* Return 1 if this is a valid conversion operation on a 387.
- OP is the expression matched, and MODE is its mode. */
-
-int
-convert_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 FLOAT:
- return GET_MODE (XEXP (op, 0)) == SImode;
-
- case FLOAT_EXTEND:
- return ((mode == DFmode && GET_MODE (XEXP (op, 0)) == SFmode)
- || (mode == XFmode && GET_MODE (XEXP (op, 0)) == DFmode)
- || (mode == XFmode && GET_MODE (XEXP (op, 0)) == SFmode));
-
- default:
- return 0;
- }
-}
-
+
/* Return 1 if this is a valid shift or rotate operation on a 386.
OP is the expression matched, and MODE is its mode. */
@@ -1813,9 +2536,9 @@ output_387_binary_op (insn, operands)
return strcat (buf, AS2 (p,%2,%0));
if (STACK_TOP_P (operands[0]))
- return strcat (buf, AS2 (,%y2,%0));
+ return strcat (buf, AS2C (%y2,%0));
else
- return strcat (buf, AS2 (,%2,%0));
+ return strcat (buf, AS2C (%2,%0));
case MINUS:
case DIV:
@@ -1848,12 +2571,12 @@ output_387_binary_op (insn, operands)
if (STACK_TOP_P (operands[0]))
{
if (STACK_TOP_P (operands[1]))
- return strcat (buf, AS2 (,%y2,%0));
+ return strcat (buf, AS2C (%y2,%0));
else
return strcat (buf, AS2 (r,%y1,%0));
}
else if (STACK_TOP_P (operands[1]))
- return strcat (buf, AS2 (,%1,%0));
+ return strcat (buf, AS2C (%1,%0));
else
return strcat (buf, AS2 (r,%2,%0));
OpenPOWER on IntegriCloud