/* Subroutines used for code generation on the Lattice Mico32 architecture.
|
/* Subroutines used for code generation on the Lattice Mico32 architecture.
|
Contributed by Jon Beniston <jon@beniston.com>
|
Contributed by Jon Beniston <jon@beniston.com>
|
|
|
Copyright (C) 2009 Free Software Foundation, Inc.
|
Copyright (C) 2009 Free Software Foundation, Inc.
|
|
|
This file is part of GCC.
|
This file is part of GCC.
|
|
|
GCC is free software; you can redistribute it and/or modify it
|
GCC is free software; you can redistribute it and/or modify it
|
under the terms of the GNU General Public License as published
|
under the terms of the GNU General Public License as published
|
by the Free Software Foundation; either version 3, or (at your
|
by the Free Software Foundation; either version 3, or (at your
|
option) any later version.
|
option) any later version.
|
|
|
GCC is distributed in the hope that it will be useful, but WITHOUT
|
GCC is distributed in the hope that it will be useful, but WITHOUT
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
License for more details.
|
License for more details.
|
|
|
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
along with GCC; see the file COPYING3. If not see
|
along with GCC; see the file COPYING3. If not see
|
<http://www.gnu.org/licenses/>. */
|
<http://www.gnu.org/licenses/>. */
|
|
|
#include "config.h"
|
#include "config.h"
|
#include "system.h"
|
#include "system.h"
|
#include "coretypes.h"
|
#include "coretypes.h"
|
#include "tm.h"
|
#include "tm.h"
|
#include "rtl.h"
|
#include "rtl.h"
|
#include "regs.h"
|
#include "regs.h"
|
#include "hard-reg-set.h"
|
#include "hard-reg-set.h"
|
#include "basic-block.h"
|
#include "basic-block.h"
|
#include "real.h"
|
#include "real.h"
|
#include "insn-config.h"
|
#include "insn-config.h"
|
#include "conditions.h"
|
#include "conditions.h"
|
#include "insn-flags.h"
|
#include "insn-flags.h"
|
#include "insn-attr.h"
|
#include "insn-attr.h"
|
#include "insn-codes.h"
|
#include "insn-codes.h"
|
#include "recog.h"
|
#include "recog.h"
|
#include "output.h"
|
#include "output.h"
|
#include "tree.h"
|
#include "tree.h"
|
#include "expr.h"
|
#include "expr.h"
|
#include "flags.h"
|
#include "flags.h"
|
#include "reload.h"
|
#include "reload.h"
|
#include "tm_p.h"
|
#include "tm_p.h"
|
#include "function.h"
|
#include "function.h"
|
#include "toplev.h"
|
#include "toplev.h"
|
#include "optabs.h"
|
#include "optabs.h"
|
#include "libfuncs.h"
|
#include "libfuncs.h"
|
#include "ggc.h"
|
#include "ggc.h"
|
#include "target.h"
|
#include "target.h"
|
#include "target-def.h"
|
#include "target-def.h"
|
#include "langhooks.h"
|
#include "langhooks.h"
|
#include "tm-constrs.h"
|
#include "tm-constrs.h"
|
#include "df.h"
|
#include "df.h"
|
|
|
struct lm32_frame_info
|
struct lm32_frame_info
|
{
|
{
|
HOST_WIDE_INT total_size; /* number of bytes of entire frame. */
|
HOST_WIDE_INT total_size; /* number of bytes of entire frame. */
|
HOST_WIDE_INT callee_size; /* number of bytes to save callee saves. */
|
HOST_WIDE_INT callee_size; /* number of bytes to save callee saves. */
|
HOST_WIDE_INT pretend_size; /* number of bytes we pretend caller did. */
|
HOST_WIDE_INT pretend_size; /* number of bytes we pretend caller did. */
|
HOST_WIDE_INT args_size; /* number of bytes for outgoing arguments. */
|
HOST_WIDE_INT args_size; /* number of bytes for outgoing arguments. */
|
HOST_WIDE_INT locals_size; /* number of bytes for local variables. */
|
HOST_WIDE_INT locals_size; /* number of bytes for local variables. */
|
unsigned int reg_save_mask; /* mask of saved registers. */
|
unsigned int reg_save_mask; /* mask of saved registers. */
|
};
|
};
|
|
|
/* Prototypes for static functions. */
|
/* Prototypes for static functions. */
|
static rtx emit_add (rtx dest, rtx src0, rtx src1);
|
static rtx emit_add (rtx dest, rtx src0, rtx src1);
|
static void expand_save_restore (struct lm32_frame_info *info, int op);
|
static void expand_save_restore (struct lm32_frame_info *info, int op);
|
static void stack_adjust (HOST_WIDE_INT amount);
|
static void stack_adjust (HOST_WIDE_INT amount);
|
static bool lm32_in_small_data_p (const_tree);
|
static bool lm32_in_small_data_p (const_tree);
|
static void lm32_setup_incoming_varargs (CUMULATIVE_ARGS * cum,
|
static void lm32_setup_incoming_varargs (CUMULATIVE_ARGS * cum,
|
enum machine_mode mode, tree type,
|
enum machine_mode mode, tree type,
|
int *pretend_size, int no_rtl);
|
int *pretend_size, int no_rtl);
|
static bool lm32_rtx_costs (rtx x, int code, int outer_code, int *total,
|
static bool lm32_rtx_costs (rtx x, int code, int outer_code, int *total,
|
bool speed);
|
bool speed);
|
static bool lm32_can_eliminate (const int, const int);
|
static bool lm32_can_eliminate (const int, const int);
|
static bool
|
static bool
|
lm32_legitimate_address_p (enum machine_mode mode, rtx x, bool strict);
|
lm32_legitimate_address_p (enum machine_mode mode, rtx x, bool strict);
|
static HOST_WIDE_INT lm32_compute_frame_size (int size);
|
static HOST_WIDE_INT lm32_compute_frame_size (int size);
|
|
|
#undef TARGET_ADDRESS_COST
|
#undef TARGET_ADDRESS_COST
|
#define TARGET_ADDRESS_COST hook_int_rtx_bool_0
|
#define TARGET_ADDRESS_COST hook_int_rtx_bool_0
|
#undef TARGET_RTX_COSTS
|
#undef TARGET_RTX_COSTS
|
#define TARGET_RTX_COSTS lm32_rtx_costs
|
#define TARGET_RTX_COSTS lm32_rtx_costs
|
#undef TARGET_IN_SMALL_DATA_P
|
#undef TARGET_IN_SMALL_DATA_P
|
#define TARGET_IN_SMALL_DATA_P lm32_in_small_data_p
|
#define TARGET_IN_SMALL_DATA_P lm32_in_small_data_p
|
#undef TARGET_PROMOTE_FUNCTION_MODE
|
#undef TARGET_PROMOTE_FUNCTION_MODE
|
#define TARGET_PROMOTE_FUNCTION_MODE default_promote_function_mode_always_promote
|
#define TARGET_PROMOTE_FUNCTION_MODE default_promote_function_mode_always_promote
|
#undef TARGET_SETUP_INCOMING_VARARGS
|
#undef TARGET_SETUP_INCOMING_VARARGS
|
#define TARGET_SETUP_INCOMING_VARARGS lm32_setup_incoming_varargs
|
#define TARGET_SETUP_INCOMING_VARARGS lm32_setup_incoming_varargs
|
#undef TARGET_PROMOTE_PROTOTYPES
|
#undef TARGET_PROMOTE_PROTOTYPES
|
#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
|
#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
|
#undef TARGET_MIN_ANCHOR_OFFSET
|
#undef TARGET_MIN_ANCHOR_OFFSET
|
#define TARGET_MIN_ANCHOR_OFFSET -0x8000
|
#define TARGET_MIN_ANCHOR_OFFSET -0x8000
|
#undef TARGET_MAX_ANCHOR_OFFSET
|
#undef TARGET_MAX_ANCHOR_OFFSET
|
#define TARGET_MAX_ANCHOR_OFFSET 0x7fff
|
#define TARGET_MAX_ANCHOR_OFFSET 0x7fff
|
#undef TARGET_CAN_ELIMINATE
|
#undef TARGET_CAN_ELIMINATE
|
#define TARGET_CAN_ELIMINATE lm32_can_eliminate
|
#define TARGET_CAN_ELIMINATE lm32_can_eliminate
|
#undef TARGET_LEGITIMATE_ADDRESS_P
|
#undef TARGET_LEGITIMATE_ADDRESS_P
|
#define TARGET_LEGITIMATE_ADDRESS_P lm32_legitimate_address_p
|
#define TARGET_LEGITIMATE_ADDRESS_P lm32_legitimate_address_p
|
|
|
struct gcc_target targetm = TARGET_INITIALIZER;
|
struct gcc_target targetm = TARGET_INITIALIZER;
|
|
|
/* Current frame information calculated by lm32_compute_frame_size. */
|
/* Current frame information calculated by lm32_compute_frame_size. */
|
static struct lm32_frame_info current_frame_info;
|
static struct lm32_frame_info current_frame_info;
|
|
|
/* Return non-zero if the given return type should be returned in memory. */
|
/* Return non-zero if the given return type should be returned in memory. */
|
|
|
int
|
int
|
lm32_return_in_memory (tree type)
|
lm32_return_in_memory (tree type)
|
{
|
{
|
HOST_WIDE_INT size;
|
HOST_WIDE_INT size;
|
|
|
if (!AGGREGATE_TYPE_P (type))
|
if (!AGGREGATE_TYPE_P (type))
|
{
|
{
|
/* All simple types are returned in registers. */
|
/* All simple types are returned in registers. */
|
return 0;
|
return 0;
|
}
|
}
|
|
|
size = int_size_in_bytes (type);
|
size = int_size_in_bytes (type);
|
if (size >= 0 && size <= UNITS_PER_WORD)
|
if (size >= 0 && size <= UNITS_PER_WORD)
|
{
|
{
|
/* If it can fit in one register. */
|
/* If it can fit in one register. */
|
return 0;
|
return 0;
|
}
|
}
|
|
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Generate an emit a word sized add instruction. */
|
/* Generate an emit a word sized add instruction. */
|
|
|
static rtx
|
static rtx
|
emit_add (rtx dest, rtx src0, rtx src1)
|
emit_add (rtx dest, rtx src0, rtx src1)
|
{
|
{
|
rtx insn;
|
rtx insn;
|
insn = emit_insn (gen_addsi3 (dest, src0, src1));
|
insn = emit_insn (gen_addsi3 (dest, src0, src1));
|
return insn;
|
return insn;
|
}
|
}
|
|
|
/* Generate the code to compare (and possibly branch) two integer values
|
/* Generate the code to compare (and possibly branch) two integer values
|
TEST_CODE is the comparison code we are trying to emulate
|
TEST_CODE is the comparison code we are trying to emulate
|
(or implement directly)
|
(or implement directly)
|
RESULT is where to store the result of the comparison,
|
RESULT is where to store the result of the comparison,
|
or null to emit a branch
|
or null to emit a branch
|
CMP0 CMP1 are the two comparison operands
|
CMP0 CMP1 are the two comparison operands
|
DESTINATION is the destination of the branch, or null to only compare
|
DESTINATION is the destination of the branch, or null to only compare
|
*/
|
*/
|
|
|
static void
|
static void
|
gen_int_relational (enum rtx_code code,
|
gen_int_relational (enum rtx_code code,
|
rtx result,
|
rtx result,
|
rtx cmp0,
|
rtx cmp0,
|
rtx cmp1,
|
rtx cmp1,
|
rtx destination)
|
rtx destination)
|
{
|
{
|
enum machine_mode mode;
|
enum machine_mode mode;
|
int branch_p;
|
int branch_p;
|
|
|
mode = GET_MODE (cmp0);
|
mode = GET_MODE (cmp0);
|
if (mode == VOIDmode)
|
if (mode == VOIDmode)
|
mode = GET_MODE (cmp1);
|
mode = GET_MODE (cmp1);
|
|
|
/* Is this a branch or compare. */
|
/* Is this a branch or compare. */
|
branch_p = (destination != 0);
|
branch_p = (destination != 0);
|
|
|
/* Instruction set doesn't support LE or LT, so swap operands and use
|
/* Instruction set doesn't support LE or LT, so swap operands and use
|
GE, GT. */
|
GE, GT. */
|
switch (code)
|
switch (code)
|
{
|
{
|
case LE:
|
case LE:
|
case LT:
|
case LT:
|
case LEU:
|
case LEU:
|
case LTU:
|
case LTU:
|
code = swap_condition (code);
|
code = swap_condition (code);
|
rtx temp = cmp0;
|
rtx temp = cmp0;
|
cmp0 = cmp1;
|
cmp0 = cmp1;
|
cmp1 = temp;
|
cmp1 = temp;
|
break;
|
break;
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
|
|
if (branch_p)
|
if (branch_p)
|
{
|
{
|
rtx insn;
|
rtx insn;
|
|
|
/* Operands must be in registers. */
|
/* Operands must be in registers. */
|
if (!register_operand (cmp0, mode))
|
if (!register_operand (cmp0, mode))
|
cmp0 = force_reg (mode, cmp0);
|
cmp0 = force_reg (mode, cmp0);
|
if (!register_operand (cmp1, mode))
|
if (!register_operand (cmp1, mode))
|
cmp1 = force_reg (mode, cmp1);
|
cmp1 = force_reg (mode, cmp1);
|
|
|
/* Generate conditional branch instruction. */
|
/* Generate conditional branch instruction. */
|
rtx cond = gen_rtx_fmt_ee (code, mode, cmp0, cmp1);
|
rtx cond = gen_rtx_fmt_ee (code, mode, cmp0, cmp1);
|
rtx label = gen_rtx_LABEL_REF (VOIDmode, destination);
|
rtx label = gen_rtx_LABEL_REF (VOIDmode, destination);
|
insn = gen_rtx_SET (VOIDmode, pc_rtx,
|
insn = gen_rtx_SET (VOIDmode, pc_rtx,
|
gen_rtx_IF_THEN_ELSE (VOIDmode,
|
gen_rtx_IF_THEN_ELSE (VOIDmode,
|
cond, label, pc_rtx));
|
cond, label, pc_rtx));
|
emit_jump_insn (insn);
|
emit_jump_insn (insn);
|
}
|
}
|
else
|
else
|
{
|
{
|
/* We can't have const_ints in cmp0, other than 0. */
|
/* We can't have const_ints in cmp0, other than 0. */
|
if ((GET_CODE (cmp0) == CONST_INT) && (INTVAL (cmp0) != 0))
|
if ((GET_CODE (cmp0) == CONST_INT) && (INTVAL (cmp0) != 0))
|
cmp0 = force_reg (mode, cmp0);
|
cmp0 = force_reg (mode, cmp0);
|
|
|
/* If the comparison is against an int not in legal range
|
/* If the comparison is against an int not in legal range
|
move it into a register. */
|
move it into a register. */
|
if (GET_CODE (cmp1) == CONST_INT)
|
if (GET_CODE (cmp1) == CONST_INT)
|
{
|
{
|
switch (code)
|
switch (code)
|
{
|
{
|
case EQ:
|
case EQ:
|
case NE:
|
case NE:
|
case LE:
|
case LE:
|
case LT:
|
case LT:
|
case GE:
|
case GE:
|
case GT:
|
case GT:
|
if (!satisfies_constraint_K (cmp1))
|
if (!satisfies_constraint_K (cmp1))
|
cmp1 = force_reg (mode, cmp1);
|
cmp1 = force_reg (mode, cmp1);
|
break;
|
break;
|
case LEU:
|
case LEU:
|
case LTU:
|
case LTU:
|
case GEU:
|
case GEU:
|
case GTU:
|
case GTU:
|
if (!satisfies_constraint_L (cmp1))
|
if (!satisfies_constraint_L (cmp1))
|
cmp1 = force_reg (mode, cmp1);
|
cmp1 = force_reg (mode, cmp1);
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
}
|
}
|
|
|
/* Generate compare instruction. */
|
/* Generate compare instruction. */
|
emit_move_insn (result, gen_rtx_fmt_ee (code, mode, cmp0, cmp1));
|
emit_move_insn (result, gen_rtx_fmt_ee (code, mode, cmp0, cmp1));
|
}
|
}
|
}
|
}
|
|
|
/* Try performing the comparison in OPERANDS[1], whose arms are OPERANDS[2]
|
/* Try performing the comparison in OPERANDS[1], whose arms are OPERANDS[2]
|
and OPERAND[3]. Store the result in OPERANDS[0]. */
|
and OPERAND[3]. Store the result in OPERANDS[0]. */
|
|
|
void
|
void
|
lm32_expand_scc (rtx operands[])
|
lm32_expand_scc (rtx operands[])
|
{
|
{
|
rtx target = operands[0];
|
rtx target = operands[0];
|
enum rtx_code code = GET_CODE (operands[1]);
|
enum rtx_code code = GET_CODE (operands[1]);
|
rtx op0 = operands[2];
|
rtx op0 = operands[2];
|
rtx op1 = operands[3];
|
rtx op1 = operands[3];
|
|
|
gen_int_relational (code, target, op0, op1, NULL_RTX);
|
gen_int_relational (code, target, op0, op1, NULL_RTX);
|
}
|
}
|
|
|
/* Compare OPERANDS[1] with OPERANDS[2] using comparison code
|
/* Compare OPERANDS[1] with OPERANDS[2] using comparison code
|
CODE and jump to OPERANDS[3] if the condition holds. */
|
CODE and jump to OPERANDS[3] if the condition holds. */
|
|
|
void
|
void
|
lm32_expand_conditional_branch (rtx operands[])
|
lm32_expand_conditional_branch (rtx operands[])
|
{
|
{
|
enum rtx_code code = GET_CODE (operands[0]);
|
enum rtx_code code = GET_CODE (operands[0]);
|
rtx op0 = operands[1];
|
rtx op0 = operands[1];
|
rtx op1 = operands[2];
|
rtx op1 = operands[2];
|
rtx destination = operands[3];
|
rtx destination = operands[3];
|
|
|
gen_int_relational (code, NULL_RTX, op0, op1, destination);
|
gen_int_relational (code, NULL_RTX, op0, op1, destination);
|
}
|
}
|
|
|
/* Generate and emit RTL to save or restore callee save registers. */
|
/* Generate and emit RTL to save or restore callee save registers. */
|
static void
|
static void
|
expand_save_restore (struct lm32_frame_info *info, int op)
|
expand_save_restore (struct lm32_frame_info *info, int op)
|
{
|
{
|
unsigned int reg_save_mask = info->reg_save_mask;
|
unsigned int reg_save_mask = info->reg_save_mask;
|
int regno;
|
int regno;
|
HOST_WIDE_INT offset;
|
HOST_WIDE_INT offset;
|
rtx insn;
|
rtx insn;
|
|
|
/* Callee saves are below locals and above outgoing arguments. */
|
/* Callee saves are below locals and above outgoing arguments. */
|
offset = info->args_size + info->callee_size;
|
offset = info->args_size + info->callee_size;
|
for (regno = 0; regno <= 31; regno++)
|
for (regno = 0; regno <= 31; regno++)
|
{
|
{
|
if ((reg_save_mask & (1 << regno)) != 0)
|
if ((reg_save_mask & (1 << regno)) != 0)
|
{
|
{
|
rtx offset_rtx;
|
rtx offset_rtx;
|
rtx mem;
|
rtx mem;
|
|
|
offset_rtx = GEN_INT (offset);
|
offset_rtx = GEN_INT (offset);
|
if (satisfies_constraint_K (offset_rtx))
|
if (satisfies_constraint_K (offset_rtx))
|
{
|
{
|
mem = gen_rtx_MEM (word_mode,
|
mem = gen_rtx_MEM (word_mode,
|
gen_rtx_PLUS (Pmode,
|
gen_rtx_PLUS (Pmode,
|
stack_pointer_rtx,
|
stack_pointer_rtx,
|
offset_rtx));
|
offset_rtx));
|
}
|
}
|
else
|
else
|
{
|
{
|
/* r10 is caller saved so it can be used as a temp reg. */
|
/* r10 is caller saved so it can be used as a temp reg. */
|
rtx r10;
|
rtx r10;
|
|
|
r10 = gen_rtx_REG (word_mode, 10);
|
r10 = gen_rtx_REG (word_mode, 10);
|
insn = emit_move_insn (r10, offset_rtx);
|
insn = emit_move_insn (r10, offset_rtx);
|
if (op == 0)
|
if (op == 0)
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
insn = emit_add (r10, r10, stack_pointer_rtx);
|
insn = emit_add (r10, r10, stack_pointer_rtx);
|
if (op == 0)
|
if (op == 0)
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
mem = gen_rtx_MEM (word_mode, r10);
|
mem = gen_rtx_MEM (word_mode, r10);
|
}
|
}
|
|
|
if (op == 0)
|
if (op == 0)
|
insn = emit_move_insn (mem, gen_rtx_REG (word_mode, regno));
|
insn = emit_move_insn (mem, gen_rtx_REG (word_mode, regno));
|
else
|
else
|
insn = emit_move_insn (gen_rtx_REG (word_mode, regno), mem);
|
insn = emit_move_insn (gen_rtx_REG (word_mode, regno), mem);
|
|
|
/* only prologue instructions which set the sp fp or save a
|
/* only prologue instructions which set the sp fp or save a
|
register should be marked as frame related. */
|
register should be marked as frame related. */
|
if (op == 0)
|
if (op == 0)
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
offset -= UNITS_PER_WORD;
|
offset -= UNITS_PER_WORD;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
static void
|
static void
|
stack_adjust (HOST_WIDE_INT amount)
|
stack_adjust (HOST_WIDE_INT amount)
|
{
|
{
|
rtx insn;
|
rtx insn;
|
|
|
if (!IN_RANGE (amount, -32776, 32768))
|
if (!IN_RANGE (amount, -32776, 32768))
|
{
|
{
|
/* r10 is caller saved so it can be used as a temp reg. */
|
/* r10 is caller saved so it can be used as a temp reg. */
|
rtx r10;
|
rtx r10;
|
r10 = gen_rtx_REG (word_mode, 10);
|
r10 = gen_rtx_REG (word_mode, 10);
|
insn = emit_move_insn (r10, GEN_INT (amount));
|
insn = emit_move_insn (r10, GEN_INT (amount));
|
if (amount < 0)
|
if (amount < 0)
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
insn = emit_add (stack_pointer_rtx, stack_pointer_rtx, r10);
|
insn = emit_add (stack_pointer_rtx, stack_pointer_rtx, r10);
|
if (amount < 0)
|
if (amount < 0)
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
}
|
}
|
else
|
else
|
{
|
{
|
insn = emit_add (stack_pointer_rtx,
|
insn = emit_add (stack_pointer_rtx,
|
stack_pointer_rtx, GEN_INT (amount));
|
stack_pointer_rtx, GEN_INT (amount));
|
if (amount < 0)
|
if (amount < 0)
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
}
|
}
|
}
|
}
|
|
|
|
|
/* Create and emit instructions for a functions prologue. */
|
/* Create and emit instructions for a functions prologue. */
|
void
|
void
|
lm32_expand_prologue (void)
|
lm32_expand_prologue (void)
|
{
|
{
|
rtx insn;
|
rtx insn;
|
|
|
lm32_compute_frame_size (get_frame_size ());
|
lm32_compute_frame_size (get_frame_size ());
|
|
|
if (current_frame_info.total_size > 0)
|
if (current_frame_info.total_size > 0)
|
{
|
{
|
/* Add space on stack new frame. */
|
/* Add space on stack new frame. */
|
stack_adjust (-current_frame_info.total_size);
|
stack_adjust (-current_frame_info.total_size);
|
|
|
/* Save callee save registers. */
|
/* Save callee save registers. */
|
if (current_frame_info.reg_save_mask != 0)
|
if (current_frame_info.reg_save_mask != 0)
|
expand_save_restore (¤t_frame_info, 0);
|
expand_save_restore (¤t_frame_info, 0);
|
|
|
/* Setup frame pointer if it's needed. */
|
/* Setup frame pointer if it's needed. */
|
if (frame_pointer_needed == 1)
|
if (frame_pointer_needed == 1)
|
{
|
{
|
/* Load offset - Don't use total_size, as that includes pretend_size,
|
/* Load offset - Don't use total_size, as that includes pretend_size,
|
which isn't part of this frame? */
|
which isn't part of this frame? */
|
insn =
|
insn =
|
emit_move_insn (frame_pointer_rtx,
|
emit_move_insn (frame_pointer_rtx,
|
GEN_INT (current_frame_info.args_size +
|
GEN_INT (current_frame_info.args_size +
|
current_frame_info.callee_size +
|
current_frame_info.callee_size +
|
current_frame_info.locals_size));
|
current_frame_info.locals_size));
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
|
|
/* Add in sp. */
|
/* Add in sp. */
|
insn = emit_add (frame_pointer_rtx,
|
insn = emit_add (frame_pointer_rtx,
|
frame_pointer_rtx, stack_pointer_rtx);
|
frame_pointer_rtx, stack_pointer_rtx);
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
}
|
}
|
|
|
/* Prevent prologue from being scheduled into function body. */
|
/* Prevent prologue from being scheduled into function body. */
|
emit_insn (gen_blockage ());
|
emit_insn (gen_blockage ());
|
}
|
}
|
}
|
}
|
|
|
/* Create an emit instructions for a functions epilogue. */
|
/* Create an emit instructions for a functions epilogue. */
|
void
|
void
|
lm32_expand_epilogue (void)
|
lm32_expand_epilogue (void)
|
{
|
{
|
rtx ra_rtx = gen_rtx_REG (Pmode, RA_REGNUM);
|
rtx ra_rtx = gen_rtx_REG (Pmode, RA_REGNUM);
|
|
|
lm32_compute_frame_size (get_frame_size ());
|
lm32_compute_frame_size (get_frame_size ());
|
|
|
if (current_frame_info.total_size > 0)
|
if (current_frame_info.total_size > 0)
|
{
|
{
|
/* Prevent stack code from being reordered. */
|
/* Prevent stack code from being reordered. */
|
emit_insn (gen_blockage ());
|
emit_insn (gen_blockage ());
|
|
|
/* Restore callee save registers. */
|
/* Restore callee save registers. */
|
if (current_frame_info.reg_save_mask != 0)
|
if (current_frame_info.reg_save_mask != 0)
|
expand_save_restore (¤t_frame_info, 1);
|
expand_save_restore (¤t_frame_info, 1);
|
|
|
/* Deallocate stack. */
|
/* Deallocate stack. */
|
stack_adjust (current_frame_info.total_size);
|
stack_adjust (current_frame_info.total_size);
|
|
|
/* Return to calling function. */
|
/* Return to calling function. */
|
emit_jump_insn (gen_return_internal (ra_rtx));
|
emit_jump_insn (gen_return_internal (ra_rtx));
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Return to calling function. */
|
/* Return to calling function. */
|
emit_jump_insn (gen_return_internal (ra_rtx));
|
emit_jump_insn (gen_return_internal (ra_rtx));
|
}
|
}
|
}
|
}
|
|
|
/* Return the bytes needed to compute the frame pointer from the current
|
/* Return the bytes needed to compute the frame pointer from the current
|
stack pointer. */
|
stack pointer. */
|
static HOST_WIDE_INT
|
static HOST_WIDE_INT
|
lm32_compute_frame_size (int size)
|
lm32_compute_frame_size (int size)
|
{
|
{
|
int regno;
|
int regno;
|
HOST_WIDE_INT total_size, locals_size, args_size, pretend_size, callee_size;
|
HOST_WIDE_INT total_size, locals_size, args_size, pretend_size, callee_size;
|
unsigned int reg_save_mask;
|
unsigned int reg_save_mask;
|
|
|
locals_size = size;
|
locals_size = size;
|
args_size = crtl->outgoing_args_size;
|
args_size = crtl->outgoing_args_size;
|
pretend_size = crtl->args.pretend_args_size;
|
pretend_size = crtl->args.pretend_args_size;
|
callee_size = 0;
|
callee_size = 0;
|
reg_save_mask = 0;
|
reg_save_mask = 0;
|
|
|
/* Build mask that actually determines which regsiters we save
|
/* Build mask that actually determines which regsiters we save
|
and calculate size required to store them in the stack. */
|
and calculate size required to store them in the stack. */
|
for (regno = 1; regno < SP_REGNUM; regno++)
|
for (regno = 1; regno < SP_REGNUM; regno++)
|
{
|
{
|
if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
|
if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
|
{
|
{
|
reg_save_mask |= 1 << regno;
|
reg_save_mask |= 1 << regno;
|
callee_size += UNITS_PER_WORD;
|
callee_size += UNITS_PER_WORD;
|
}
|
}
|
}
|
}
|
if (df_regs_ever_live_p (RA_REGNUM) || !current_function_is_leaf
|
if (df_regs_ever_live_p (RA_REGNUM) || !current_function_is_leaf
|
|| !optimize)
|
|| !optimize)
|
{
|
{
|
reg_save_mask |= 1 << RA_REGNUM;
|
reg_save_mask |= 1 << RA_REGNUM;
|
callee_size += UNITS_PER_WORD;
|
callee_size += UNITS_PER_WORD;
|
}
|
}
|
if (!(reg_save_mask & (1 << FP_REGNUM)) && frame_pointer_needed)
|
if (!(reg_save_mask & (1 << FP_REGNUM)) && frame_pointer_needed)
|
{
|
{
|
reg_save_mask |= 1 << FP_REGNUM;
|
reg_save_mask |= 1 << FP_REGNUM;
|
callee_size += UNITS_PER_WORD;
|
callee_size += UNITS_PER_WORD;
|
}
|
}
|
|
|
/* Compute total frame size. */
|
/* Compute total frame size. */
|
total_size = pretend_size + args_size + locals_size + callee_size;
|
total_size = pretend_size + args_size + locals_size + callee_size;
|
|
|
/* Align frame to appropriate boundary. */
|
/* Align frame to appropriate boundary. */
|
total_size = (total_size + 3) & ~3;
|
total_size = (total_size + 3) & ~3;
|
|
|
/* Save computed information. */
|
/* Save computed information. */
|
current_frame_info.total_size = total_size;
|
current_frame_info.total_size = total_size;
|
current_frame_info.callee_size = callee_size;
|
current_frame_info.callee_size = callee_size;
|
current_frame_info.pretend_size = pretend_size;
|
current_frame_info.pretend_size = pretend_size;
|
current_frame_info.locals_size = locals_size;
|
current_frame_info.locals_size = locals_size;
|
current_frame_info.args_size = args_size;
|
current_frame_info.args_size = args_size;
|
current_frame_info.reg_save_mask = reg_save_mask;
|
current_frame_info.reg_save_mask = reg_save_mask;
|
|
|
return total_size;
|
return total_size;
|
}
|
}
|
|
|
void
|
void
|
lm32_print_operand (FILE * file, rtx op, int letter)
|
lm32_print_operand (FILE * file, rtx op, int letter)
|
{
|
{
|
enum rtx_code code;
|
enum rtx_code code;
|
|
|
code = GET_CODE (op);
|
code = GET_CODE (op);
|
|
|
if (code == SIGN_EXTEND)
|
if (code == SIGN_EXTEND)
|
op = XEXP (op, 0), code = GET_CODE (op);
|
op = XEXP (op, 0), code = GET_CODE (op);
|
else if (code == REG || code == SUBREG)
|
else if (code == REG || code == SUBREG)
|
{
|
{
|
int regnum;
|
int regnum;
|
|
|
if (code == REG)
|
if (code == REG)
|
regnum = REGNO (op);
|
regnum = REGNO (op);
|
else
|
else
|
regnum = true_regnum (op);
|
regnum = true_regnum (op);
|
|
|
fprintf (file, "%s", reg_names[regnum]);
|
fprintf (file, "%s", reg_names[regnum]);
|
}
|
}
|
else if (code == HIGH)
|
else if (code == HIGH)
|
output_addr_const (file, XEXP (op, 0));
|
output_addr_const (file, XEXP (op, 0));
|
else if (code == MEM)
|
else if (code == MEM)
|
output_address (XEXP (op, 0));
|
output_address (XEXP (op, 0));
|
else if (letter == 'z' && GET_CODE (op) == CONST_INT && INTVAL (op) == 0)
|
else if (letter == 'z' && GET_CODE (op) == CONST_INT && INTVAL (op) == 0)
|
fprintf (file, "%s", reg_names[0]);
|
fprintf (file, "%s", reg_names[0]);
|
else if (GET_CODE (op) == CONST_DOUBLE)
|
else if (GET_CODE (op) == CONST_DOUBLE)
|
{
|
{
|
if ((CONST_DOUBLE_LOW (op) != 0) || (CONST_DOUBLE_HIGH (op) != 0))
|
if ((CONST_DOUBLE_LOW (op) != 0) || (CONST_DOUBLE_HIGH (op) != 0))
|
output_operand_lossage ("Only 0.0 can be loaded as an immediate");
|
output_operand_lossage ("Only 0.0 can be loaded as an immediate");
|
else
|
else
|
fprintf (file, "0");
|
fprintf (file, "0");
|
}
|
}
|
else if (code == EQ)
|
else if (code == EQ)
|
fprintf (file, "e ");
|
fprintf (file, "e ");
|
else if (code == NE)
|
else if (code == NE)
|
fprintf (file, "ne ");
|
fprintf (file, "ne ");
|
else if (code == GT)
|
else if (code == GT)
|
fprintf (file, "g ");
|
fprintf (file, "g ");
|
else if (code == GTU)
|
else if (code == GTU)
|
fprintf (file, "gu ");
|
fprintf (file, "gu ");
|
else if (code == LT)
|
else if (code == LT)
|
fprintf (file, "l ");
|
fprintf (file, "l ");
|
else if (code == LTU)
|
else if (code == LTU)
|
fprintf (file, "lu ");
|
fprintf (file, "lu ");
|
else if (code == GE)
|
else if (code == GE)
|
fprintf (file, "ge ");
|
fprintf (file, "ge ");
|
else if (code == GEU)
|
else if (code == GEU)
|
fprintf (file, "geu");
|
fprintf (file, "geu");
|
else if (code == LE)
|
else if (code == LE)
|
fprintf (file, "le ");
|
fprintf (file, "le ");
|
else if (code == LEU)
|
else if (code == LEU)
|
fprintf (file, "leu");
|
fprintf (file, "leu");
|
else
|
else
|
output_addr_const (file, op);
|
output_addr_const (file, op);
|
}
|
}
|
|
|
/* A C compound statement to output to stdio stream STREAM the
|
/* A C compound statement to output to stdio stream STREAM the
|
assembler syntax for an instruction operand that is a memory
|
assembler syntax for an instruction operand that is a memory
|
reference whose address is ADDR. ADDR is an RTL expression.
|
reference whose address is ADDR. ADDR is an RTL expression.
|
|
|
On some machines, the syntax for a symbolic address depends on
|
On some machines, the syntax for a symbolic address depends on
|
the section that the address refers to. On these machines,
|
the section that the address refers to. On these machines,
|
define the macro `ENCODE_SECTION_INFO' to store the information
|
define the macro `ENCODE_SECTION_INFO' to store the information
|
into the `symbol_ref', and then check for it here. */
|
into the `symbol_ref', and then check for it here. */
|
|
|
void
|
void
|
lm32_print_operand_address (FILE * file, rtx addr)
|
lm32_print_operand_address (FILE * file, rtx addr)
|
{
|
{
|
switch (GET_CODE (addr))
|
switch (GET_CODE (addr))
|
{
|
{
|
case REG:
|
case REG:
|
fprintf (file, "(%s+0)", reg_names[REGNO (addr)]);
|
fprintf (file, "(%s+0)", reg_names[REGNO (addr)]);
|
break;
|
break;
|
|
|
case MEM:
|
case MEM:
|
output_address (XEXP (addr, 0));
|
output_address (XEXP (addr, 0));
|
break;
|
break;
|
|
|
case PLUS:
|
case PLUS:
|
{
|
{
|
rtx arg0 = XEXP (addr, 0);
|
rtx arg0 = XEXP (addr, 0);
|
rtx arg1 = XEXP (addr, 1);
|
rtx arg1 = XEXP (addr, 1);
|
|
|
if (GET_CODE (arg0) == REG && CONSTANT_P (arg1))
|
if (GET_CODE (arg0) == REG && CONSTANT_P (arg1))
|
{
|
{
|
if (GET_CODE (arg1) == CONST_INT)
|
if (GET_CODE (arg1) == CONST_INT)
|
fprintf (file, "(%s+%ld)", reg_names[REGNO (arg0)],
|
fprintf (file, "(%s+%ld)", reg_names[REGNO (arg0)],
|
INTVAL (arg1));
|
INTVAL (arg1));
|
else
|
else
|
{
|
{
|
fprintf (file, "(%s+", reg_names[REGNO (arg0)]);
|
fprintf (file, "(%s+", reg_names[REGNO (arg0)]);
|
output_addr_const (file, arg1);
|
output_addr_const (file, arg1);
|
fprintf (file, ")");
|
fprintf (file, ")");
|
}
|
}
|
}
|
}
|
else if (CONSTANT_P (arg0) && CONSTANT_P (arg1))
|
else if (CONSTANT_P (arg0) && CONSTANT_P (arg1))
|
output_addr_const (file, addr);
|
output_addr_const (file, addr);
|
else
|
else
|
fatal_insn ("bad operand", addr);
|
fatal_insn ("bad operand", addr);
|
}
|
}
|
break;
|
break;
|
|
|
case SYMBOL_REF:
|
case SYMBOL_REF:
|
if (SYMBOL_REF_SMALL_P (addr))
|
if (SYMBOL_REF_SMALL_P (addr))
|
{
|
{
|
fprintf (file, "gp(");
|
fprintf (file, "gp(");
|
output_addr_const (file, addr);
|
output_addr_const (file, addr);
|
fprintf (file, ")");
|
fprintf (file, ")");
|
}
|
}
|
else
|
else
|
fatal_insn ("can't use non gp relative absolute address", addr);
|
fatal_insn ("can't use non gp relative absolute address", addr);
|
break;
|
break;
|
|
|
default:
|
default:
|
fatal_insn ("invalid addressing mode", addr);
|
fatal_insn ("invalid addressing mode", addr);
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
/* Determine where to put an argument to a function.
|
/* Determine where to put an argument to a function.
|
Value is zero to push the argument on the stack,
|
Value is zero to push the argument on the stack,
|
or a hard register in which to store the argument.
|
or a hard register in which to store the argument.
|
|
|
MODE is the argument's machine mode.
|
MODE is the argument's machine mode.
|
TYPE is the data type of the argument (as a tree).
|
TYPE is the data type of the argument (as a tree).
|
This is null for libcalls where that information may
|
This is null for libcalls where that information may
|
not be available.
|
not be available.
|
CUM is a variable of type CUMULATIVE_ARGS which gives info about
|
CUM is a variable of type CUMULATIVE_ARGS which gives info about
|
the preceding args and about the function being called.
|
the preceding args and about the function being called.
|
NAMED is nonzero if this argument is a named parameter
|
NAMED is nonzero if this argument is a named parameter
|
(otherwise it is an extra parameter matching an ellipsis). */
|
(otherwise it is an extra parameter matching an ellipsis). */
|
|
|
rtx
|
rtx
|
lm32_function_arg (CUMULATIVE_ARGS cum, enum machine_mode mode,
|
lm32_function_arg (CUMULATIVE_ARGS cum, enum machine_mode mode,
|
tree type, int named)
|
tree type, int named)
|
{
|
{
|
if (mode == VOIDmode)
|
if (mode == VOIDmode)
|
/* Compute operand 2 of the call insn. */
|
/* Compute operand 2 of the call insn. */
|
return GEN_INT (0);
|
return GEN_INT (0);
|
|
|
if (targetm.calls.must_pass_in_stack (mode, type))
|
if (targetm.calls.must_pass_in_stack (mode, type))
|
return NULL_RTX;
|
return NULL_RTX;
|
|
|
if (!named || (cum + LM32_NUM_REGS2 (mode, type) > LM32_NUM_ARG_REGS))
|
if (!named || (cum + LM32_NUM_REGS2 (mode, type) > LM32_NUM_ARG_REGS))
|
return NULL_RTX;
|
return NULL_RTX;
|
|
|
return gen_rtx_REG (mode, cum + LM32_FIRST_ARG_REG);
|
return gen_rtx_REG (mode, cum + LM32_FIRST_ARG_REG);
|
}
|
}
|
|
|
HOST_WIDE_INT
|
HOST_WIDE_INT
|
lm32_compute_initial_elimination_offset (int from, int to)
|
lm32_compute_initial_elimination_offset (int from, int to)
|
{
|
{
|
HOST_WIDE_INT offset = 0;
|
HOST_WIDE_INT offset = 0;
|
|
|
switch (from)
|
switch (from)
|
{
|
{
|
case ARG_POINTER_REGNUM:
|
case ARG_POINTER_REGNUM:
|
switch (to)
|
switch (to)
|
{
|
{
|
case FRAME_POINTER_REGNUM:
|
case FRAME_POINTER_REGNUM:
|
offset = 0;
|
offset = 0;
|
break;
|
break;
|
case STACK_POINTER_REGNUM:
|
case STACK_POINTER_REGNUM:
|
offset =
|
offset =
|
lm32_compute_frame_size (get_frame_size ()) -
|
lm32_compute_frame_size (get_frame_size ()) -
|
current_frame_info.pretend_size;
|
current_frame_info.pretend_size;
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
return offset;
|
return offset;
|
}
|
}
|
|
|
static void
|
static void
|
lm32_setup_incoming_varargs (CUMULATIVE_ARGS * cum, enum machine_mode mode,
|
lm32_setup_incoming_varargs (CUMULATIVE_ARGS * cum, enum machine_mode mode,
|
tree type, int *pretend_size, int no_rtl)
|
tree type, int *pretend_size, int no_rtl)
|
{
|
{
|
int first_anon_arg;
|
int first_anon_arg;
|
tree fntype;
|
tree fntype;
|
int stdarg_p;
|
int stdarg_p;
|
|
|
fntype = TREE_TYPE (current_function_decl);
|
fntype = TREE_TYPE (current_function_decl);
|
stdarg_p = (TYPE_ARG_TYPES (fntype) != 0
|
stdarg_p = (TYPE_ARG_TYPES (fntype) != 0
|
&& (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
|
&& (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
|
!= void_type_node));
|
!= void_type_node));
|
|
|
if (stdarg_p)
|
if (stdarg_p)
|
first_anon_arg = *cum + LM32_FIRST_ARG_REG;
|
first_anon_arg = *cum + LM32_FIRST_ARG_REG;
|
else
|
else
|
{
|
{
|
/* this is the common case, we have been passed details setup
|
/* this is the common case, we have been passed details setup
|
for the last named argument, we want to skip over the
|
for the last named argument, we want to skip over the
|
registers, if any used in passing this named paramter in
|
registers, if any used in passing this named paramter in
|
order to determine which is the first registers used to pass
|
order to determine which is the first registers used to pass
|
anonymous arguments. */
|
anonymous arguments. */
|
int size;
|
int size;
|
|
|
if (mode == BLKmode)
|
if (mode == BLKmode)
|
size = int_size_in_bytes (type);
|
size = int_size_in_bytes (type);
|
else
|
else
|
size = GET_MODE_SIZE (mode);
|
size = GET_MODE_SIZE (mode);
|
|
|
first_anon_arg =
|
first_anon_arg =
|
*cum + LM32_FIRST_ARG_REG +
|
*cum + LM32_FIRST_ARG_REG +
|
((size + UNITS_PER_WORD - 1) / UNITS_PER_WORD);
|
((size + UNITS_PER_WORD - 1) / UNITS_PER_WORD);
|
}
|
}
|
|
|
if ((first_anon_arg < (LM32_FIRST_ARG_REG + LM32_NUM_ARG_REGS)) && !no_rtl)
|
if ((first_anon_arg < (LM32_FIRST_ARG_REG + LM32_NUM_ARG_REGS)) && !no_rtl)
|
{
|
{
|
int first_reg_offset = first_anon_arg;
|
int first_reg_offset = first_anon_arg;
|
int size = LM32_FIRST_ARG_REG + LM32_NUM_ARG_REGS - first_anon_arg;
|
int size = LM32_FIRST_ARG_REG + LM32_NUM_ARG_REGS - first_anon_arg;
|
rtx regblock;
|
rtx regblock;
|
|
|
regblock = gen_rtx_MEM (BLKmode,
|
regblock = gen_rtx_MEM (BLKmode,
|
plus_constant (arg_pointer_rtx,
|
plus_constant (arg_pointer_rtx,
|
FIRST_PARM_OFFSET (0)));
|
FIRST_PARM_OFFSET (0)));
|
move_block_from_reg (first_reg_offset, regblock, size);
|
move_block_from_reg (first_reg_offset, regblock, size);
|
|
|
*pretend_size = size * UNITS_PER_WORD;
|
*pretend_size = size * UNITS_PER_WORD;
|
}
|
}
|
}
|
}
|
|
|
/* Override command line options. */
|
/* Override command line options. */
|
void
|
void
|
lm32_override_options (void)
|
lm32_override_options (void)
|
{
|
{
|
/* We must have sign-extend enabled if barrel-shift isn't. */
|
/* We must have sign-extend enabled if barrel-shift isn't. */
|
if (!TARGET_BARREL_SHIFT_ENABLED && !TARGET_SIGN_EXTEND_ENABLED)
|
if (!TARGET_BARREL_SHIFT_ENABLED && !TARGET_SIGN_EXTEND_ENABLED)
|
target_flags |= MASK_SIGN_EXTEND_ENABLED;
|
target_flags |= MASK_SIGN_EXTEND_ENABLED;
|
}
|
}
|
|
|
/* Return nonzero if this function is known to have a null epilogue.
|
/* Return nonzero if this function is known to have a null epilogue.
|
This allows the optimizer to omit jumps to jumps if no stack
|
This allows the optimizer to omit jumps to jumps if no stack
|
was created. */
|
was created. */
|
int
|
int
|
lm32_can_use_return (void)
|
lm32_can_use_return (void)
|
{
|
{
|
if (!reload_completed)
|
if (!reload_completed)
|
return 0;
|
return 0;
|
|
|
if (df_regs_ever_live_p (RA_REGNUM) || crtl->profile)
|
if (df_regs_ever_live_p (RA_REGNUM) || crtl->profile)
|
return 0;
|
return 0;
|
|
|
if (lm32_compute_frame_size (get_frame_size ()) != 0)
|
if (lm32_compute_frame_size (get_frame_size ()) != 0)
|
return 0;
|
return 0;
|
|
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Support function to determine the return address of the function
|
/* Support function to determine the return address of the function
|
'count' frames back up the stack. */
|
'count' frames back up the stack. */
|
rtx
|
rtx
|
lm32_return_addr_rtx (int count, rtx frame)
|
lm32_return_addr_rtx (int count, rtx frame)
|
{
|
{
|
rtx r;
|
rtx r;
|
if (count == 0)
|
if (count == 0)
|
{
|
{
|
if (!df_regs_ever_live_p (RA_REGNUM))
|
if (!df_regs_ever_live_p (RA_REGNUM))
|
r = gen_rtx_REG (Pmode, RA_REGNUM);
|
r = gen_rtx_REG (Pmode, RA_REGNUM);
|
else
|
else
|
{
|
{
|
r = gen_rtx_MEM (Pmode,
|
r = gen_rtx_MEM (Pmode,
|
gen_rtx_PLUS (Pmode, frame,
|
gen_rtx_PLUS (Pmode, frame,
|
GEN_INT (-2 * UNITS_PER_WORD)));
|
GEN_INT (-2 * UNITS_PER_WORD)));
|
set_mem_alias_set (r, get_frame_alias_set ());
|
set_mem_alias_set (r, get_frame_alias_set ());
|
}
|
}
|
}
|
}
|
else if (flag_omit_frame_pointer)
|
else if (flag_omit_frame_pointer)
|
r = NULL_RTX;
|
r = NULL_RTX;
|
else
|
else
|
{
|
{
|
r = gen_rtx_MEM (Pmode,
|
r = gen_rtx_MEM (Pmode,
|
gen_rtx_PLUS (Pmode, frame,
|
gen_rtx_PLUS (Pmode, frame,
|
GEN_INT (-2 * UNITS_PER_WORD)));
|
GEN_INT (-2 * UNITS_PER_WORD)));
|
set_mem_alias_set (r, get_frame_alias_set ());
|
set_mem_alias_set (r, get_frame_alias_set ());
|
}
|
}
|
return r;
|
return r;
|
}
|
}
|
|
|
/* Return true if EXP should be placed in the small data section. */
|
/* Return true if EXP should be placed in the small data section. */
|
|
|
static bool
|
static bool
|
lm32_in_small_data_p (const_tree exp)
|
lm32_in_small_data_p (const_tree exp)
|
{
|
{
|
/* We want to merge strings, so we never consider them small data. */
|
/* We want to merge strings, so we never consider them small data. */
|
if (TREE_CODE (exp) == STRING_CST)
|
if (TREE_CODE (exp) == STRING_CST)
|
return false;
|
return false;
|
|
|
/* Functions are never in the small data area. Duh. */
|
/* Functions are never in the small data area. Duh. */
|
if (TREE_CODE (exp) == FUNCTION_DECL)
|
if (TREE_CODE (exp) == FUNCTION_DECL)
|
return false;
|
return false;
|
|
|
if (TREE_CODE (exp) == VAR_DECL && DECL_SECTION_NAME (exp))
|
if (TREE_CODE (exp) == VAR_DECL && DECL_SECTION_NAME (exp))
|
{
|
{
|
const char *section = TREE_STRING_POINTER (DECL_SECTION_NAME (exp));
|
const char *section = TREE_STRING_POINTER (DECL_SECTION_NAME (exp));
|
if (strcmp (section, ".sdata") == 0 || strcmp (section, ".sbss") == 0)
|
if (strcmp (section, ".sdata") == 0 || strcmp (section, ".sbss") == 0)
|
return true;
|
return true;
|
}
|
}
|
else
|
else
|
{
|
{
|
HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (exp));
|
HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (exp));
|
|
|
/* If this is an incomplete type with size 0, then we can't put it
|
/* If this is an incomplete type with size 0, then we can't put it
|
in sdata because it might be too big when completed. */
|
in sdata because it might be too big when completed. */
|
if (size > 0 && (unsigned HOST_WIDE_INT) size <= g_switch_value)
|
if (size > 0 && (unsigned HOST_WIDE_INT) size <= g_switch_value)
|
return true;
|
return true;
|
}
|
}
|
|
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Emit straight-line code to move LENGTH bytes from SRC to DEST.
|
/* Emit straight-line code to move LENGTH bytes from SRC to DEST.
|
Assume that the areas do not overlap. */
|
Assume that the areas do not overlap. */
|
|
|
static void
|
static void
|
lm32_block_move_inline (rtx dest, rtx src, HOST_WIDE_INT length,
|
lm32_block_move_inline (rtx dest, rtx src, HOST_WIDE_INT length,
|
HOST_WIDE_INT alignment)
|
HOST_WIDE_INT alignment)
|
{
|
{
|
HOST_WIDE_INT offset, delta;
|
HOST_WIDE_INT offset, delta;
|
unsigned HOST_WIDE_INT bits;
|
unsigned HOST_WIDE_INT bits;
|
int i;
|
int i;
|
enum machine_mode mode;
|
enum machine_mode mode;
|
rtx *regs;
|
rtx *regs;
|
|
|
/* Work out how many bits to move at a time. */
|
/* Work out how many bits to move at a time. */
|
switch (alignment)
|
switch (alignment)
|
{
|
{
|
case 1:
|
case 1:
|
bits = 8;
|
bits = 8;
|
break;
|
break;
|
case 2:
|
case 2:
|
bits = 16;
|
bits = 16;
|
break;
|
break;
|
default:
|
default:
|
bits = 32;
|
bits = 32;
|
break;
|
break;
|
}
|
}
|
|
|
mode = mode_for_size (bits, MODE_INT, 0);
|
mode = mode_for_size (bits, MODE_INT, 0);
|
delta = bits / BITS_PER_UNIT;
|
delta = bits / BITS_PER_UNIT;
|
|
|
/* Allocate a buffer for the temporary registers. */
|
/* Allocate a buffer for the temporary registers. */
|
regs = alloca (sizeof (rtx) * length / delta);
|
regs = alloca (sizeof (rtx) * length / delta);
|
|
|
/* Load as many BITS-sized chunks as possible. */
|
/* Load as many BITS-sized chunks as possible. */
|
for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
|
for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
|
{
|
{
|
regs[i] = gen_reg_rtx (mode);
|
regs[i] = gen_reg_rtx (mode);
|
emit_move_insn (regs[i], adjust_address (src, mode, offset));
|
emit_move_insn (regs[i], adjust_address (src, mode, offset));
|
}
|
}
|
|
|
/* Copy the chunks to the destination. */
|
/* Copy the chunks to the destination. */
|
for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
|
for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
|
emit_move_insn (adjust_address (dest, mode, offset), regs[i]);
|
emit_move_insn (adjust_address (dest, mode, offset), regs[i]);
|
|
|
/* Mop up any left-over bytes. */
|
/* Mop up any left-over bytes. */
|
if (offset < length)
|
if (offset < length)
|
{
|
{
|
src = adjust_address (src, BLKmode, offset);
|
src = adjust_address (src, BLKmode, offset);
|
dest = adjust_address (dest, BLKmode, offset);
|
dest = adjust_address (dest, BLKmode, offset);
|
move_by_pieces (dest, src, length - offset,
|
move_by_pieces (dest, src, length - offset,
|
MIN (MEM_ALIGN (src), MEM_ALIGN (dest)), 0);
|
MIN (MEM_ALIGN (src), MEM_ALIGN (dest)), 0);
|
}
|
}
|
}
|
}
|
|
|
/* Expand string/block move operations.
|
/* Expand string/block move operations.
|
|
|
operands[0] is the pointer to the destination.
|
operands[0] is the pointer to the destination.
|
operands[1] is the pointer to the source.
|
operands[1] is the pointer to the source.
|
operands[2] is the number of bytes to move.
|
operands[2] is the number of bytes to move.
|
operands[3] is the alignment. */
|
operands[3] is the alignment. */
|
|
|
int
|
int
|
lm32_expand_block_move (rtx * operands)
|
lm32_expand_block_move (rtx * operands)
|
{
|
{
|
if ((GET_CODE (operands[2]) == CONST_INT) && (INTVAL (operands[2]) <= 32))
|
if ((GET_CODE (operands[2]) == CONST_INT) && (INTVAL (operands[2]) <= 32))
|
{
|
{
|
lm32_block_move_inline (operands[0], operands[1], INTVAL (operands[2]),
|
lm32_block_move_inline (operands[0], operands[1], INTVAL (operands[2]),
|
INTVAL (operands[3]));
|
INTVAL (operands[3]));
|
return 1;
|
return 1;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Return TRUE if X references a SYMBOL_REF or LABEL_REF whose symbol
|
/* Return TRUE if X references a SYMBOL_REF or LABEL_REF whose symbol
|
isn't protected by a PIC unspec. */
|
isn't protected by a PIC unspec. */
|
int
|
int
|
nonpic_symbol_mentioned_p (rtx x)
|
nonpic_symbol_mentioned_p (rtx x)
|
{
|
{
|
const char *fmt;
|
const char *fmt;
|
int i;
|
int i;
|
|
|
if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF
|
if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF
|
|| GET_CODE (x) == PC)
|
|| GET_CODE (x) == PC)
|
return 1;
|
return 1;
|
|
|
/* We don't want to look into the possible MEM location of a
|
/* We don't want to look into the possible MEM location of a
|
CONST_DOUBLE, since we're not going to use it, in general. */
|
CONST_DOUBLE, since we're not going to use it, in general. */
|
if (GET_CODE (x) == CONST_DOUBLE)
|
if (GET_CODE (x) == CONST_DOUBLE)
|
return 0;
|
return 0;
|
|
|
if (GET_CODE (x) == UNSPEC)
|
if (GET_CODE (x) == UNSPEC)
|
return 0;
|
return 0;
|
|
|
fmt = GET_RTX_FORMAT (GET_CODE (x));
|
fmt = GET_RTX_FORMAT (GET_CODE (x));
|
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
|
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
|
{
|
{
|
if (fmt[i] == 'E')
|
if (fmt[i] == 'E')
|
{
|
{
|
int j;
|
int j;
|
|
|
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
|
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
|
if (nonpic_symbol_mentioned_p (XVECEXP (x, i, j)))
|
if (nonpic_symbol_mentioned_p (XVECEXP (x, i, j)))
|
return 1;
|
return 1;
|
}
|
}
|
else if (fmt[i] == 'e' && nonpic_symbol_mentioned_p (XEXP (x, i)))
|
else if (fmt[i] == 'e' && nonpic_symbol_mentioned_p (XEXP (x, i)))
|
return 1;
|
return 1;
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Compute a (partial) cost for rtx X. Return true if the complete
|
/* Compute a (partial) cost for rtx X. Return true if the complete
|
cost has been computed, and false if subexpressions should be
|
cost has been computed, and false if subexpressions should be
|
scanned. In either case, *TOTAL contains the cost result. */
|
scanned. In either case, *TOTAL contains the cost result. */
|
|
|
static bool
|
static bool
|
lm32_rtx_costs (rtx x, int code, int outer_code, int *total, bool speed)
|
lm32_rtx_costs (rtx x, int code, int outer_code, int *total, bool speed)
|
{
|
{
|
enum machine_mode mode = GET_MODE (x);
|
enum machine_mode mode = GET_MODE (x);
|
bool small_mode;
|
bool small_mode;
|
|
|
const int arithmetic_latency = 1;
|
const int arithmetic_latency = 1;
|
const int shift_latency = 1;
|
const int shift_latency = 1;
|
const int compare_latency = 2;
|
const int compare_latency = 2;
|
const int multiply_latency = 3;
|
const int multiply_latency = 3;
|
const int load_latency = 3;
|
const int load_latency = 3;
|
const int libcall_size_cost = 5;
|
const int libcall_size_cost = 5;
|
|
|
/* Determine if we can handle the given mode size in a single instruction. */
|
/* Determine if we can handle the given mode size in a single instruction. */
|
small_mode = (mode == QImode) || (mode == HImode) || (mode == SImode);
|
small_mode = (mode == QImode) || (mode == HImode) || (mode == SImode);
|
|
|
switch (code)
|
switch (code)
|
{
|
{
|
|
|
case PLUS:
|
case PLUS:
|
case MINUS:
|
case MINUS:
|
case AND:
|
case AND:
|
case IOR:
|
case IOR:
|
case XOR:
|
case XOR:
|
case NOT:
|
case NOT:
|
case NEG:
|
case NEG:
|
if (!speed)
|
if (!speed)
|
*total = COSTS_N_INSNS (LM32_NUM_REGS (mode));
|
*total = COSTS_N_INSNS (LM32_NUM_REGS (mode));
|
else
|
else
|
*total =
|
*total =
|
COSTS_N_INSNS (arithmetic_latency + (LM32_NUM_REGS (mode) - 1));
|
COSTS_N_INSNS (arithmetic_latency + (LM32_NUM_REGS (mode) - 1));
|
break;
|
break;
|
|
|
case COMPARE:
|
case COMPARE:
|
if (small_mode)
|
if (small_mode)
|
{
|
{
|
if (!speed)
|
if (!speed)
|
*total = COSTS_N_INSNS (1);
|
*total = COSTS_N_INSNS (1);
|
else
|
else
|
*total = COSTS_N_INSNS (compare_latency);
|
*total = COSTS_N_INSNS (compare_latency);
|
}
|
}
|
else
|
else
|
{
|
{
|
/* FIXME. Guessing here. */
|
/* FIXME. Guessing here. */
|
*total = COSTS_N_INSNS (LM32_NUM_REGS (mode) * (2 + 3) / 2);
|
*total = COSTS_N_INSNS (LM32_NUM_REGS (mode) * (2 + 3) / 2);
|
}
|
}
|
break;
|
break;
|
|
|
case ASHIFT:
|
case ASHIFT:
|
case ASHIFTRT:
|
case ASHIFTRT:
|
case LSHIFTRT:
|
case LSHIFTRT:
|
if (TARGET_BARREL_SHIFT_ENABLED && small_mode)
|
if (TARGET_BARREL_SHIFT_ENABLED && small_mode)
|
{
|
{
|
if (!speed)
|
if (!speed)
|
*total = COSTS_N_INSNS (1);
|
*total = COSTS_N_INSNS (1);
|
else
|
else
|
*total = COSTS_N_INSNS (shift_latency);
|
*total = COSTS_N_INSNS (shift_latency);
|
}
|
}
|
else if (TARGET_BARREL_SHIFT_ENABLED)
|
else if (TARGET_BARREL_SHIFT_ENABLED)
|
{
|
{
|
/* FIXME: Guessing here. */
|
/* FIXME: Guessing here. */
|
*total = COSTS_N_INSNS (LM32_NUM_REGS (mode) * 4);
|
*total = COSTS_N_INSNS (LM32_NUM_REGS (mode) * 4);
|
}
|
}
|
else if (small_mode && GET_CODE (XEXP (x, 1)) == CONST_INT)
|
else if (small_mode && GET_CODE (XEXP (x, 1)) == CONST_INT)
|
{
|
{
|
*total = COSTS_N_INSNS (INTVAL (XEXP (x, 1)));
|
*total = COSTS_N_INSNS (INTVAL (XEXP (x, 1)));
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Libcall. */
|
/* Libcall. */
|
if (!speed)
|
if (!speed)
|
*total = COSTS_N_INSNS (libcall_size_cost);
|
*total = COSTS_N_INSNS (libcall_size_cost);
|
else
|
else
|
*total = COSTS_N_INSNS (100);
|
*total = COSTS_N_INSNS (100);
|
}
|
}
|
break;
|
break;
|
|
|
case MULT:
|
case MULT:
|
if (TARGET_MULTIPLY_ENABLED && small_mode)
|
if (TARGET_MULTIPLY_ENABLED && small_mode)
|
{
|
{
|
if (!speed)
|
if (!speed)
|
*total = COSTS_N_INSNS (1);
|
*total = COSTS_N_INSNS (1);
|
else
|
else
|
*total = COSTS_N_INSNS (multiply_latency);
|
*total = COSTS_N_INSNS (multiply_latency);
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Libcall. */
|
/* Libcall. */
|
if (!speed)
|
if (!speed)
|
*total = COSTS_N_INSNS (libcall_size_cost);
|
*total = COSTS_N_INSNS (libcall_size_cost);
|
else
|
else
|
*total = COSTS_N_INSNS (100);
|
*total = COSTS_N_INSNS (100);
|
}
|
}
|
break;
|
break;
|
|
|
case DIV:
|
case DIV:
|
case MOD:
|
case MOD:
|
case UDIV:
|
case UDIV:
|
case UMOD:
|
case UMOD:
|
if (TARGET_DIVIDE_ENABLED && small_mode)
|
if (TARGET_DIVIDE_ENABLED && small_mode)
|
{
|
{
|
if (!speed)
|
if (!speed)
|
*total = COSTS_N_INSNS (1);
|
*total = COSTS_N_INSNS (1);
|
else
|
else
|
{
|
{
|
if (GET_CODE (XEXP (x, 1)) == CONST_INT)
|
if (GET_CODE (XEXP (x, 1)) == CONST_INT)
|
{
|
{
|
int cycles = 0;
|
int cycles = 0;
|
unsigned HOST_WIDE_INT i = INTVAL (XEXP (x, 1));
|
unsigned HOST_WIDE_INT i = INTVAL (XEXP (x, 1));
|
|
|
while (i)
|
while (i)
|
{
|
{
|
i >>= 2;
|
i >>= 2;
|
cycles++;
|
cycles++;
|
}
|
}
|
if (IN_RANGE (i, 0, 65536))
|
if (IN_RANGE (i, 0, 65536))
|
*total = COSTS_N_INSNS (1 + 1 + cycles);
|
*total = COSTS_N_INSNS (1 + 1 + cycles);
|
else
|
else
|
*total = COSTS_N_INSNS (2 + 1 + cycles);
|
*total = COSTS_N_INSNS (2 + 1 + cycles);
|
return true;
|
return true;
|
}
|
}
|
else if (GET_CODE (XEXP (x, 1)) == REG)
|
else if (GET_CODE (XEXP (x, 1)) == REG)
|
{
|
{
|
*total = COSTS_N_INSNS (1 + GET_MODE_SIZE (mode) / 2);
|
*total = COSTS_N_INSNS (1 + GET_MODE_SIZE (mode) / 2);
|
return true;
|
return true;
|
}
|
}
|
else
|
else
|
{
|
{
|
*total = COSTS_N_INSNS (1 + GET_MODE_SIZE (mode) / 2);
|
*total = COSTS_N_INSNS (1 + GET_MODE_SIZE (mode) / 2);
|
return false;
|
return false;
|
}
|
}
|
}
|
}
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Libcall. */
|
/* Libcall. */
|
if (!speed)
|
if (!speed)
|
*total = COSTS_N_INSNS (libcall_size_cost);
|
*total = COSTS_N_INSNS (libcall_size_cost);
|
else
|
else
|
*total = COSTS_N_INSNS (100);
|
*total = COSTS_N_INSNS (100);
|
}
|
}
|
break;
|
break;
|
|
|
case HIGH:
|
case HIGH:
|
case LO_SUM:
|
case LO_SUM:
|
if (!speed)
|
if (!speed)
|
*total = COSTS_N_INSNS (1);
|
*total = COSTS_N_INSNS (1);
|
else
|
else
|
*total = COSTS_N_INSNS (arithmetic_latency);
|
*total = COSTS_N_INSNS (arithmetic_latency);
|
break;
|
break;
|
|
|
case ZERO_EXTEND:
|
case ZERO_EXTEND:
|
if (MEM_P (XEXP (x, 0)))
|
if (MEM_P (XEXP (x, 0)))
|
*total = COSTS_N_INSNS (0);
|
*total = COSTS_N_INSNS (0);
|
else if (small_mode)
|
else if (small_mode)
|
{
|
{
|
if (!speed)
|
if (!speed)
|
*total = COSTS_N_INSNS (1);
|
*total = COSTS_N_INSNS (1);
|
else
|
else
|
*total = COSTS_N_INSNS (arithmetic_latency);
|
*total = COSTS_N_INSNS (arithmetic_latency);
|
}
|
}
|
else
|
else
|
*total = COSTS_N_INSNS (LM32_NUM_REGS (mode) / 2);
|
*total = COSTS_N_INSNS (LM32_NUM_REGS (mode) / 2);
|
break;
|
break;
|
|
|
case CONST_INT:
|
case CONST_INT:
|
{
|
{
|
switch (outer_code)
|
switch (outer_code)
|
{
|
{
|
case HIGH:
|
case HIGH:
|
case LO_SUM:
|
case LO_SUM:
|
*total = COSTS_N_INSNS (0);
|
*total = COSTS_N_INSNS (0);
|
return true;
|
return true;
|
|
|
case AND:
|
case AND:
|
case XOR:
|
case XOR:
|
case IOR:
|
case IOR:
|
case ASHIFT:
|
case ASHIFT:
|
case ASHIFTRT:
|
case ASHIFTRT:
|
case LSHIFTRT:
|
case LSHIFTRT:
|
case ROTATE:
|
case ROTATE:
|
case ROTATERT:
|
case ROTATERT:
|
if (satisfies_constraint_L (x))
|
if (satisfies_constraint_L (x))
|
*total = COSTS_N_INSNS (0);
|
*total = COSTS_N_INSNS (0);
|
else
|
else
|
*total = COSTS_N_INSNS (2);
|
*total = COSTS_N_INSNS (2);
|
return true;
|
return true;
|
|
|
case SET:
|
case SET:
|
case PLUS:
|
case PLUS:
|
case MINUS:
|
case MINUS:
|
case COMPARE:
|
case COMPARE:
|
if (satisfies_constraint_K (x))
|
if (satisfies_constraint_K (x))
|
*total = COSTS_N_INSNS (0);
|
*total = COSTS_N_INSNS (0);
|
else
|
else
|
*total = COSTS_N_INSNS (2);
|
*total = COSTS_N_INSNS (2);
|
return true;
|
return true;
|
|
|
case MULT:
|
case MULT:
|
if (TARGET_MULTIPLY_ENABLED)
|
if (TARGET_MULTIPLY_ENABLED)
|
{
|
{
|
if (satisfies_constraint_K (x))
|
if (satisfies_constraint_K (x))
|
*total = COSTS_N_INSNS (0);
|
*total = COSTS_N_INSNS (0);
|
else
|
else
|
*total = COSTS_N_INSNS (2);
|
*total = COSTS_N_INSNS (2);
|
return true;
|
return true;
|
}
|
}
|
/* Fall through. */
|
/* Fall through. */
|
|
|
default:
|
default:
|
if (satisfies_constraint_K (x))
|
if (satisfies_constraint_K (x))
|
*total = COSTS_N_INSNS (1);
|
*total = COSTS_N_INSNS (1);
|
else
|
else
|
*total = COSTS_N_INSNS (2);
|
*total = COSTS_N_INSNS (2);
|
return true;
|
return true;
|
}
|
}
|
}
|
}
|
|
|
case SYMBOL_REF:
|
case SYMBOL_REF:
|
case CONST:
|
case CONST:
|
switch (outer_code)
|
switch (outer_code)
|
{
|
{
|
case HIGH:
|
case HIGH:
|
case LO_SUM:
|
case LO_SUM:
|
*total = COSTS_N_INSNS (0);
|
*total = COSTS_N_INSNS (0);
|
return true;
|
return true;
|
|
|
case MEM:
|
case MEM:
|
case SET:
|
case SET:
|
if (g_switch_value)
|
if (g_switch_value)
|
{
|
{
|
*total = COSTS_N_INSNS (0);
|
*total = COSTS_N_INSNS (0);
|
return true;
|
return true;
|
}
|
}
|
break;
|
break;
|
}
|
}
|
/* Fall through. */
|
/* Fall through. */
|
|
|
case LABEL_REF:
|
case LABEL_REF:
|
case CONST_DOUBLE:
|
case CONST_DOUBLE:
|
*total = COSTS_N_INSNS (2);
|
*total = COSTS_N_INSNS (2);
|
return true;
|
return true;
|
|
|
case SET:
|
case SET:
|
*total = COSTS_N_INSNS (1);
|
*total = COSTS_N_INSNS (1);
|
break;
|
break;
|
|
|
case MEM:
|
case MEM:
|
if (!speed)
|
if (!speed)
|
*total = COSTS_N_INSNS (1);
|
*total = COSTS_N_INSNS (1);
|
else
|
else
|
*total = COSTS_N_INSNS (load_latency);
|
*total = COSTS_N_INSNS (load_latency);
|
break;
|
break;
|
|
|
}
|
}
|
|
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Implemenent TARGET_CAN_ELIMINATE. */
|
/* Implemenent TARGET_CAN_ELIMINATE. */
|
|
|
bool
|
bool
|
lm32_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to)
|
lm32_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to)
|
{
|
{
|
return (to == STACK_POINTER_REGNUM && frame_pointer_needed) ? false : true;
|
return (to == STACK_POINTER_REGNUM && frame_pointer_needed) ? false : true;
|
}
|
}
|
|
|
/* Implement TARGET_LEGITIMATE_ADDRESS_P. */
|
/* Implement TARGET_LEGITIMATE_ADDRESS_P. */
|
|
|
static bool
|
static bool
|
lm32_legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x, bool strict)
|
lm32_legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x, bool strict)
|
{
|
{
|
/* (rM) */
|
/* (rM) */
|
if (strict && REG_P (x) && STRICT_REG_OK_FOR_BASE_P (x))
|
if (strict && REG_P (x) && STRICT_REG_OK_FOR_BASE_P (x))
|
return true;
|
return true;
|
if (!strict && REG_P (x) && NONSTRICT_REG_OK_FOR_BASE_P (x))
|
if (!strict && REG_P (x) && NONSTRICT_REG_OK_FOR_BASE_P (x))
|
return true;
|
return true;
|
|
|
/* (rM)+literal) */
|
/* (rM)+literal) */
|
if (GET_CODE (x) == PLUS
|
if (GET_CODE (x) == PLUS
|
&& REG_P (XEXP (x, 0))
|
&& REG_P (XEXP (x, 0))
|
&& ((strict && STRICT_REG_OK_FOR_BASE_P (XEXP (x, 0)))
|
&& ((strict && STRICT_REG_OK_FOR_BASE_P (XEXP (x, 0)))
|
|| (!strict && NONSTRICT_REG_OK_FOR_BASE_P (XEXP (x, 0))))
|
|| (!strict && NONSTRICT_REG_OK_FOR_BASE_P (XEXP (x, 0))))
|
&& GET_CODE (XEXP (x, 1)) == CONST_INT
|
&& GET_CODE (XEXP (x, 1)) == CONST_INT
|
&& satisfies_constraint_K (XEXP ((x), 1)))
|
&& satisfies_constraint_K (XEXP ((x), 1)))
|
return true;
|
return true;
|
|
|
/* gp(sym) */
|
/* gp(sym) */
|
if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_SMALL_P (x))
|
if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_SMALL_P (x))
|
return true;
|
return true;
|
|
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Check a move is not memory to memory. */
|
/* Check a move is not memory to memory. */
|
|
|
bool
|
bool
|
lm32_move_ok (enum machine_mode mode, rtx operands[2]) {
|
lm32_move_ok (enum machine_mode mode, rtx operands[2]) {
|
if (memory_operand (operands[0], mode))
|
if (memory_operand (operands[0], mode))
|
return register_or_zero_operand (operands[1], mode);
|
return register_or_zero_operand (operands[1], mode);
|
return true;
|
return true;
|
}
|
}
|
|
|
/* Implement LEGITIMATE_CONSTANT_P. */
|
/* Implement LEGITIMATE_CONSTANT_P. */
|
|
|
bool
|
bool
|
lm32_legitimate_constant_p (rtx x)
|
lm32_legitimate_constant_p (rtx x)
|
{
|
{
|
/* 32-bit addresses require multiple instructions. */
|
/* 32-bit addresses require multiple instructions. */
|
if (!flag_pic && reloc_operand (x, GET_MODE (x)))
|
if (!flag_pic && reloc_operand (x, GET_MODE (x)))
|
return false;
|
return false;
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|