struct machine_function GTY(())
{
int accesses_prev_frame;
- bool incoming_a7_copied;
+ bool need_a7_copy;
+ bool vararg_a7;
+ rtx set_frame_ptr_insn;
};
/* Vector, indexed by hard register number, which contains 1 for a
}
}
- if (!(reload_in_progress | reload_completed))
- {
- if (!xtensa_valid_move (mode, operands))
- operands[1] = force_reg (mode, operands[1]);
+ if (!(reload_in_progress | reload_completed)
+ && !xtensa_valid_move (mode, operands))
+ operands[1] = force_reg (mode, operands[1]);
- if (xtensa_copy_incoming_a7 (operands, mode))
- return 1;
- }
+ operands[1] = xtensa_copy_incoming_a7 (operands[1]);
/* During reload we don't want to emit (subreg:X (mem:Y)) since that
instruction won't be recognized after reload, so we remove the
}
-/* Check if this move is copying an incoming argument in a7. If so,
- emit the move, followed by the special "set_frame_ptr"
- unspec_volatile insn, at the very beginning of the function. This
- is necessary because the register allocator will ignore conflicts
- with a7 and may assign some other pseudo to a7. If that pseudo was
- assigned prior to this move, it would clobber the incoming argument
- in a7. By copying the argument out of a7 as the very first thing,
- and then immediately following that with an unspec_volatile to keep
- the scheduler away, we should avoid any problems. */
+/* Check if an incoming argument in a7 is expected to be used soon and
+ if OPND is a register or register pair that includes a7. If so,
+ create a new pseudo and copy a7 into that pseudo at the very
+ beginning of the function, followed by the special "set_frame_ptr"
+ unspec_volatile insn. The return value is either the original
+ operand, if it is not a7, or the new pseudo containing a copy of
+ the incoming argument. This is necessary because the register
+ allocator will ignore conflicts with a7 and may either assign some
+ other pseudo to a7 or use a7 as the hard_frame_pointer, clobbering
+ the incoming argument in a7. By copying the argument out of a7 as
+ the very first thing, and then immediately following that with an
+ unspec_volatile to keep the scheduler away, we should avoid any
+ problems. Putting the set_frame_ptr insn at the beginning, with
+ only the a7 copy before it, also makes it easier for the prologue
+ expander to initialize the frame pointer after the a7 copy and to
+ fix up the a7 copy to use the stack pointer instead of the frame
+ pointer. */
-bool
-xtensa_copy_incoming_a7 (rtx *operands, enum machine_mode mode)
+rtx
+xtensa_copy_incoming_a7 (rtx opnd)
{
- if (a7_overlap_mentioned_p (operands[1])
- && !cfun->machine->incoming_a7_copied)
+ rtx entry_insns = 0;
+ rtx reg, tmp;
+ enum machine_mode mode;
+
+ if (!cfun->machine->need_a7_copy)
+ return opnd;
+
+ /* This function should never be called again once a7 has been copied. */
+ if (cfun->machine->set_frame_ptr_insn)
+ abort ();
+
+ mode = GET_MODE (opnd);
+
+ /* The operand using a7 may come in a later instruction, so just return
+ the original operand if it doesn't use a7. */
+ reg = opnd;
+ if (GET_CODE (reg) == SUBREG)
{
- rtx mov, src;
-
- /* Despite defining SPLIT_COMPLEX_ARGS, complex function
- arguments may still appear if they are wrapped in a struct.
- For CQImode and CHImode arguments, this results in a move
- with a source operand of the form: "(subreg:SI (reg:CHI a7)
- 0)". The subreg is later removed by the reload pass,
- resulting in the RTL for a7 being regenerated using
- hard_frame_pointer_rtx, and making it impossible for us to
- distinguish the function argument. Detect this here when
- generating the RTL and remove the subreg immediately so that
- reload won't mess it up. */
- src = operands[1];
- if (GET_CODE (src) == SUBREG
- && GET_CODE (SUBREG_REG (src)) == REG
- && REGNO (SUBREG_REG (src)) == A7_REG
- && SUBREG_BYTE (src) == 0
- && (GET_MODE (SUBREG_REG (src)) == CHImode
- || GET_MODE (SUBREG_REG (src)) == CQImode))
- operands[1] = gen_raw_REG (mode, A7_REG);
+ if (SUBREG_BYTE (reg) != 0)
+ abort ();
+ reg = SUBREG_REG (reg);
+ }
+ if (GET_CODE (reg) != REG
+ || REGNO (reg) > A7_REG
+ || REGNO (reg) + HARD_REGNO_NREGS (A7_REG, mode) <= A7_REG)
+ return opnd;
- switch (mode)
- {
- case DFmode:
- mov = gen_movdf_internal (operands[0], operands[1]);
- break;
- case SFmode:
- mov = gen_movsf_internal (operands[0], operands[1]);
- break;
- case DImode:
- mov = gen_movdi_internal (operands[0], operands[1]);
- break;
- case SImode:
- mov = gen_movsi_internal (operands[0], operands[1]);
- break;
- case HImode:
- mov = gen_movhi_internal (operands[0], operands[1]);
- break;
- case QImode:
- mov = gen_movqi_internal (operands[0], operands[1]);
- break;
- default:
- abort ();
- }
+ /* 1-word args will always be in a7; 2-word args in a6/a7. */
+ if (REGNO (reg) + HARD_REGNO_NREGS (A7_REG, mode) - 1 != A7_REG)
+ abort ();
- /* Insert the instructions before any other argument copies.
- (The set_frame_ptr insn comes _after_ the move, so push it
- out first.) */
- push_topmost_sequence ();
- emit_insn_after (gen_set_frame_ptr (), get_insns ());
- emit_insn_after (mov, get_insns ());
- pop_topmost_sequence ();
+ cfun->machine->need_a7_copy = false;
- /* Ideally the incoming argument in a7 would only be copied
- once, since propagating a7 into the body of a function
- will almost certainly lead to errors. However, there is
- at least one harmless case (in GCSE) where the original
- copy from a7 is changed to copy into a new pseudo. Thus,
- we use a flag to only do this special treatment for the
- first copy of a7. */
+ /* Copy a7 to a new pseudo at the function entry. Use gen_raw_REG to
+ create the REG for a7 so that hard_frame_pointer_rtx is not used. */
- cfun->machine->incoming_a7_copied = true;
+ push_to_sequence (entry_insns);
+ tmp = gen_reg_rtx (mode);
- return 1;
+ switch (mode)
+ {
+ case DFmode:
+ case DImode:
+ emit_insn (gen_movsi_internal (gen_rtx_SUBREG (SImode, tmp, 0),
+ gen_rtx_REG (SImode, A7_REG - 1)));
+ emit_insn (gen_movsi_internal (gen_rtx_SUBREG (SImode, tmp, 4),
+ gen_raw_REG (SImode, A7_REG)));
+ break;
+ case SFmode:
+ emit_insn (gen_movsf_internal (tmp, gen_raw_REG (mode, A7_REG)));
+ break;
+ case SImode:
+ emit_insn (gen_movsi_internal (tmp, gen_raw_REG (mode, A7_REG)));
+ break;
+ case HImode:
+ emit_insn (gen_movhi_internal (tmp, gen_raw_REG (mode, A7_REG)));
+ break;
+ case QImode:
+ emit_insn (gen_movqi_internal (tmp, gen_raw_REG (mode, A7_REG)));
+ break;
+ default:
+ abort ();
}
- return 0;
+ cfun->machine->set_frame_ptr_insn = emit_insn (gen_set_frame_ptr ());
+ entry_insns = get_insns ();
+ end_sequence ();
+
+ if (cfun->machine->vararg_a7)
+ {
+ /* This is called from within builtin_savereg, so we're already
+ inside a start_sequence that will be placed at the start of
+ the function. */
+ emit_insn (entry_insns);
+ }
+ else
+ {
+ /* Put entry_insns after the NOTE that starts the function. If
+ this is inside a start_sequence, make the outer-level insn
+ chain current, so the code is placed at the start of the
+ function. */
+ push_topmost_sequence ();
+ emit_insn_after (entry_insns, get_insns ());
+ pop_topmost_sequence ();
+ }
+
+ return tmp;
}
/* Initialize CUMULATIVE_ARGS for a function. */
void
-init_cumulative_args (CUMULATIVE_ARGS *cum,
- tree fntype ATTRIBUTE_UNUSED,
- rtx libname ATTRIBUTE_UNUSED)
+init_cumulative_args (CUMULATIVE_ARGS *cum, int incoming)
{
cum->arg_words = 0;
+ cum->incoming = incoming;
}
int regbase, words, max;
int *arg_words;
int regno;
- enum machine_mode result_mode;
arg_words = &cum->arg_words;
regbase = (incoming_p ? GP_ARG_FIRST : GP_OUTGOING_ARG_FIRST);
return (rtx)0;
regno = regbase + *arg_words;
- result_mode = (mode == BLKmode ? TYPE_MODE (type) : mode);
-
- /* We need to make sure that references to a7 are represented with
- rtx that is not equal to hard_frame_pointer_rtx. For multi-word
- modes for which we don't define move patterns, we can't control
- the expansion unless we explicitly list the individual registers
- in a PARALLEL. Likewise, a single-word BLKmode argument passed
- in a7 must be wrapped in a PARALLEL to avoid code that takes the
- register number and builds a new REG. This is extremely fragile
- but seems to be the best solution for now. */
-
- if ((mode != DImode && mode != DFmode
- && regno < A7_REG
- && regno + words > A7_REG)
- || (mode == BLKmode && regno == A7_REG))
- {
- rtx result;
- int n;
- result = gen_rtx_PARALLEL (result_mode, rtvec_alloc (words));
- for (n = 0; n < words; n++)
- {
- XVECEXP (result, 0, n) =
- gen_rtx_EXPR_LIST (VOIDmode,
- gen_raw_REG (SImode, regno + n),
- GEN_INT (n * UNITS_PER_WORD));
- }
- return result;
- }
+ if (cum->incoming && regno <= A7_REG && regno + words > A7_REG)
+ cfun->machine->need_a7_copy = true;
- return gen_raw_REG (result_mode, regno);
+ return gen_rtx_REG (mode, regno);
}
if (frame_pointer_needed)
{
- rtx first, insn, set_frame_ptr_insn = 0;
-
- push_topmost_sequence ();
- first = get_insns ();
- pop_topmost_sequence ();
-
- /* Search all instructions, looking for the insn that sets up the
- frame pointer. This search will fail if the function does not
- have an incoming argument in $a7, but in that case, we can just
- set up the frame pointer at the very beginning of the
- function. */
-
- for (insn = first; insn; insn = NEXT_INSN (insn))
+ if (cfun->machine->set_frame_ptr_insn)
{
- rtx pat;
-
- if (!INSN_P (insn))
- continue;
+ rtx first, insn;
- pat = PATTERN (insn);
- if (GET_CODE (pat) == SET
- && GET_CODE (SET_SRC (pat)) == UNSPEC_VOLATILE
- && (XINT (SET_SRC (pat), 1) == UNSPECV_SET_FP))
- {
- set_frame_ptr_insn = insn;
- break;
- }
- }
+ push_topmost_sequence ();
+ first = get_insns ();
+ pop_topmost_sequence ();
- if (set_frame_ptr_insn)
- {
/* For all instructions prior to set_frame_ptr_insn, replace
hard_frame_pointer references with stack_pointer. */
for (insn = first;
- insn != set_frame_ptr_insn;
+ insn != cfun->machine->set_frame_ptr_insn;
insn = NEXT_INSN (insn))
{
if (INSN_P (insn))
rtx gp_regs, dest;
int arg_words = current_function_arg_words;
int gp_left = MAX_ARGS_IN_REGISTERS - arg_words;
- int i;
- if (gp_left == 0)
+ if (gp_left <= 0)
return const0_rtx;
/* Allocate the general-purpose register space. */
dest = change_address (gp_regs, SImode,
plus_constant (XEXP (gp_regs, 0),
arg_words * UNITS_PER_WORD));
-
- /* Note: Don't use move_block_from_reg() here because the incoming
- argument in a7 cannot be represented by hard_frame_pointer_rtx.
- Instead, call gen_raw_REG() directly so that we get a distinct
- instance of (REG:SI 7). */
- for (i = 0; i < gp_left; i++)
- {
- emit_move_insn (operand_subword (dest, i, 1, BLKmode),
- gen_raw_REG (SImode, GP_ARG_FIRST + arg_words + i));
- }
+ cfun->machine->need_a7_copy = true;
+ cfun->machine->vararg_a7 = true;
+ move_block_from_reg (GP_ARG_FIRST + arg_words, dest, gp_left);
return XEXP (gp_regs, 0);
}
}
-/* A customized version of reg_overlap_mentioned_p that only looks for
- references to a7 (as opposed to hard_frame_pointer_rtx). */
-
-int
-a7_overlap_mentioned_p (rtx x)
-{
- int i, j;
- unsigned int x_regno;
- const char *fmt;
-
- if (GET_CODE (x) == REG)
- {
- x_regno = REGNO (x);
- return (x != hard_frame_pointer_rtx
- && x_regno < A7_REG + 1
- && x_regno + HARD_REGNO_NREGS (A7_REG, GET_MODE (x)) > A7_REG);
- }
-
- if (GET_CODE (x) == SUBREG
- && GET_CODE (SUBREG_REG (x)) == REG
- && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER)
- {
- x_regno = subreg_regno (x);
- return (SUBREG_REG (x) != hard_frame_pointer_rtx
- && x_regno < A7_REG + 1
- && x_regno + HARD_REGNO_NREGS (A7_REG, GET_MODE (x)) > A7_REG);
- }
-
- /* X does not match, so try its subexpressions. */
- fmt = GET_RTX_FORMAT (GET_CODE (x));
- for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
- {
- if (fmt[i] == 'e')
- {
- if (a7_overlap_mentioned_p (XEXP (x, i)))
- return 1;
- }
- else if (fmt[i] == 'E')
- {
- for (j = XVECLEN (x, i) - 1; j >=0; j--)
- if (a7_overlap_mentioned_p (XVECEXP (x, i, j)))
- return 1;
- }
- }
-
- return 0;
-}
-
-
/* Some Xtensa targets support multiple bss sections. If the section
name ends with ".bss", add SECTION_BSS to the flags. */
#define FUNCTION_ARG_REGNO_P(N) \
((N) >= GP_OUTGOING_ARG_FIRST && (N) <= GP_OUTGOING_ARG_LAST)
-/* Define a data type for recording info about an argument list
- during the scan of that argument list. This data type should
- hold all necessary information about the function itself
- and about the args processed so far, enough to enable macros
- such as FUNCTION_ARG to determine where the next arg should go. */
-typedef struct xtensa_args {
- int arg_words; /* # total words the arguments take */
+/* Record the number of argument words seen so far, along with a flag to
+ indicate whether these are incoming arguments. (FUNCTION_INCOMING_ARG
+ is used for both incoming and outgoing args, so a separate flag is
+ needed. */
+typedef struct xtensa_args
+{
+ int arg_words;
+ int incoming;
} CUMULATIVE_ARGS;
-/* Initialize a variable CUM of type CUMULATIVE_ARGS
- for a call to a function whose data type is FNTYPE.
- For a library call, FNTYPE is 0. */
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \
- init_cumulative_args (&CUM, FNTYPE, LIBNAME)
+ init_cumulative_args (&CUM, 0)
#define INIT_CUMULATIVE_INCOMING_ARGS(CUM, FNTYPE, LIBNAME) \
- init_cumulative_args (&CUM, FNTYPE, LIBNAME)
+ init_cumulative_args (&CUM, 1)
/* Update the data in CUM to advance over an argument
of mode MODE and data type TYPE.
/* Pass complex arguments independently. */
#define SPLIT_COMPLEX_ARGS 1
-/* Because Xtensa's function_arg() wraps BLKmode arguments passed in
- a7 inside a PARALLEL, BLOCK_REG_PADDING needs to be defined
- to get emit_group_store to do the right thing. */
-#define BLOCK_REG_PADDING(MODE, TYPE, FIRST) \
- FUNCTION_ARG_PADDING (MODE, TYPE)
-
/* Profiling Xtensa code is typically done with the built-in profiling
feature of Tensilica's instruction set simulator, which does not
require any compiler support. Profiling code on a real (i.e.,
/* Prefer word-sized loads. */
#define SLOW_BYTE_ACCESS 1
-/* ??? Xtensa doesn't have any instructions that set integer values
- based on the results of comparisons, but the simplification code in
- the combiner also uses STORE_FLAG_VALUE. The default value (1) is
- fine for us, but (-1) might be better. */
-
/* Shift instructions ignore all but the low-order few bits. */
#define SHIFT_COUNT_TRUNCATED 1
if (CONSTANT_P (operands[1]) && !TARGET_CONST16)
operands[1] = force_const_mem (DImode, operands[1]);
- if (!(reload_in_progress | reload_completed))
- {
- if (!register_operand (operands[0], DImode)
- && !register_operand (operands[1], DImode))
- operands[1] = force_reg (DImode, operands[1]);
+ if (!register_operand (operands[0], DImode)
+ && !register_operand (operands[1], DImode))
+ operands[1] = force_reg (DImode, operands[1]);
- if (xtensa_copy_incoming_a7 (operands, DImode))
- DONE;
- }
+ operands[1] = xtensa_copy_incoming_a7 (operands[1]);
})
(define_insn_and_split "movdi_internal"
if (!TARGET_CONST16 && CONSTANT_P (operands[1]))
operands[1] = force_const_mem (SFmode, operands[1]);
- if (!(reload_in_progress | reload_completed))
- {
- if ((!register_operand (operands[0], SFmode)
- && !register_operand (operands[1], SFmode))
- || (FP_REG_P (xt_true_regnum (operands[0]))
- && (constantpool_mem_p (operands[1])
- || CONSTANT_P (operands[1]))))
- operands[1] = force_reg (SFmode, operands[1]);
-
- if (xtensa_copy_incoming_a7 (operands, SFmode))
- DONE;
- }
+ if ((!register_operand (operands[0], SFmode)
+ && !register_operand (operands[1], SFmode))
+ || (FP_REG_P (xt_true_regnum (operands[0]))
+ && !(reload_in_progress | reload_completed)
+ && (constantpool_mem_p (operands[1])
+ || CONSTANT_P (operands[1]))))
+ operands[1] = force_reg (SFmode, operands[1]);
+
+ operands[1] = xtensa_copy_incoming_a7 (operands[1]);
})
(define_insn "movsf_internal"
if (CONSTANT_P (operands[1]) && !TARGET_CONST16)
operands[1] = force_const_mem (DFmode, operands[1]);
- if (!(reload_in_progress | reload_completed))
- {
- if (!register_operand (operands[0], DFmode)
- && !register_operand (operands[1], DFmode))
- operands[1] = force_reg (DFmode, operands[1]);
+ if (!register_operand (operands[0], DFmode)
+ && !register_operand (operands[1], DFmode))
+ operands[1] = force_reg (DFmode, operands[1]);
- if (xtensa_copy_incoming_a7 (operands, DFmode))
- DONE;
- }
+ operands[1] = xtensa_copy_incoming_a7 (operands[1]);
})
(define_insn_and_split "movdf_internal"
\f
;; Shift instructions.
-(define_insn "ashlsi3"
+(define_expand "ashlsi3"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (ashift:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "arith_operand" "")))]
+ ""
+{
+ operands[1] = xtensa_copy_incoming_a7 (operands[1]);
+})
+
+(define_insn "ashlsi3_internal"
[(set (match_operand:SI 0 "register_operand" "=a,a")
(ashift:SI (match_operand:SI 1 "register_operand" "r,r")
(match_operand:SI 2 "arith_operand" "J,r")))]