abort ();
}
+\f
/* Output an insn to add the constant N to the register X. */
static void
}
}
+\f
/* Output assembler code to perform a doubleword move insn
with operands OPERANDS. */
return "";
}
+
+\f
+#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 "";
+}
+
+\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 && 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 "";
+}
+
\f
int
standard_80387_constant_p (x)
/* Fastest way to change a 0 to a 1. */
return AS1 (inc%L0,%0);
+ if (flag_pic
+ && GET_CODE (operands[1]) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (operands[1]))
+ return AS2 (lea%L0,%a1,%0);
+
return AS2 (mov%L0,%1,%0);
}")
}")
(define_insn "movsf_push"
- [(set (match_operand:SF 0 "push_operand" "=<,<,<")
- (match_operand:SF 1 "general_operand" "rF,f,m"))
- (clobber (match_scratch:SI 2 "=X,X,r"))]
+ [(set (match_operand:SF 0 "push_operand" "=<,<,<,<")
+ (match_operand:SF 1 "general_operand" "rF,f,m,m"))
+ (clobber (match_scratch:SI 2 "=X,X,r,X"))]
""
"*
{
RET;
}
- else if (GET_CODE (operands[1]) != MEM)
+ else if (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != REG)
return AS1 (push%L1,%1);
else
RET;
}
- else if (GET_CODE (operands[1]) != MEM
- || GET_CODE (operands[2]) != REG)
+ else if (GET_CODE (operands[1]) != MEM)
return output_move_double (operands);
else
- {
- rtx low[1], high[1], xop[4];
-
- split_di (&operands[1], 1, low, high);
- xop[0] = operands[2];
- xop[1] = operands[3];
- xop[2] = high[0];
- xop[3] = low[0];
-
- if (GET_CODE (operands[3]) == REG)
- { /* 2 scratch registers available */
- output_asm_insn (AS2 (mov%L0,%2,%0), xop);
- output_asm_insn (AS2 (mov%L0,%3,%1), xop);
- output_asm_insn (AS1 (push%L0,%0), xop);
- output_asm_insn (AS1 (push%L0,%1), xop);
- }
- else
- { /* 1 scratch register */
- output_asm_insn (AS2 (mov%L0,%2,%0), xop);
- output_asm_insn (AS1 (push%L0,%0), xop);
-
- /* account for push above */
- if (reg_mentioned_p (stack_pointer_rtx, XEXP (xop[3], 0)))
- xop[3] = adj_offsettable_operand (xop[3], 4);
-
- output_asm_insn (AS2 (mov%L0,%3,%0), xop);
- output_asm_insn (AS1 (push%L0,%0), xop);
- }
-
- RET;
- }
+ return output_move_pushmem (operands, insn, GET_MODE_SIZE (DFmode), 2, 4);
}")
(define_insn "movdf_mem"
(clobber (match_scratch:SI 2 "=&r,&r"))
(clobber (match_scratch:SI 3 "=&r,X"))]
""
- "*
-{
- rtx low[2], high[2], xop[6];
-
- split_di (operands, 2, low, high);
- xop[0] = operands[2];
- xop[1] = operands[3];
- xop[2] = high[0];
- xop[3] = high[1];
- xop[4] = low[0];
- xop[5] = low[1];
- if (GET_CODE (operands[3]) == REG)
- { /* 2 scratch registers available */
- output_asm_insn (AS2 (mov%L0,%5,%0), xop);
- output_asm_insn (AS2 (mov%L0,%3,%1), xop);
- output_asm_insn (AS2 (mov%L0,%0,%4), xop);
- output_asm_insn (AS2 (mov%L0,%1,%2), xop);
- }
- else
- { /* 1 scratch register */
- output_asm_insn (AS2 (mov%L0,%5,%0), xop);
- output_asm_insn (AS2 (mov%L0,%0,%4), xop);
- output_asm_insn (AS2 (mov%L0,%3,%0), xop);
- output_asm_insn (AS2 (mov%L0,%0,%2), xop);
- }
-
- RET;
-}")
+ "* return output_move_memory (operands, insn, GET_MODE_SIZE (DFmode), 2, 4);")
;; For the purposes of regclass, prefer FLOAT_REGS.
(define_insn "movdf_normal"
return output_move_double (operands);
else
- {
- rtx xop[5];
-
- xop[0] = operands[2];
- xop[1] = operands[3];
- xop[2] = adj_offsettable_operand (operands[1], 8);
- xop[3] = adj_offsettable_operand (operands[1], 4);
- xop[4] = operands[1];
-
- if (GET_CODE (operands[3]) == REG)
- { /* 2 scratch registers available */
- output_asm_insn (AS2 (mov%L0,%2,%0), xop);
- output_asm_insn (AS2 (mov%L0,%3,%1), xop);
- output_asm_insn (AS1 (push%L0,%0), xop);
- output_asm_insn (AS1 (push%L0,%1), xop);
-
- /* account for 2 pushes above */
- if (reg_mentioned_p (stack_pointer_rtx, XEXP (xop[4], 0)))
- xop[4] = adj_offsettable_operand (xop[4], 8);
-
- output_asm_insn (AS2 (mov%L0,%4,%0), xop);
- output_asm_insn (AS1 (push%L0,%0), xop);
- }
- else
- { /* 1 scratch register */
- output_asm_insn (AS2 (mov%L0,%2,%0), xop);
- output_asm_insn (AS1 (push%L0,%0), xop);
-
- /* account for 1 push above */
- if (reg_mentioned_p (stack_pointer_rtx, XEXP (xop[3], 0)))
- xop[3] = adj_offsettable_operand (xop[3], 4);
-
- output_asm_insn (AS2 (mov%L0,%3,%0), xop);
- output_asm_insn (AS1 (push%L0,%0), xop);
-
- /* account for 2 pushes above */
- if (reg_mentioned_p (stack_pointer_rtx, XEXP (xop[4], 0)))
- xop[4] = adj_offsettable_operand (xop[4], 8);
-
- output_asm_insn (AS2 (mov%L0,%4,%0), xop);
- output_asm_insn (AS1 (push%L0,%0), xop);
- }
-
- RET;
- }
+ return output_move_pushmem (operands, insn, GET_MODE_SIZE (XFmode), 2, 4);
}")
(define_insn "movxf_mem"
(clobber (match_scratch:SI 2 "=&r,&r"))
(clobber (match_scratch:SI 3 "=&r,X"))]
""
- "*
-{
- rtx xop[8];
-
- xop[0] = operands[2];
- xop[1] = operands[3];
- xop[2] = adj_offsettable_operand (operands[1], 8);
- xop[3] = adj_offsettable_operand (operands[1], 4);
- xop[4] = operands[1];
- xop[5] = adj_offsettable_operand (operands[0], 8);
- xop[6] = adj_offsettable_operand (operands[0], 4);
- xop[7] = operands[0];
-
- if (GET_CODE (operands[3]) == REG)
- { /* 2 scratch registers available */
- output_asm_insn (AS2 (mov%L0,%2,%0), xop);
- output_asm_insn (AS2 (mov%L0,%3,%1), xop);
- output_asm_insn (AS2 (mov%L5,%0,%5), xop);
- output_asm_insn (AS2 (mov%L6,%1,%6), xop);
- output_asm_insn (AS2 (mov%L0,%4,%0), xop);
- output_asm_insn (AS2 (mov%L7,%0,%7), xop);
- }
- else
- { /* 1 scratch register */
- output_asm_insn (AS2 (mov%L0,%2,%0), xop);
- output_asm_insn (AS2 (mov%L0,%0,%5), xop);
- output_asm_insn (AS2 (mov%L0,%3,%0), xop);
- output_asm_insn (AS2 (mov%L0,%0,%6), xop);
- output_asm_insn (AS2 (mov%L0,%4,%0), xop);
- output_asm_insn (AS2 (mov%L7,%0,%7), xop);
- }
-
- RET;
-}")
+ "* return output_move_memory (operands, insn, GET_MODE_SIZE (XFmode), 2, 4);")
(define_insn "movxf_normal"
[(set (match_operand:XF 0 "general_operand" "=f,fm,!*rf,!*rm")
return AS1 (fxch,%0);
}")
-(define_expand "movdi"
- [(set (match_operand:DI 0 "general_operand" "")
- (match_operand:DI 1 "general_operand" ""))]
- ""
- "
-{
- /* Special case memory->memory moves and pushes */
- if (TARGET_MOVE
- && (reload_in_progress | reload_completed) == 0
- && GET_CODE (operands[0]) == MEM
- && (GET_CODE (operands[1]) == MEM || push_operand (operands[0], DImode)))
- {
- rtx (*genfunc) PROTO((rtx, rtx)) = (push_operand (operands[0], DImode))
- ? gen_movdi_push
- : gen_movdi_mem;
-
- emit_insn ((*genfunc) (operands[0], operands[1]));
- DONE;
- }
-}")
-
-(define_insn "movdi_push_nomove"
- [(set (match_operand:DI 0 "push_operand" "=<")
- (match_operand:DI 1 "general_operand" "roiF"))]
- "!TARGET_MOVE"
- "* return output_move_double (operands);")
-
-(define_insn "movdi_push"
+(define_insn ""
[(set (match_operand:DI 0 "push_operand" "=<,<,<,<")
(match_operand:DI 1 "general_operand" "riF,o,o,o"))
(clobber (match_scratch:SI 2 "=X,&r,&r,X"))
""
"*
{
- if (GET_CODE (operands[1]) != MEM
- || GET_CODE (operands[2]) != REG)
+ if (GET_CODE (operands[1]) != MEM)
return output_move_double (operands);
else
- {
- rtx low[1], high[1], xop[4];
-
- split_di (&operands[1], 1, low, high);
- xop[0] = operands[2];
- xop[1] = operands[3];
- xop[2] = high[0];
- xop[3] = low[0];
-
- if (GET_CODE (operands[3]) == REG)
- { /* 2 scratch registers available */
- output_asm_insn (AS2 (mov%L0,%2,%0), xop);
- output_asm_insn (AS2 (mov%L0,%3,%1), xop);
- output_asm_insn (AS1 (push%L0,%0), xop);
- output_asm_insn (AS1 (push%L0,%1), xop);
- }
- else
- { /* 1 scratch register */
- output_asm_insn (AS2 (mov%L0,%2,%0), xop);
- output_asm_insn (AS1 (push%L0,%0), xop);
-
- /* account for push above */
- if (reg_mentioned_p (stack_pointer_rtx, XEXP (xop[3], 0)))
- xop[3] = adj_offsettable_operand (xop[3], 4);
-
- output_asm_insn (AS2 (mov%L0,%3,%0), xop);
- output_asm_insn (AS1 (push%L0,%0), xop);
- }
-
- RET;
- }
+ return output_move_pushmem (operands, insn, GET_MODE_SIZE (DImode), 2, 4);
}")
-(define_insn "movdi_mem"
- [(set (match_operand:DI 0 "memory_operand" "=o,o")
- (match_operand:DI 1 "memory_operand" "o,o"))
- (clobber (match_scratch:SI 2 "=&r,&r"))
- (clobber (match_scratch:SI 3 "=&r,X"))]
+(define_insn "movdi"
+ [(set (match_operand:DI 0 "general_operand" "=o,o,r,rm")
+ (match_operand:DI 1 "general_operand" "o,o,m,riF"))
+ (clobber (match_scratch:SI 2 "=&r,&r,X,X"))
+ (clobber (match_scratch:SI 3 "=&r,X,X,X"))]
""
"*
{
rtx low[2], high[2], xop[6];
- split_di (operands, 2, low, high);
- xop[0] = operands[2];
- xop[1] = operands[3];
- xop[2] = high[0];
- xop[3] = high[1];
- xop[4] = low[0];
- xop[5] = low[1];
- if (GET_CODE (operands[3]) == REG)
- { /* 2 scratch registers available */
- output_asm_insn (AS2 (mov%L0,%5,%0), xop);
- output_asm_insn (AS2 (mov%L0,%3,%1), xop);
- output_asm_insn (AS2 (mov%L0,%0,%4), xop);
- output_asm_insn (AS2 (mov%L0,%1,%2), xop);
- }
+ if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
+ return output_move_double (operands);
else
- { /* 1 scratch register */
- output_asm_insn (AS2 (mov%L0,%5,%0), xop);
- output_asm_insn (AS2 (mov%L0,%0,%4), xop);
- output_asm_insn (AS2 (mov%L0,%3,%0), xop);
- output_asm_insn (AS2 (mov%L0,%0,%2), xop);
- }
-
- RET;
+ return output_move_memory (operands, insn, GET_MODE_SIZE (DImode), 2, 4);
}")
-(define_insn "movdi_normal"
- [(set (match_operand:DI 0 "general_operand" "=r,rm")
- (match_operand:DI 1 "general_operand" "m,riF"))]
- "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM) || (GET_CODE (operands[1]) != MEM)"
- "* return output_move_double (operands);")
-
\f
;;- conversion instructions
;;- NONE
"!HALF_PIC_P ()"
"call %P1")
+;; Call subroutine returning any type.
+
(define_expand "untyped_call"
- [(parallel [(call (match_operand:QI 0 "indirect_operand" "")
+ [(parallel [(call (match_operand 0 "" "")
(const_int 0))
- (match_operand:BLK 1 "memory_operand" "")
+ (match_operand 1 "" "")
(match_operand 2 "" "")])]
""
"
{
- rtx addr;
+ int i;
- if (flag_pic)
- current_function_uses_pic_offset_table = 1;
-
- /* With half-pic, force the address into a register. */
- addr = XEXP (operands[0], 0);
- if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr))
- XEXP (operands[0], 0) = force_reg (Pmode, addr);
-
- operands[1] = change_address (operands[1], DImode, XEXP (operands[1], 0));
- if (! expander_call_insn_operand (operands[1], QImode))
- operands[1]
- = change_address (operands[1], VOIDmode,
- copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
-}")
+ emit_call_insn (gen_call (operands[0], const0_rtx, NULL, const0_rtx));
-(define_insn ""
- [(call (match_operand:QI 0 "call_insn_operand" "m")
- (const_int 0))
- (match_operand:DI 1 "memory_operand" "o")
- (match_operand 2 "" "")]
- ""
- "*
-{
- rtx addr = operands[1];
-
- if (GET_CODE (operands[0]) == MEM
- && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
+ for (i = 0; i < XVECLEN (operands[2], 0); i++)
{
- operands[0] = XEXP (operands[0], 0);
- output_asm_insn (AS1 (call,%*%0), operands);
+ rtx set = XVECEXP (operands[2], 0, i);
+ emit_move_insn (SET_DEST (set), SET_SRC (set));
}
- else
- output_asm_insn (AS1 (call,%P0), operands);
-
- operands[2] = gen_rtx (REG, SImode, 0);
- output_asm_insn (AS2 (mov%L2,%2,%1), operands);
-
- operands[2] = gen_rtx (REG, SImode, 1);
- operands[1] = adj_offsettable_operand (addr, 4);
- output_asm_insn (AS2 (mov%L2,%2,%1), operands);
-
- operands[1] = adj_offsettable_operand (addr, 8);
- return AS1 (fnsave,%1);
-}")
-
-(define_insn ""
- [(call (mem:QI (match_operand:SI 0 "symbolic_operand" ""))
- (const_int 0))
- (match_operand:DI 1 "memory_operand" "o")
- (match_operand 2 "" "")]
- "!HALF_PIC_P ()"
- "*
-{
- rtx addr = operands[1];
-
- output_asm_insn (AS1 (call,%P0), operands);
-
- operands[2] = gen_rtx (REG, SImode, 0);
- output_asm_insn (AS2 (mov%L2,%2,%1), operands);
-
- operands[2] = gen_rtx (REG, SImode, 1);
- operands[1] = adj_offsettable_operand (addr, 4);
- output_asm_insn (AS2 (mov%L2,%2,%1), operands);
-
- operands[1] = adj_offsettable_operand (addr, 8);
- return AS1 (fnsave,%1);
-}")
-
-;; We use fnsave and frstor to save and restore the floating point result.
-;; These are expensive instructions and require a large space to save the
-;; FPU state. An more complicated alternative is to use fnstenv to store
-;; the FPU environment and test whether the stack top is valid. Store the
-;; result of the test, and if it is valid, pop and save the value. The
-;; untyped_return would check the test and optionally push the saved value.
-
-(define_expand "untyped_return"
- [(match_operand:BLK 0 "memory_operand" "")
- (match_operand 1 "" "")]
- ""
- "
-{
- rtx valreg1 = gen_rtx (REG, SImode, 0);
- rtx valreg2 = gen_rtx (REG, SImode, 1);
- rtx result = operands[0];
-
- /* Restore the FPU state. */
- emit_insn (gen_update_return (change_address (result, SImode,
- plus_constant (XEXP (result, 0),
- 8))));
- /* Reload the function value registers. */
- emit_move_insn (valreg1, change_address (result, SImode, XEXP (result, 0)));
- emit_move_insn (valreg2,
- change_address (result, SImode,
- plus_constant (XEXP (result, 0), 4)));
-
- /* Put USE insns before the return. */
- emit_insn (gen_rtx (USE, VOIDmode, valreg1));
- emit_insn (gen_rtx (USE, VOIDmode, valreg2));
-
- /* Construct the return. */
- expand_null_return ();
+ /* The optimizer does not know that the call sets the function value
+ registers we stored in the result block. We avoid problems by
+ claiming that all hard registers are used and clobbered at this
+ point. */
+ emit_insn (gen_blockage ());
DONE;
}")
-(define_insn "update_return"
- [(unspec:SI [(match_operand:SI 0 "memory_operand" "m")] 0)]
+;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and
+;; all of memory. This blocks insns from being moved across this point.
+
+(define_insn "blockage"
+ [(unspec_volatile [(const_int 0)] 0)]
""
- "frstor %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.