OpenCores
URL https://opencores.org/ocsvn/openrisc_2011-10-31/openrisc_2011-10-31/trunk

Subversion Repositories openrisc_2011-10-31

[/] [openrisc/] [tags/] [gnu-src/] [gcc-4.5.1/] [gcc-4.5.1-or32-1.0rc2/] [gcc/] [config/] [lm32/] [lm32.c] - Diff between revs 282 and 384

Only display areas with differences | Details | Blame | View Log

Rev 282 Rev 384
/* 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 (&current_frame_info, 0);
        expand_save_restore (&current_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 (&current_frame_info, 1);
        expand_save_restore (&current_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;
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.