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

Subversion Repositories openrisc_2011-10-31

[/] [openrisc/] [trunk/] [gnu-src/] [gcc-4.5.1/] [gcc/] [config/] [or32/] [or32.c] - Diff between revs 444 and 452

Go to most recent revision | Only display areas with differences | Details | Blame | View Log

Rev 444 Rev 452
/* Subroutines for insn-output.c for GNU compiler.  OpenRISC 1000 version.
/* Subroutines for insn-output.c for GNU compiler.  OpenRISC 1000 version.
   Copyright (C) 1987, 1992, 1997, 1999, 2000, 2001, 2002, 2003, 2004,
   Copyright (C) 1987, 1992, 1997, 1999, 2000, 2001, 2002, 2003, 2004,
   2005, 2006, 2007, 2008, 2009, 2010  Free Software Foundation, Inc
   2005, 2006, 2007, 2008, 2009, 2010  Free Software Foundation, Inc
   Copyright (C) 2010 Embecosm Limited
   Copyright (C) 2010 Embecosm Limited
 
 
   Contributed by Damjan Lampret <damjanl@bsemi.com> in 1999.
   Contributed by Damjan Lampret <damjanl@bsemi.com> in 1999.
   Major optimizations by Matjaz Breskvar <matjazb@bsemi.com> in 2005.
   Major optimizations by Matjaz Breskvar <matjazb@bsemi.com> in 2005.
   Updated for GCC 4.5 by Jeremy Bennett <jeremy.bennett@embecoms.com>
   Updated for GCC 4.5 by Jeremy Bennett <jeremy.bennett@embecoms.com>
   and Joern Rennecke <joern.rennecke@embecosm.com> in 2010.
   and Joern Rennecke <joern.rennecke@embecosm.com> in 2010.
 
 
   This file is part of GNU CC.
   This file is part of GNU CC.
 
 
   This program is free software; you can redistribute it and/or modify it
   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the Free
   under the terms of the GNU General Public License as published by the Free
   Software Foundation; either version 3 of the License, or (at your option)
   Software Foundation; either version 3 of the License, or (at your option)
   any later version.
   any later version.
 
 
   This program is distributed in the hope that it will be useful, but WITHOUT
   This program is distributed in the hope that it will be useful, but WITHOUT
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
   more details.
   more details.
 
 
   You should have received a copy of the GNU General Public License along
   You should have received a copy of the GNU General Public License along
   with this program.  If not, see <http://www.gnu.org/licenses/>. */
   with this program.  If not, see <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 "tree.h"
#include "tree.h"
#include "obstack.h"
#include "obstack.h"
#include "regs.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "hard-reg-set.h"
#include "real.h"
#include "real.h"
#include "insn-config.h"
#include "insn-config.h"
#include "conditions.h"
#include "conditions.h"
#include "output.h"
#include "output.h"
#include "insn-attr.h"
#include "insn-attr.h"
#include "flags.h"
#include "flags.h"
#include "reload.h"
#include "reload.h"
#include "function.h"
#include "function.h"
#include "expr.h"
#include "expr.h"
#include "toplev.h"
#include "toplev.h"
#include "recog.h"
#include "recog.h"
#include "ggc.h"
#include "ggc.h"
#include "except.h"
#include "except.h"
#include "integrate.h"
#include "integrate.h"
#include "tm_p.h"
#include "tm_p.h"
#include "target.h"
#include "target.h"
#include "target-def.h"
#include "target-def.h"
#include "debug.h"
#include "debug.h"
#include "langhooks.h"
#include "langhooks.h"
#include "df.h"
#include "df.h"
#include "dwarf2.h"
#include "dwarf2.h"
 
 
 
 
/* ========================================================================== */
/* ========================================================================== */
/* Local macros                                                               */
/* Local macros                                                               */
 
 
/* Construct a l.movhi instruction for the given reg and value */
/* Construct a l.movhi instruction for the given reg and value */
#define OR32_MOVHI(rd, k)                                               \
#define OR32_MOVHI(rd, k)                                               \
  ((0x6 << 26) | ((rd) << 21) | (k))
  ((0x6 << 26) | ((rd) << 21) | (k))
 
 
/* Construct a l.ori instruction for the given two regs and value */
/* Construct a l.ori instruction for the given two regs and value */
#define OR32_ORI(rd, ra, k)                                             \
#define OR32_ORI(rd, ra, k)                                             \
  ((0x2a << 26) | ((rd) << 21) | ((ra) << 16) | (k))
  ((0x2a << 26) | ((rd) << 21) | ((ra) << 16) | (k))
 
 
/* Construct a l.lwz instruction for the given two registers and offset */
/* Construct a l.lwz instruction for the given two registers and offset */
#define OR32_LWZ(rd, ra, i)                                             \
#define OR32_LWZ(rd, ra, i)                                             \
  ((0x21 << 26) | ((rd) << 21) | ((ra) << 16) | (i))
  ((0x21 << 26) | ((rd) << 21) | ((ra) << 16) | (i))
 
 
/* Construct a l.jr instruction for the given register */
/* Construct a l.jr instruction for the given register */
#define OR32_JR(rb)                                                     \
#define OR32_JR(rb)                                                     \
  ((0x11 << 26) | ((rb) << 11))
  ((0x11 << 26) | ((rb) << 11))
 
 
/* ========================================================================== */
/* ========================================================================== */
/* Static variables (i.e. global to this file only.                           */
/* Static variables (i.e. global to this file only.                           */
 
 
 
 
/* Save information from a "cmpxx" pattern until the branch or scc is
/* Save information from a "cmpxx" pattern until the branch or scc is
   emitted. These record the two operands of the "cmpxx" */
   emitted. These record the two operands of the "cmpxx" */
rtx  or32_compare_op0;
rtx  or32_compare_op0;
rtx  or32_compare_op1;
rtx  or32_compare_op1;
 
 
/*!Stack layout we use for pushing and poping saved registers */
/*!Stack layout we use for pushing and poping saved registers */
static struct
static struct
{
{
  bool save_lr_p;
  bool save_lr_p;
  int lr_save_offset;
  int lr_save_offset;
  bool save_fp_p;
  bool save_fp_p;
  int fp_save_offset;
  int fp_save_offset;
  int gpr_size;
  int gpr_size;
  int gpr_offset;
  int gpr_offset;
  int total_size;
  int total_size;
  int vars_size;
  int vars_size;
  int args_size;
  int args_size;
  int gpr_frame;
  int gpr_frame;
  int late_frame;
  int late_frame;
  HOST_WIDE_INT mask;
  HOST_WIDE_INT mask;
}  frame_info;
}  frame_info;
 
 
 
 
/* ========================================================================== */
/* ========================================================================== */
/* Local (i.e. static) utility functions */
/* Local (i.e. static) utility functions */
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Must the current function save a register?
/*!Must the current function save a register?
 
 
   @param[in] regno  The register to consider.
   @param[in] regno  The register to consider.
 
 
   @return  Non-zero (TRUE) if current function must save "regno", zero
   @return  Non-zero (TRUE) if current function must save "regno", zero
            (FALSE) otherwise.                                                */
            (FALSE) otherwise.                                                */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static bool
static bool
or32_save_reg_p (int regno)
or32_save_reg_p (int regno)
{
{
  /* No need to save the faked cc0 register.  */
  /* No need to save the faked cc0 register.  */
  if (regno == OR32_FLAGS_REG)
  if (regno == OR32_FLAGS_REG)
    return false;
    return false;
 
 
  /* Check call-saved registers.  */
  /* Check call-saved registers.  */
  if (df_regs_ever_live_p(regno) && !call_used_regs[regno])
  if (df_regs_ever_live_p(regno) && !call_used_regs[regno])
    return true;
    return true;
 
 
  /* We need to save the old frame pointer before setting up a new
  /* We need to save the old frame pointer before setting up a new
     one.  */
     one.  */
  if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)
  if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)
    return true;
    return true;
 
 
  /* We need to save the incoming return address if it is ever clobbered
  /* We need to save the incoming return address if it is ever clobbered
     within the function.  */
     within the function.  */
  if (regno == LINK_REGNUM && df_regs_ever_live_p(regno))
  if (regno == LINK_REGNUM && df_regs_ever_live_p(regno))
    return true;
    return true;
 
 
  return false;
  return false;
 
 
}       /* or32_save_reg_p () */
}       /* or32_save_reg_p () */
 
 
bool
bool
or32_save_reg_p_cached (int regno)
or32_save_reg_p_cached (int regno)
{
{
  return (frame_info.mask & ((HOST_WIDE_INT) 1 << regno)) != 0;
  return (frame_info.mask & ((HOST_WIDE_INT) 1 << regno)) != 0;
}
}
 
 
/* N.B. contrary to the ISA documentation, the stack includes the outgoing
/* N.B. contrary to the ISA documentation, the stack includes the outgoing
   arguments.  */
   arguments.  */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Compute full frame size and layout.
/*!Compute full frame size and layout.
 
 
   Store information in "frame_info".
   Store information in "frame_info".
 
 
   @param[in] size  The size of the function's local variables.
   @param[in] size  The size of the function's local variables.
 
 
   @return  Total size of stack frame.                                        */
   @return  Total size of stack frame.                                        */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static HOST_WIDE_INT
static HOST_WIDE_INT
or32_compute_frame_size (HOST_WIDE_INT size)
or32_compute_frame_size (HOST_WIDE_INT size)
{
{
  HOST_WIDE_INT args_size;
  HOST_WIDE_INT args_size;
  HOST_WIDE_INT vars_size;
  HOST_WIDE_INT vars_size;
  HOST_WIDE_INT stack_offset;
  HOST_WIDE_INT stack_offset;
  HOST_WIDE_INT save_size;
  HOST_WIDE_INT save_size;
  bool interrupt_p = false;
  bool interrupt_p = false;
 
 
  int regno;
  int regno;
 
 
  args_size = crtl->outgoing_args_size;
  args_size = crtl->outgoing_args_size;
  vars_size = OR32_ALIGN (size, 4);
  vars_size = OR32_ALIGN (size, 4);
 
 
  frame_info.args_size = args_size;
  frame_info.args_size = args_size;
  frame_info.vars_size = vars_size;
  frame_info.vars_size = vars_size;
  frame_info.gpr_frame = interrupt_p ? or32_redzone : 0;
  frame_info.gpr_frame = interrupt_p ? or32_redzone : 0;
 
 
  /* If the function has local variables, we're committed to
  /* If the function has local variables, we're committed to
     allocating it anyway.  Otherwise reclaim it here.  */
     allocating it anyway.  Otherwise reclaim it here.  */
  /* FIXME: Verify this.  Got if from the MIPS port.  */
  /* FIXME: Verify this.  Got if from the MIPS port.  */
  if (vars_size == 0 && current_function_is_leaf)
  if (vars_size == 0 && current_function_is_leaf)
    args_size = 0;
    args_size = 0;
 
 
  stack_offset = 0;
  stack_offset = 0;
 
 
  /* Save link register right at the bottom.  */
  /* Save link register right at the bottom.  */
  if (or32_save_reg_p (LINK_REGNUM))
  if (or32_save_reg_p (LINK_REGNUM))
    {
    {
      stack_offset = stack_offset - UNITS_PER_WORD;
      stack_offset = stack_offset - UNITS_PER_WORD;
      frame_info.lr_save_offset = stack_offset;
      frame_info.lr_save_offset = stack_offset;
      frame_info.save_lr_p = true;
      frame_info.save_lr_p = true;
    }
    }
  else
  else
    frame_info.save_lr_p = false;
    frame_info.save_lr_p = false;
 
 
  /* Save frame pointer right after possible link register.  */
  /* Save frame pointer right after possible link register.  */
  if (frame_pointer_needed)
  if (frame_pointer_needed)
    {
    {
      stack_offset = stack_offset - UNITS_PER_WORD;
      stack_offset = stack_offset - UNITS_PER_WORD;
      frame_info.fp_save_offset = stack_offset;
      frame_info.fp_save_offset = stack_offset;
      frame_info.save_fp_p = true;
      frame_info.save_fp_p = true;
    }
    }
  else
  else
    frame_info.save_fp_p = false;
    frame_info.save_fp_p = false;
 
 
  frame_info.gpr_size = 0;
  frame_info.gpr_size = 0;
  frame_info.mask = 0;
  frame_info.mask = 0;
 
 
  for (regno = 0; regno <= OR32_LAST_ACTUAL_REG; regno++)
  for (regno = 0; regno <= OR32_LAST_ACTUAL_REG; regno++)
    {
    {
      if (regno == LINK_REGNUM
      if (regno == LINK_REGNUM
          || (frame_pointer_needed && regno == HARD_FRAME_POINTER_REGNUM))
          || (frame_pointer_needed && regno == HARD_FRAME_POINTER_REGNUM))
        /* These have already been saved if so needed.  */
        /* These have already been saved if so needed.  */
        continue;
        continue;
 
 
      if (or32_save_reg_p (regno))
      if (or32_save_reg_p (regno))
        {
        {
          frame_info.gpr_size += UNITS_PER_WORD;
          frame_info.gpr_size += UNITS_PER_WORD;
          frame_info.mask |= ((HOST_WIDE_INT) 1 << regno);
          frame_info.mask |= ((HOST_WIDE_INT) 1 << regno);
        }
        }
    }
    }
 
 
  save_size = (frame_info.gpr_size
  save_size = (frame_info.gpr_size
               + (frame_info.save_fp_p ? UNITS_PER_WORD : 0)
               + (frame_info.save_fp_p ? UNITS_PER_WORD : 0)
               + (frame_info.save_lr_p ? UNITS_PER_WORD : 0));
               + (frame_info.save_lr_p ? UNITS_PER_WORD : 0));
  frame_info.total_size = save_size + vars_size + args_size;
  frame_info.total_size = save_size + vars_size + args_size;
  gcc_assert (PROLOGUE_TMP != STATIC_CHAIN_REGNUM);
  gcc_assert (PROLOGUE_TMP != STATIC_CHAIN_REGNUM);
  if (frame_info.total_size > 32767 && interrupt_p)
  if (frame_info.total_size > 32767 && interrupt_p)
    {
    {
      int n_extra
      int n_extra
        = (!!(~frame_info.mask && 1 << PROLOGUE_TMP)
        = (!!(~frame_info.mask && 1 << PROLOGUE_TMP)
           + !!(~frame_info.mask & 1 << EPILOGUE_TMP)) * UNITS_PER_WORD;
           + !!(~frame_info.mask & 1 << EPILOGUE_TMP)) * UNITS_PER_WORD;
 
 
      save_size += n_extra;
      save_size += n_extra;
      frame_info.gpr_size += n_extra;
      frame_info.gpr_size += n_extra;
      frame_info.total_size += n_extra;
      frame_info.total_size += n_extra;
      frame_info.mask |= (1 << PROLOGUE_TMP) | (1 << EPILOGUE_TMP);
      frame_info.mask |= (1 << PROLOGUE_TMP) | (1 << EPILOGUE_TMP);
    }
    }
 
 
  stack_offset -= frame_info.gpr_size;
  stack_offset -= frame_info.gpr_size;
  frame_info.gpr_offset = stack_offset;
  frame_info.gpr_offset = stack_offset;
  frame_info.late_frame = frame_info.total_size;
  frame_info.late_frame = frame_info.total_size;
 
 
  if (save_size > or32_redzone
  if (save_size > or32_redzone
      || (frame_info.gpr_frame
      || (frame_info.gpr_frame
          && (frame_info.gpr_frame + frame_info.late_frame <= 32767)))
          && (frame_info.gpr_frame + frame_info.late_frame <= 32767)))
    {
    {
      if (frame_info.gpr_frame + frame_info.late_frame <= 32767)
      if (frame_info.gpr_frame + frame_info.late_frame <= 32767)
        save_size = frame_info.total_size;
        save_size = frame_info.total_size;
      frame_info.gpr_frame += save_size;
      frame_info.gpr_frame += save_size;
      frame_info.lr_save_offset += save_size;
      frame_info.lr_save_offset += save_size;
      frame_info.fp_save_offset += save_size;
      frame_info.fp_save_offset += save_size;
      frame_info.gpr_offset += save_size;
      frame_info.gpr_offset += save_size;
      frame_info.late_frame -= save_size;
      frame_info.late_frame -= save_size;
      /* FIXME: check in TARGET_OVERRIDE_OPTIONS for invalid or32_redzone.  */
      /* FIXME: check in TARGET_OVERRIDE_OPTIONS for invalid or32_redzone.  */
      gcc_assert (frame_info.gpr_frame <= 32767);
      gcc_assert (frame_info.gpr_frame <= 32767);
      gcc_assert ((frame_info.gpr_frame & 3) == 0);
      gcc_assert ((frame_info.gpr_frame & 3) == 0);
    }
    }
 
 
  return frame_info.total_size;
  return frame_info.total_size;
 
 
}       /* or32_compute_frame_size () */
}       /* or32_compute_frame_size () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Emit a frame related insn.
/*!Emit a frame related insn.
 
 
   Same as emit_insn, but sets RTX_FRAME_RELATED_P to one. Getting this right
   Same as emit_insn, but sets RTX_FRAME_RELATED_P to one. Getting this right
   will matter for DWARF 2 output, if prologues are handled via the "prologue"
   will matter for DWARF 2 output, if prologues are handled via the "prologue"
   pattern rather than target hooks.
   pattern rather than target hooks.
 
 
   @param[in] insn  The insn to emit.
   @param[in] insn  The insn to emit.
 
 
   @return  The RTX for the emitted insn.                                     */
   @return  The RTX for the emitted insn.                                     */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static rtx
static rtx
emit_frame_insn (rtx insn)
emit_frame_insn (rtx insn)
{
{
  insn = emit_insn (insn);
  insn = emit_insn (insn);
  RTX_FRAME_RELATED_P (insn) = 1;
  RTX_FRAME_RELATED_P (insn) = 1;
  return (insn);
  return (insn);
 
 
}       /* emit_frame_insn () */
}       /* emit_frame_insn () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* Generate a RTX for the indexed memory address based on stack_pointer_rtx
/* Generate a RTX for the indexed memory address based on stack_pointer_rtx
   and a displacement
   and a displacement
 
 
   @param[in] disp  The displacement
   @param[in] disp  The displacement
 
 
   @return  The RTX for the generated address.                                */
   @return  The RTX for the generated address.                                */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static rtx
static rtx
stack_disp_mem (HOST_WIDE_INT disp)
stack_disp_mem (HOST_WIDE_INT disp)
{
{
  return gen_frame_mem (Pmode, plus_constant (stack_pointer_rtx, disp));
  return gen_frame_mem (Pmode, plus_constant (stack_pointer_rtx, disp));
}
}
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Generate insn patterns to do an integer compare of operands.
/*!Generate insn patterns to do an integer compare of operands.
 
 
   @param[in] code  RTX for the condition code.
   @param[in] code  RTX for the condition code.
   @param[in] op0   RTX for the first operand.
   @param[in] op0   RTX for the first operand.
   @param[in] op1   RTX for the second operand.
   @param[in] op1   RTX for the second operand.
 
 
   @return  RTX for the comparison.                                           */
   @return  RTX for the comparison.                                           */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static rtx
static rtx
or32_expand_int_compare (enum rtx_code  code,
or32_expand_int_compare (enum rtx_code  code,
                         rtx            op0,
                         rtx            op0,
                         rtx            op1)
                         rtx            op1)
{
{
  enum machine_mode cmpmode;
  enum machine_mode cmpmode;
  rtx tmp, flags;
  rtx tmp, flags;
 
 
  cmpmode = SELECT_CC_MODE (code, op0, op1);
  cmpmode = SELECT_CC_MODE (code, op0, op1);
  flags = gen_rtx_REG (cmpmode, OR32_FLAGS_REG);
  flags = gen_rtx_REG (cmpmode, OR32_FLAGS_REG);
 
 
  /* This is very simple, but making the interface the same as in the
  /* This is very simple, but making the interface the same as in the
     FP case makes the rest of the code easier.  */
     FP case makes the rest of the code easier.  */
  tmp = gen_rtx_COMPARE (cmpmode, op0, op1);
  tmp = gen_rtx_COMPARE (cmpmode, op0, op1);
  emit_insn (gen_rtx_SET (VOIDmode, flags, tmp));
  emit_insn (gen_rtx_SET (VOIDmode, flags, tmp));
 
 
  /* Return the test that should be put into the flags user, i.e.
  /* Return the test that should be put into the flags user, i.e.
     the bcc, scc, or cmov instruction.  */
     the bcc, scc, or cmov instruction.  */
  return gen_rtx_fmt_ee (code, VOIDmode, flags, const0_rtx);
  return gen_rtx_fmt_ee (code, VOIDmode, flags, const0_rtx);
 
 
}       /* or32_expand_int_compare () */
}       /* or32_expand_int_compare () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Generate insn patterns to do an integer compare of operands.
/*!Generate insn patterns to do an integer compare of operands.
 
 
   We only deal with the case where the comparison is an integer
   We only deal with the case where the comparison is an integer
   comparison. This wrapper function potentially allows reuse for non-integer
   comparison. This wrapper function potentially allows reuse for non-integer
   comparison in the future.
   comparison in the future.
 
 
   @param[in] code  RTX for the condition code.
   @param[in] code  RTX for the condition code.
   @param[in] op0   RTX for the first operand.
   @param[in] op0   RTX for the first operand.
   @param[in] op1   RTX for the second operand.
   @param[in] op1   RTX for the second operand.
 
 
   @return  RTX for the comparison.                                           */
   @return  RTX for the comparison.                                           */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static rtx
static rtx
or32_expand_compare (enum rtx_code code, rtx op0, rtx op1)
or32_expand_compare (enum rtx_code code, rtx op0, rtx op1)
{
{
  return or32_expand_int_compare (code, op0, op1);
  return or32_expand_int_compare (code, op0, op1);
 
 
}       /* or32_expand_compare () */
}       /* or32_expand_compare () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Emit insns to use the l.cmov instruction
/*!Emit insns to use the l.cmov instruction
 
 
   Emit a compare and then cmov. Only works for integer first operand.
   Emit a compare and then cmov. Only works for integer first operand.
 
 
   @param[in] dest        RTX for the destination operand.
   @param[in] dest        RTX for the destination operand.
   @param[in] op          RTX for the comparison operation
   @param[in] op          RTX for the comparison operation
   @param[in] true_cond   RTX to move to dest if condition is TRUE.
   @param[in] true_cond   RTX to move to dest if condition is TRUE.
   @param[in] false_cond  RTX to move to dest if condition is FALSE.
   @param[in] false_cond  RTX to move to dest if condition is FALSE.
 
 
   @return  Non-zero (TRUE) if insns were emitted, zero (FALSE) otherwise.    */
   @return  Non-zero (TRUE) if insns were emitted, zero (FALSE) otherwise.    */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static int
static int
or32_emit_int_cmove (rtx  dest,
or32_emit_int_cmove (rtx  dest,
                     rtx  op,
                     rtx  op,
                     rtx  true_cond,
                     rtx  true_cond,
                     rtx  false_cond)
                     rtx  false_cond)
{
{
  rtx condition_rtx, cr;
  rtx condition_rtx, cr;
 
 
  if ((GET_MODE (or32_compare_op0) != SImode) &&
  if ((GET_MODE (or32_compare_op0) != SImode) &&
      (GET_MODE (or32_compare_op0) != HImode) &&
      (GET_MODE (or32_compare_op0) != HImode) &&
      (GET_MODE (or32_compare_op0) != QImode))
      (GET_MODE (or32_compare_op0) != QImode))
    {
    {
      return 0;
      return 0;
    }
    }
 
 
  /* We still have to do the compare, because cmov doesn't do a compare, it
  /* We still have to do the compare, because cmov doesn't do a compare, it
     just looks at the FLAG bit set by a previous compare instruction.  */
     just looks at the FLAG bit set by a previous compare instruction.  */
  condition_rtx = or32_expand_compare (GET_CODE (op),
  condition_rtx = or32_expand_compare (GET_CODE (op),
                                       or32_compare_op0, or32_compare_op1);
                                       or32_compare_op0, or32_compare_op1);
 
 
  cr = XEXP (condition_rtx, 0);
  cr = XEXP (condition_rtx, 0);
 
 
  emit_insn (gen_cmov (dest, condition_rtx, true_cond, false_cond, cr));
  emit_insn (gen_cmov (dest, condition_rtx, true_cond, false_cond, cr));
 
 
  return 1;
  return 1;
 
 
}       /* or32_emit_int_cmove () */
}       /* or32_emit_int_cmove () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Calculate stack size for current function.
/*!Calculate stack size for current function.
 
 
   We need space for:
   We need space for:
   - any callee-saved registers that are live in the function
   - any callee-saved registers that are live in the function
   - any local variables
   - any local variables
   - the return address (if saved)
   - the return address (if saved)
   - the frame pointer (if saved)
   - the frame pointer (if saved)
   - any outgoing arguments.
   - any outgoing arguments.
 
 
   We also return information on whether the return address and frame pointer
   We also return information on whether the return address and frame pointer
   must be saved, the space required to save callee-saved registers and the
   must be saved, the space required to save callee-saved registers and the
   sapce required to save the return address, frame pointer and outgoing
   sapce required to save the return address, frame pointer and outgoing
   arguments.
   arguments.
 
 
   Throughout adjust for OR32 alignment requirements.
   Throughout adjust for OR32 alignment requirements.
 
 
   @param[in]  vars           Bytes required for local variables (if any).
   @param[in]  vars           Bytes required for local variables (if any).
   @param[out] lr_save_area   Space required for return address (if any).
   @param[out] lr_save_area   Space required for return address (if any).
   @param[out] fp_save_area   Space required for frame pointer (if any).
   @param[out] fp_save_area   Space required for frame pointer (if any).
   @param[out] gpr_save_area  Space required for callee-saved registers (if
   @param[out] gpr_save_area  Space required for callee-saved registers (if
                              any).
                              any).
   @param[out] save_area      Space required for outgoing arguments (if any) +
   @param[out] save_area      Space required for outgoing arguments (if any) +
                              return address (if any) and frame pointer (if
                              return address (if any) and frame pointer (if
                              any).
                              any).
 
 
   @return  Total space required (if any).                                    */
   @return  Total space required (if any).                                    */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static int
static int
calculate_stack_size (int  vars,
calculate_stack_size (int  vars,
                      int *lr_save_area,
                      int *lr_save_area,
                      int *fp_save_area,
                      int *fp_save_area,
                      int *gpr_save_area,
                      int *gpr_save_area,
                      int *save_area)
                      int *save_area)
{
{
  int regno;
  int regno;
 
 
  *gpr_save_area = 0;
  *gpr_save_area = 0;
  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
    {
    {
      if (df_regs_ever_live_p(regno) && !call_used_regs[regno])
      if (df_regs_ever_live_p(regno) && !call_used_regs[regno])
        *gpr_save_area += 4;
        *gpr_save_area += 4;
    }
    }
 
 
  *lr_save_area = (!current_function_is_leaf
  *lr_save_area = (!current_function_is_leaf
                   || df_regs_ever_live_p(LINK_REGNUM)) ? 4 : 0;
                   || df_regs_ever_live_p(LINK_REGNUM)) ? 4 : 0;
  *fp_save_area = frame_pointer_needed ? 4 : 0;
  *fp_save_area = frame_pointer_needed ? 4 : 0;
  *save_area    = (OR32_ALIGN (crtl->outgoing_args_size, 4)
  *save_area    = (OR32_ALIGN (crtl->outgoing_args_size, 4)
                   + *lr_save_area + *fp_save_area);
                   + *lr_save_area + *fp_save_area);
 
 
  return *save_area + *gpr_save_area + OR32_ALIGN (vars, 4);
  return *save_area + *gpr_save_area + OR32_ALIGN (vars, 4);
 
 
}       /* calculate_stack_size () */
}       /* calculate_stack_size () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Is this a value suitable for an OR32 address displacement?
/*!Is this a value suitable for an OR32 address displacement?
 
 
   Must be an integer (signed) which fits into 16-bits. If the result is a
   Must be an integer (signed) which fits into 16-bits. If the result is a
   double word, we had better also check that we can also get at the second
   double word, we had better also check that we can also get at the second
   word.
   word.
 
 
   @param[in] mode  Mode of the result for which this displacement will be
   @param[in] mode  Mode of the result for which this displacement will be
                    used.
                    used.
   @param[in] x     RTX for an expression.
   @param[in] x     RTX for an expression.
 
 
   @return  Non-zero (TRUE) if this is a valid 16-bit offset, zero (FALSE)
   @return  Non-zero (TRUE) if this is a valid 16-bit offset, zero (FALSE)
            otherwise.                                                        */
            otherwise.                                                        */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static int
static int
or32_legitimate_displacement_p (enum machine_mode  mode,
or32_legitimate_displacement_p (enum machine_mode  mode,
                                rtx                x)
                                rtx                x)
{
{
  if (CONST_INT == GET_CODE(x))
  if (CONST_INT == GET_CODE(x))
    {
    {
      HOST_WIDE_INT  disp = INTVAL (x);
      HOST_WIDE_INT  disp = INTVAL (x);
 
 
      /* Allow for a second access 4 bytes further on if double. */
      /* Allow for a second access 4 bytes further on if double. */
      if ((DFmode == mode) || (DImode == mode))
      if ((DFmode == mode) || (DImode == mode))
        {
        {
          return  (-32768 < disp) && (disp <= 32763);
          return  (-32768 < disp) && (disp <= 32763);
        }
        }
      else
      else
        {
        {
          return  (-32768 < disp) && (disp <= 32767);
          return  (-32768 < disp) && (disp <= 32767);
        }
        }
    }
    }
  else
  else
    {
    {
      return  0;
      return  0;
    }
    }
}       /* or32_legitimate_displacement_p () */
}       /* or32_legitimate_displacement_p () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Can this register be used as a base register?
/*!Can this register be used as a base register?
 
 
   We need a strict version, for which the register must either be a hard
   We need a strict version, for which the register must either be a hard
   register, or already renumbered to a hard register.
   register, or already renumbered to a hard register.
 
 
   For the non-strict version, any register (other than the flag register will
   For the non-strict version, any register (other than the flag register will
   do).
   do).
 
 
   @todo The code from the old port does not allow r0 as a base when strict,
   @todo The code from the old port does not allow r0 as a base when strict,
         and does when non-strict. Surely it is always a valid register?
         and does when non-strict. Surely it is always a valid register?
 
 
   @param[in] regno   The register to test
   @param[in] regno   The register to test
   @param[in] strict  Non-zero (TRUE) if this is a strict check, zero (FALSE)
   @param[in] strict  Non-zero (TRUE) if this is a strict check, zero (FALSE)
                      otherwise.
                      otherwise.
 
 
   @return  Non-zero (TRUE) if this register can be used as a base register,
   @return  Non-zero (TRUE) if this register can be used as a base register,
            zero (FALSE) otherwise.                                           */
            zero (FALSE) otherwise.                                           */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static bool
static bool
or32_regnum_ok_for_base_p (HOST_WIDE_INT  num,
or32_regnum_ok_for_base_p (HOST_WIDE_INT  num,
                           bool           strict)
                           bool           strict)
{
{
  if (strict)
  if (strict)
    {
    {
      return (num < FIRST_PSEUDO_REGISTER)
      return (num < FIRST_PSEUDO_REGISTER)
        ? (num > 0) && (num <= OR32_LAST_INT_REG)
        ? (num > 0) && (num <= OR32_LAST_INT_REG)
        : (reg_renumber[num] > 0) && (reg_renumber[num] <= OR32_LAST_INT_REG);
        : (reg_renumber[num] > 0) && (reg_renumber[num] <= OR32_LAST_INT_REG);
    }
    }
  else
  else
    {
    {
      return (num <= OR32_LAST_INT_REG) || (num >= FIRST_PSEUDO_REGISTER);
      return (num <= OR32_LAST_INT_REG) || (num >= FIRST_PSEUDO_REGISTER);
    }
    }
}       /* or32_regnum_ok_for_base_p () */
}       /* or32_regnum_ok_for_base_p () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Emit a move from SRC to DEST.
/*!Emit a move from SRC to DEST.
 
 
   Assume that the move expanders can handle all moves if !can_create_pseudo_p
   Assume that the move expanders can handle all moves if !can_create_pseudo_p
   ().  The distinction is important because, unlike emit_move_insn, the move
   ().  The distinction is important because, unlike emit_move_insn, the move
   expanders know how to force Pmode objects into the constant pool even when
   expanders know how to force Pmode objects into the constant pool even when
   the constant pool address is not itself legitimate.
   the constant pool address is not itself legitimate.
 
 
   @param[in] dest  Destination of the move.
   @param[in] dest  Destination of the move.
   @param[in] src   Source for the move.
   @param[in] src   Source for the move.
 
 
   @return  RTX for the move.                                                 */
   @return  RTX for the move.                                                 */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static rtx
static rtx
or32_emit_move (rtx dest, rtx src)
or32_emit_move (rtx dest, rtx src)
{
{
  return (can_create_pseudo_p ()
  return (can_create_pseudo_p ()
          ? emit_move_insn (dest, src)
          ? emit_move_insn (dest, src)
          : emit_move_insn_1 (dest, src));
          : emit_move_insn_1 (dest, src));
 
 
}       /* or32_emit_move () */
}       /* or32_emit_move () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Emit an instruction of the form (set TARGET (CODE OP0 OP1)).
/*!Emit an instruction of the form (set TARGET (CODE OP0 OP1)).
 
 
   @param[in] code    The code for the operation.
   @param[in] code    The code for the operation.
   @param[in] target  Destination for the set operation.
   @param[in] target  Destination for the set operation.
   @param[in] op0     First operand.
   @param[in] op0     First operand.
   @param[in] op1     Second operand.                                         */
   @param[in] op1     Second operand.                                         */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void
static void
or32_emit_binary (enum rtx_code  code,
or32_emit_binary (enum rtx_code  code,
                  rtx            target,
                  rtx            target,
                  rtx            op0,
                  rtx            op0,
                  rtx            op1)
                  rtx            op1)
{
{
  emit_insn (gen_rtx_SET (VOIDmode, target,
  emit_insn (gen_rtx_SET (VOIDmode, target,
                          gen_rtx_fmt_ee (code, GET_MODE (target), op0, op1)));
                          gen_rtx_fmt_ee (code, GET_MODE (target), op0, op1)));
 
 
}       /* or32_emit_binary () */
}       /* or32_emit_binary () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Compute the result of an operation into a new register.
/*!Compute the result of an operation into a new register.
 
 
   Compute ("code" "op0" "op1") and store the result in a new register of mode
   Compute ("code" "op0" "op1") and store the result in a new register of mode
   "mode".
   "mode".
 
 
   @param[in] mode  Mode of the result
   @param[in] mode  Mode of the result
   @parma[in] code  RTX for the operation to perform
   @parma[in] code  RTX for the operation to perform
   @param[in] op0   RTX for the first operand
   @param[in] op0   RTX for the first operand
   @param[in] op1   RTX for the second operand
   @param[in] op1   RTX for the second operand
 
 
   @return  The RTX for the new register.                                     */
   @return  The RTX for the new register.                                     */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static rtx
static rtx
or32_force_binary (enum machine_mode  mode,
or32_force_binary (enum machine_mode  mode,
                   enum rtx_code      code,
                   enum rtx_code      code,
                   rtx                op0,
                   rtx                op0,
                   rtx                op1)
                   rtx                op1)
{
{
  rtx  reg;
  rtx  reg;
 
 
  reg = gen_reg_rtx (mode);
  reg = gen_reg_rtx (mode);
  or32_emit_binary (code, reg, op0, op1);
  or32_emit_binary (code, reg, op0, op1);
 
 
  return reg;
  return reg;
 
 
}       /* or32_force_binary () */
}       /* or32_force_binary () */
 
 
 
 
/* ========================================================================== */
/* ========================================================================== */
/* Global support functions                                                   */
/* Global support functions                                                   */
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* Return the size in bytes of the trampoline code.
/* Return the size in bytes of the trampoline code.
 
 
   Padded to TRAMPOLINE_ALIGNMENT bits. The code sequence is documented in
   Padded to TRAMPOLINE_ALIGNMENT bits. The code sequence is documented in
   or32_trampoline_init ().
   or32_trampoline_init ().
 
 
   This is just the code size. the static chain pointer and target function
   This is just the code size. the static chain pointer and target function
   address immediately follow.
   address immediately follow.
 
 
   @return  The size of the trampoline code in bytes.                         */
   @return  The size of the trampoline code in bytes.                         */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int
int
or32_trampoline_code_size (void)
or32_trampoline_code_size (void)
{
{
  const int  TRAMP_BYTE_ALIGN = TRAMPOLINE_ALIGNMENT / 8;
  const int  TRAMP_BYTE_ALIGN = TRAMPOLINE_ALIGNMENT / 8;
 
 
  /* Five 32-bit code words are needed */
  /* Five 32-bit code words are needed */
  return (5 * 4 + TRAMP_BYTE_ALIGN - 1) / TRAMP_BYTE_ALIGN * TRAMP_BYTE_ALIGN;
  return (5 * 4 + TRAMP_BYTE_ALIGN - 1) / TRAMP_BYTE_ALIGN * TRAMP_BYTE_ALIGN;
 
 
}       /* or32_trampoline_code_size () */
}       /* or32_trampoline_code_size () */
 
 
 
 
/* ========================================================================== */
/* ========================================================================== */
/* Functions to support the Machine Description                               */
/* Functions to support the Machine Description                               */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Expand a prologue pattern.
/*!Expand a prologue pattern.
 
 
   Called after register allocation to add any instructions needed for the
   Called after register allocation to add any instructions needed for the
   prologue.  Using a prologue insn is favored compared to putting all of the
   prologue.  Using a prologue insn is favored compared to putting all of the
   instructions in output_function_prologue(), since it allows the scheduler
   instructions in output_function_prologue(), since it allows the scheduler
   to intermix instructions with the saves of the caller saved registers.  In
   to intermix instructions with the saves of the caller saved registers.  In
   some cases, it might be necessary to emit a barrier instruction as the last
   some cases, it might be necessary to emit a barrier instruction as the last
   insn to prevent such scheduling.
   insn to prevent such scheduling.
 
 
   For the OR32 this is currently controlled by the -mlogue option. It should
   For the OR32 this is currently controlled by the -mlogue option. It should
   be the default, once it is proved to work.                                 */
   be the default, once it is proved to work.                                 */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void
void
or32_expand_prologue (void)
or32_expand_prologue (void)
{
{
  int total_size = or32_compute_frame_size (get_frame_size ());
  int total_size = or32_compute_frame_size (get_frame_size ());
  rtx insn;
  rtx insn;
 
 
  if (!total_size)
  if (!total_size)
    /* No frame needed.  */
    /* No frame needed.  */
    return;
    return;
 
 
  if (frame_info.gpr_frame)
  if (frame_info.gpr_frame)
    emit_frame_insn (gen_add2_insn (stack_pointer_rtx,
    emit_frame_insn (gen_add2_insn (stack_pointer_rtx,
                                    GEN_INT (-frame_info.gpr_frame)));
                                    GEN_INT (-frame_info.gpr_frame)));
  if (frame_info.save_fp_p)
  if (frame_info.save_fp_p)
    {
    {
      emit_frame_insn (gen_rtx_SET (Pmode,
      emit_frame_insn (gen_rtx_SET (Pmode,
                                    stack_disp_mem (frame_info.fp_save_offset),
                                    stack_disp_mem (frame_info.fp_save_offset),
                                    hard_frame_pointer_rtx));
                                    hard_frame_pointer_rtx));
 
 
      emit_frame_insn
      emit_frame_insn
        (gen_add3_insn (hard_frame_pointer_rtx, stack_pointer_rtx, const0_rtx));
        (gen_add3_insn (hard_frame_pointer_rtx, stack_pointer_rtx, const0_rtx));
    }
    }
  if (frame_info.save_lr_p)
  if (frame_info.save_lr_p)
    {
    {
 
 
      emit_frame_insn
      emit_frame_insn
        (gen_rtx_SET (Pmode, stack_disp_mem (frame_info.lr_save_offset),
        (gen_rtx_SET (Pmode, stack_disp_mem (frame_info.lr_save_offset),
                      gen_rtx_REG (Pmode, LINK_REGNUM)));
                      gen_rtx_REG (Pmode, LINK_REGNUM)));
    }
    }
  if (frame_info.gpr_size)
  if (frame_info.gpr_size)
    {
    {
      int offset = 0;
      int offset = 0;
      int regno;
      int regno;
 
 
      for (regno = 0; regno <= OR32_LAST_ACTUAL_REG; regno++)
      for (regno = 0; regno <= OR32_LAST_ACTUAL_REG; regno++)
        {
        {
          if (!(frame_info.mask & ((HOST_WIDE_INT) 1 << regno)))
          if (!(frame_info.mask & ((HOST_WIDE_INT) 1 << regno)))
            continue;
            continue;
 
 
          emit_frame_insn
          emit_frame_insn
            (gen_rtx_SET (Pmode,
            (gen_rtx_SET (Pmode,
                          stack_disp_mem (frame_info.gpr_offset + offset),
                          stack_disp_mem (frame_info.gpr_offset + offset),
                          gen_rtx_REG (Pmode, regno)));
                          gen_rtx_REG (Pmode, regno)));
          offset = offset + UNITS_PER_WORD;
          offset = offset + UNITS_PER_WORD;
        }
        }
    }
    }
 
 
  /* Update the stack pointer to reflect frame size.  */
  /* Update the stack pointer to reflect frame size.  */
  total_size = frame_info.late_frame;
  total_size = frame_info.late_frame;
  insn = gen_add2_insn (stack_pointer_rtx, GEN_INT (-total_size));
  insn = gen_add2_insn (stack_pointer_rtx, GEN_INT (-total_size));
  if (total_size > 32768)
  if (total_size > 32768)
    {
    {
      rtx note = insn;
      rtx note = insn;
      rtx value_rtx = gen_rtx_REG (Pmode, PROLOGUE_TMP);
      rtx value_rtx = gen_rtx_REG (Pmode, PROLOGUE_TMP);
 
 
      or32_emit_set_const32 (value_rtx, GEN_INT (-total_size));
      or32_emit_set_const32 (value_rtx, GEN_INT (-total_size));
      insn = emit_frame_insn (gen_add2_insn (stack_pointer_rtx, value_rtx));
      insn = emit_frame_insn (gen_add2_insn (stack_pointer_rtx, value_rtx));
      add_reg_note (insn, REG_FRAME_RELATED_EXPR, note);
      add_reg_note (insn, REG_FRAME_RELATED_EXPR, note);
    }
    }
  else if (total_size)
  else if (total_size)
    emit_frame_insn (insn);
    emit_frame_insn (insn);
 
 
}       /* or32_expand_prologue () */
}       /* or32_expand_prologue () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Expand an epilogue pattern.
/*!Expand an epilogue pattern.
 
 
   Called after register allocation to add any instructions needed for the
   Called after register allocation to add any instructions needed for the
   epilogue.  Using an epilogue insn is favored compared to putting all of the
   epilogue.  Using an epilogue insn is favored compared to putting all of the
   instructions in output_function_epilogue(), since it allows the scheduler
   instructions in output_function_epilogue(), since it allows the scheduler
   to intermix instructions with the restores of the caller saved registers.
   to intermix instructions with the restores of the caller saved registers.
   In some cases, it might be necessary to emit a barrier instruction as the
   In some cases, it might be necessary to emit a barrier instruction as the
   first insn to prevent such scheduling.
   first insn to prevent such scheduling.
 
 
   For the OR32 this is currently controlled by the -mlogue option. It should
   For the OR32 this is currently controlled by the -mlogue option. It should
   be the default, once it is proved to work.
   be the default, once it is proved to work.
 
 
   @param[in] sibcall  The sibcall epilogue insn if this is a sibcall return,
   @param[in] sibcall  The sibcall epilogue insn if this is a sibcall return,
                       NULL_RTX otherwise.                                             */
                       NULL_RTX otherwise.                                             */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void
void
or32_expand_epilogue (rtx sibcall)
or32_expand_epilogue (rtx sibcall)
{
{
  int total_size = or32_compute_frame_size (get_frame_size ());
  int total_size = or32_compute_frame_size (get_frame_size ());
  int sibcall_regno = FIRST_PSEUDO_REGISTER;
  int sibcall_regno = FIRST_PSEUDO_REGISTER;
 
 
  if (sibcall)
  if (sibcall)
    {
    {
      sibcall = next_nonnote_insn (sibcall);
      sibcall = next_nonnote_insn (sibcall);
      gcc_assert (CALL_P (sibcall) && SIBLING_CALL_P (sibcall));
      gcc_assert (CALL_P (sibcall) && SIBLING_CALL_P (sibcall));
      sibcall = XVECEXP (PATTERN (sibcall), 0, 0);
      sibcall = XVECEXP (PATTERN (sibcall), 0, 0);
      if (GET_CODE (sibcall) == SET)
      if (GET_CODE (sibcall) == SET)
        sibcall = SET_SRC (sibcall);
        sibcall = SET_SRC (sibcall);
      gcc_assert (GET_CODE (sibcall) == CALL);
      gcc_assert (GET_CODE (sibcall) == CALL);
      sibcall = XEXP (sibcall, 0);
      sibcall = XEXP (sibcall, 0);
      gcc_assert (MEM_P (sibcall));
      gcc_assert (MEM_P (sibcall));
      sibcall = XEXP (sibcall, 0);
      sibcall = XEXP (sibcall, 0);
      if (REG_P (sibcall))
      if (REG_P (sibcall))
        sibcall_regno = REGNO (sibcall);
        sibcall_regno = REGNO (sibcall);
      else
      else
        gcc_assert (CONSTANT_P (sibcall));
        gcc_assert (CONSTANT_P (sibcall));
    }
    }
  if (frame_info.save_fp_p)
  if (frame_info.save_fp_p)
    {
    {
      emit_insn (gen_frame_dealloc_fp ());
      emit_insn (gen_frame_dealloc_fp ());
      emit_insn
      emit_insn
        (gen_rtx_SET (Pmode, hard_frame_pointer_rtx,
        (gen_rtx_SET (Pmode, hard_frame_pointer_rtx,
                      stack_disp_mem (frame_info.fp_save_offset)));
                      stack_disp_mem (frame_info.fp_save_offset)));
    }
    }
  else
  else
    {
    {
      rtx value_rtx;
      rtx value_rtx;
 
 
      total_size = frame_info.late_frame;
      total_size = frame_info.late_frame;
      if (total_size > 32767)
      if (total_size > 32767)
        {
        {
          value_rtx = gen_rtx_REG (Pmode, EPILOGUE_TMP);
          value_rtx = gen_rtx_REG (Pmode, EPILOGUE_TMP);
          or32_emit_set_const32 (value_rtx, GEN_INT (total_size));
          or32_emit_set_const32 (value_rtx, GEN_INT (total_size));
        }
        }
      else if (frame_info.late_frame)
      else if (frame_info.late_frame)
        value_rtx = GEN_INT (total_size);
        value_rtx = GEN_INT (total_size);
      if (total_size)
      if (total_size)
        emit_insn (gen_frame_dealloc_sp (value_rtx));
        emit_insn (gen_frame_dealloc_sp (value_rtx));
    }
    }
 
 
  if (frame_info.save_lr_p)
  if (frame_info.save_lr_p)
    {
    {
      emit_insn
      emit_insn
        (gen_rtx_SET (Pmode, gen_rtx_REG (Pmode, LINK_REGNUM),
        (gen_rtx_SET (Pmode, gen_rtx_REG (Pmode, LINK_REGNUM),
                      stack_disp_mem (frame_info.lr_save_offset)));
                      stack_disp_mem (frame_info.lr_save_offset)));
    }
    }
 
 
  if (frame_info.gpr_size)
  if (frame_info.gpr_size)
    {
    {
      int offset = 0;
      int offset = 0;
      int regno;
      int regno;
 
 
      for (regno = 0; regno <= OR32_LAST_ACTUAL_REG; regno++)
      for (regno = 0; regno <= OR32_LAST_ACTUAL_REG; regno++)
        {
        {
          if (!(frame_info.mask & ((HOST_WIDE_INT) 1 << regno)))
          if (!(frame_info.mask & ((HOST_WIDE_INT) 1 << regno)))
            continue;
            continue;
 
 
          if (regno != sibcall_regno)
          if (regno != sibcall_regno)
            emit_insn
            emit_insn
              (gen_rtx_SET (Pmode, gen_rtx_REG (Pmode, regno),
              (gen_rtx_SET (Pmode, gen_rtx_REG (Pmode, regno),
                            stack_disp_mem (frame_info.gpr_offset + offset)));
                            stack_disp_mem (frame_info.gpr_offset + offset)));
          offset = offset + UNITS_PER_WORD;
          offset = offset + UNITS_PER_WORD;
        }
        }
    }
    }
 
 
  if (frame_info.gpr_frame)
  if (frame_info.gpr_frame)
    emit_insn (gen_add2_insn (stack_pointer_rtx,
    emit_insn (gen_add2_insn (stack_pointer_rtx,
                              GEN_INT (frame_info.gpr_frame)));
                              GEN_INT (frame_info.gpr_frame)));
  if (!sibcall)
  if (!sibcall)
    emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, 9)));
    emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, 9)));
 
 
}       /* or32_expand_epilogue () */
}       /* or32_expand_epilogue () */
 
 
/* We are outputting a jump which needs JUMP_ADDRESS, which is the
/* We are outputting a jump which needs JUMP_ADDRESS, which is the
   register it uses as jump destination, restored,
   register it uses as jump destination, restored,
   e.g. a sibcall using a callee-saved register.
   e.g. a sibcall using a callee-saved register.
   Emit the register restore as delay slot insn.  */
   Emit the register restore as delay slot insn.  */
void
void
or32_print_jump_restore (rtx jump_address)
or32_print_jump_restore (rtx jump_address)
{
{
  int regno, jump_regno;
  int regno, jump_regno;
  HOST_WIDE_INT offset = frame_info.gpr_offset;
  HOST_WIDE_INT offset = frame_info.gpr_offset;
 
 
  gcc_assert (REG_P (jump_address));
  gcc_assert (REG_P (jump_address));
  jump_regno = REGNO (jump_address);
  jump_regno = REGNO (jump_address);
  for (regno = 0; regno != jump_regno; regno++)
  for (regno = 0; regno != jump_regno; regno++)
    {
    {
      gcc_assert (regno <= OR32_LAST_ACTUAL_REG);
      gcc_assert (regno <= OR32_LAST_ACTUAL_REG);
      if (!(frame_info.mask & ((HOST_WIDE_INT) 1 << regno)))
      if (!(frame_info.mask & ((HOST_WIDE_INT) 1 << regno)))
        continue;
        continue;
      offset = offset + UNITS_PER_WORD;
      offset = offset + UNITS_PER_WORD;
    }
    }
  asm_fprintf (asm_out_file, "\n\tl.lwz\tr%d,"HOST_WIDE_INT_PRINT_DEC"(r1)\n",
  asm_fprintf (asm_out_file, "\n\tl.lwz\tr%d,"HOST_WIDE_INT_PRINT_DEC"(r1)\n",
               jump_regno, offset);
               jump_regno, offset);
}
}
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Generate assembler code for a movdi/movdf pattern
/*!Generate assembler code for a movdi/movdf pattern
 
 
   @param[in] operands  Operands to the movdx pattern.
   @param[in] operands  Operands to the movdx pattern.
 
 
   @return  The assembler string to output (always "", since we've done the
   @return  The assembler string to output (always "", since we've done the
            output here).                                                     */
            output here).                                                     */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
const char *
const char *
or32_output_move_double (rtx *operands)
or32_output_move_double (rtx *operands)
{
{
  rtx xoperands[3];
  rtx xoperands[3];
 
 
  switch (GET_CODE (operands[0]))
  switch (GET_CODE (operands[0]))
    {
    {
    case REG:
    case REG:
      if (GET_CODE (operands[1]) == REG)
      if (GET_CODE (operands[1]) == REG)
        {
        {
          if (REGNO (operands[0]) == REGNO (operands[1]) + 1)
          if (REGNO (operands[0]) == REGNO (operands[1]) + 1)
            {
            {
              output_asm_insn ("\tl.or    \t%H0, %H1, r0", operands);
              output_asm_insn ("\tl.or    \t%H0, %H1, r0", operands);
              output_asm_insn ("\tl.or    \t%0, %1, r0", operands);
              output_asm_insn ("\tl.or    \t%0, %1, r0", operands);
              return "";
              return "";
            }
            }
          else
          else
            {
            {
              output_asm_insn ("\tl.or    \t%0, %1, r0", operands);
              output_asm_insn ("\tl.or    \t%0, %1, r0", operands);
              output_asm_insn ("\tl.or    \t%H0, %H1, r0", operands);
              output_asm_insn ("\tl.or    \t%H0, %H1, r0", operands);
              return "";
              return "";
            }
            }
        }
        }
      else if (GET_CODE (operands[1]) == MEM)
      else if (GET_CODE (operands[1]) == MEM)
        {
        {
          xoperands[1] = XEXP (operands[1], 0);
          xoperands[1] = XEXP (operands[1], 0);
          if (GET_CODE (xoperands[1]) == REG)
          if (GET_CODE (xoperands[1]) == REG)
            {
            {
              xoperands[0] = operands[0];
              xoperands[0] = operands[0];
              if (REGNO (xoperands[0]) == REGNO (xoperands[1]))
              if (REGNO (xoperands[0]) == REGNO (xoperands[1]))
                {
                {
                  output_asm_insn ("\tl.lwz   \t%H0, 4(%1)", xoperands);
                  output_asm_insn ("\tl.lwz   \t%H0, 4(%1)", xoperands);
                  output_asm_insn ("\tl.lwz   \t%0, 0(%1)", xoperands);
                  output_asm_insn ("\tl.lwz   \t%0, 0(%1)", xoperands);
                  return "";
                  return "";
                }
                }
              else
              else
                {
                {
                  output_asm_insn ("\tl.lwz   \t%0, 0(%1)", xoperands);
                  output_asm_insn ("\tl.lwz   \t%0, 0(%1)", xoperands);
                  output_asm_insn ("\tl.lwz   \t%H0, 4(%1)", xoperands);
                  output_asm_insn ("\tl.lwz   \t%H0, 4(%1)", xoperands);
                  return "";
                  return "";
                }
                }
            }
            }
          else if (GET_CODE (xoperands[1]) == PLUS)
          else if (GET_CODE (xoperands[1]) == PLUS)
            {
            {
              if (GET_CODE (xoperands[2] = XEXP (xoperands[1], 1)) == REG)
              if (GET_CODE (xoperands[2] = XEXP (xoperands[1], 1)) == REG)
                {
                {
                  xoperands[0] = operands[0];
                  xoperands[0] = operands[0];
                  xoperands[1] = XEXP (xoperands[1], 0);
                  xoperands[1] = XEXP (xoperands[1], 0);
                  if (REGNO (xoperands[0]) == REGNO (xoperands[2]))
                  if (REGNO (xoperands[0]) == REGNO (xoperands[2]))
                    {
                    {
                      output_asm_insn ("\tl.lwz   \t%H0, %1+4(%2)",
                      output_asm_insn ("\tl.lwz   \t%H0, %1+4(%2)",
                                       xoperands);
                                       xoperands);
                      output_asm_insn ("\tl.lwz   \t%0, %1(%2)", xoperands);
                      output_asm_insn ("\tl.lwz   \t%0, %1(%2)", xoperands);
                      return "";
                      return "";
                    }
                    }
                  else
                  else
                    {
                    {
                      output_asm_insn ("\tl.lwz   \t%0, %1(%2)", xoperands);
                      output_asm_insn ("\tl.lwz   \t%0, %1(%2)", xoperands);
                      output_asm_insn ("\tl.lwz   \t%H0, %1+4(%2)",
                      output_asm_insn ("\tl.lwz   \t%H0, %1+4(%2)",
                                       xoperands);
                                       xoperands);
                      return "";
                      return "";
                    }
                    }
                }
                }
              else if (GET_CODE (xoperands[2] = XEXP (xoperands[1], 0)) ==
              else if (GET_CODE (xoperands[2] = XEXP (xoperands[1], 0)) ==
                       REG)
                       REG)
                {
                {
                  xoperands[0] = operands[0];
                  xoperands[0] = operands[0];
                  xoperands[1] = XEXP (xoperands[1], 1);
                  xoperands[1] = XEXP (xoperands[1], 1);
                  if (REGNO (xoperands[0]) == REGNO (xoperands[2]))
                  if (REGNO (xoperands[0]) == REGNO (xoperands[2]))
                    {
                    {
                      output_asm_insn ("\tl.lwz   \t%H0, %1+4(%2)",
                      output_asm_insn ("\tl.lwz   \t%H0, %1+4(%2)",
                                       xoperands);
                                       xoperands);
                      output_asm_insn ("\tl.lwz   \t%0, %1(%2)", xoperands);
                      output_asm_insn ("\tl.lwz   \t%0, %1(%2)", xoperands);
                      return "";
                      return "";
                    }
                    }
                  else
                  else
                    {
                    {
                      output_asm_insn ("\tl.lwz   \t%0, %1(%2)", xoperands);
                      output_asm_insn ("\tl.lwz   \t%0, %1(%2)", xoperands);
                      output_asm_insn ("\tl.lwz   \t%H0, %1+4(%2)",
                      output_asm_insn ("\tl.lwz   \t%H0, %1+4(%2)",
                                       xoperands);
                                       xoperands);
                      return "";
                      return "";
                    }
                    }
                }
                }
              else
              else
                abort ();
                abort ();
            }
            }
          else
          else
            abort ();
            abort ();
        }
        }
      else if (GET_CODE (operands[1]) == CONST_INT)
      else if (GET_CODE (operands[1]) == CONST_INT)
        {
        {
          if (INTVAL (operands[1]) < 0)
          if (INTVAL (operands[1]) < 0)
            output_asm_insn ("\tl.addi  \t%0, r0, -1", operands);
            output_asm_insn ("\tl.addi  \t%0, r0, -1", operands);
          else
          else
            output_asm_insn ("\tl.or    \t%0, r0, r0", operands);
            output_asm_insn ("\tl.or    \t%0, r0, r0", operands);
          output_asm_insn ("\tl.movhi \t%H0, hi(%1)", operands);
          output_asm_insn ("\tl.movhi \t%H0, hi(%1)", operands);
          output_asm_insn ("\tl.ori   \t%H0, %H0, lo(%1)", operands);
          output_asm_insn ("\tl.ori   \t%H0, %H0, lo(%1)", operands);
          return "";
          return "";
        }
        }
      else
      else
        abort ();
        abort ();
    case MEM:
    case MEM:
      xoperands[0] = XEXP (operands[0], 0);
      xoperands[0] = XEXP (operands[0], 0);
      if (GET_CODE (xoperands[0]) == REG)
      if (GET_CODE (xoperands[0]) == REG)
        {
        {
          xoperands[1] = operands[1];
          xoperands[1] = operands[1];
          output_asm_insn ("\tl.sw    \t0(%0), %1", xoperands);
          output_asm_insn ("\tl.sw    \t0(%0), %1", xoperands);
          output_asm_insn ("\tl.sw    \t4(%0), %H1", xoperands);
          output_asm_insn ("\tl.sw    \t4(%0), %H1", xoperands);
          return "";
          return "";
        }
        }
      else if (GET_CODE (xoperands[0]) == PLUS)
      else if (GET_CODE (xoperands[0]) == PLUS)
        {
        {
          if (GET_CODE (xoperands[1] = XEXP (xoperands[0], 1)) == REG)
          if (GET_CODE (xoperands[1] = XEXP (xoperands[0], 1)) == REG)
            {
            {
              xoperands[0] = XEXP (xoperands[0], 0);
              xoperands[0] = XEXP (xoperands[0], 0);
              xoperands[2] = operands[1];
              xoperands[2] = operands[1];
              output_asm_insn ("\tl.sw    \t%0(%1), %2", xoperands);
              output_asm_insn ("\tl.sw    \t%0(%1), %2", xoperands);
              output_asm_insn ("\tl.sw    \t%0+4(%1), %H2", xoperands);
              output_asm_insn ("\tl.sw    \t%0+4(%1), %H2", xoperands);
              return "";
              return "";
            }
            }
          else if (GET_CODE (xoperands[1] = XEXP (xoperands[0], 0)) == REG)
          else if (GET_CODE (xoperands[1] = XEXP (xoperands[0], 0)) == REG)
            {
            {
              xoperands[0] = XEXP (xoperands[0], 1);
              xoperands[0] = XEXP (xoperands[0], 1);
              xoperands[2] = operands[1];
              xoperands[2] = operands[1];
              output_asm_insn ("\tl.sw    \t%0(%1), %2", xoperands);
              output_asm_insn ("\tl.sw    \t%0(%1), %2", xoperands);
              output_asm_insn ("\tl.sw    \t%0+4(%1), %H2", xoperands);
              output_asm_insn ("\tl.sw    \t%0+4(%1), %H2", xoperands);
              return "";
              return "";
            }
            }
          else
          else
            abort ();
            abort ();
        }
        }
      else
      else
        {
        {
          fprintf (stderr, "  O/p error %s\n",
          fprintf (stderr, "  O/p error %s\n",
                   GET_RTX_NAME (GET_CODE (xoperands[0])));
                   GET_RTX_NAME (GET_CODE (xoperands[0])));
          return "";
          return "";
          /* abort (); */
          /* abort (); */
        }
        }
    default:
    default:
      abort ();
      abort ();
    }
    }
}       /* or32_output_move_double () */
}       /* or32_output_move_double () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Expand a conditional branch
/*!Expand a conditional branch
 
 
   @param[in] operands  Operands to the branch.
   @param[in] operands  Operands to the branch.
   @param[in] mode      Mode of the comparison.                               */
   @param[in] mode      Mode of the comparison.                               */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void
void
or32_expand_conditional_branch (rtx               *operands,
or32_expand_conditional_branch (rtx               *operands,
                                enum machine_mode  mode)
                                enum machine_mode  mode)
{
{
  rtx tmp;
  rtx tmp;
  enum rtx_code test_code = GET_CODE(operands[0]);
  enum rtx_code test_code = GET_CODE(operands[0]);
 
 
  switch (mode)
  switch (mode)
    {
    {
    case SImode:
    case SImode:
      tmp = or32_expand_compare (test_code, operands[1], operands[2]);
      tmp = or32_expand_compare (test_code, operands[1], operands[2]);
      tmp = gen_rtx_IF_THEN_ELSE (VOIDmode,
      tmp = gen_rtx_IF_THEN_ELSE (VOIDmode,
                                  tmp,
                                  tmp,
                                  gen_rtx_LABEL_REF (VOIDmode, operands[3]),
                                  gen_rtx_LABEL_REF (VOIDmode, operands[3]),
                                  pc_rtx);
                                  pc_rtx);
      emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
      emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
      return;
      return;
 
 
    case SFmode:
    case SFmode:
      tmp = or32_expand_compare (test_code, operands[1], operands[2]);
      tmp = or32_expand_compare (test_code, operands[1], operands[2]);
      tmp = gen_rtx_IF_THEN_ELSE (VOIDmode,
      tmp = gen_rtx_IF_THEN_ELSE (VOIDmode,
                                  tmp,
                                  tmp,
                                  gen_rtx_LABEL_REF (VOIDmode, operands[3]),
                                  gen_rtx_LABEL_REF (VOIDmode, operands[3]),
                                  pc_rtx);
                                  pc_rtx);
      emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
      emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
      return;
      return;
 
 
    default:
    default:
      abort ();
      abort ();
    }
    }
 
 
}       /* or32_expand_conditional_branch () */
}       /* or32_expand_conditional_branch () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Emit a conditional move
/*!Emit a conditional move
 
 
   move "true_cond" to "dest" if "op" of the operands of the last comparison
   move "true_cond" to "dest" if "op" of the operands of the last comparison
   is nonzero/true, "false_cond" if it is zero/false.
   is nonzero/true, "false_cond" if it is zero/false.
 
 
   @param[in] dest        RTX for the destination operand.
   @param[in] dest        RTX for the destination operand.
   @param[in] op          RTX for the comparison operation
   @param[in] op          RTX for the comparison operation
   @param[in] true_cond   RTX to move to dest if condition is TRUE.
   @param[in] true_cond   RTX to move to dest if condition is TRUE.
   @param[in] false_cond  RTX to move to dest if condition is FALSE.
   @param[in] false_cond  RTX to move to dest if condition is FALSE.
 
 
   @return  Non-zero (TRUE) if the hardware supports such an operation, zero
   @return  Non-zero (TRUE) if the hardware supports such an operation, zero
            (FALSE) otherwise.                                                */
            (FALSE) otherwise.                                                */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int
int
or32_emit_cmove (rtx  dest,
or32_emit_cmove (rtx  dest,
                 rtx  op,
                 rtx  op,
                 rtx  true_cond,
                 rtx  true_cond,
                 rtx  false_cond)
                 rtx  false_cond)
{
{
  enum machine_mode result_mode = GET_MODE (dest);
  enum machine_mode result_mode = GET_MODE (dest);
 
 
  if (GET_MODE (true_cond) != result_mode)
  if (GET_MODE (true_cond) != result_mode)
    return 0;
    return 0;
 
 
  if (GET_MODE (false_cond) != result_mode)
  if (GET_MODE (false_cond) != result_mode)
    return 0;
    return 0;
 
 
  /* First, work out if the hardware can do this at all */
  /* First, work out if the hardware can do this at all */
  return or32_emit_int_cmove (dest, op, true_cond, false_cond);
  return or32_emit_int_cmove (dest, op, true_cond, false_cond);
 
 
}       /* or32_emit_cmove () */
}       /* or32_emit_cmove () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Output the assembler for a branch on flag instruction.
/*!Output the assembler for a branch on flag instruction.
 
 
   @param[in] operands  Operands to the branch.
   @param[in] operands  Operands to the branch.
 
 
   @return  The assembler string to use.                                      */
   @return  The assembler string to use.                                      */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
const char *
const char *
or32_output_bf (rtx * operands)
or32_output_bf (rtx * operands)
{
{
  enum rtx_code code;
  enum rtx_code code;
  enum machine_mode mode_calc, mode_got;
  enum machine_mode mode_calc, mode_got;
 
 
  code      = GET_CODE (operands[1]);
  code      = GET_CODE (operands[1]);
  mode_calc = SELECT_CC_MODE (code, or32_compare_op0, or32_compare_op1);
  mode_calc = SELECT_CC_MODE (code, or32_compare_op0, or32_compare_op1);
  mode_got  = GET_MODE (operands[2]);
  mode_got  = GET_MODE (operands[2]);
 
 
  if (mode_calc != mode_got)
  if (mode_calc != mode_got)
    return "l.bnf\t%l0%(";
    return "l.bnf\t%l0%(";
  else
  else
    return "l.bf\t%l0%(";
    return "l.bf\t%l0%(";
}       /* or32_output_bf () */
}       /* or32_output_bf () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Output the assembler for a conditional move instruction.
/*!Output the assembler for a conditional move instruction.
 
 
   @param[in] operands  Operands to the conditional move.
   @param[in] operands  Operands to the conditional move.
 
 
   @return  The assembler string to use.                                      */
   @return  The assembler string to use.                                      */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
const char *
const char *
or32_output_cmov (rtx * operands)
or32_output_cmov (rtx * operands)
{
{
  enum rtx_code code;
  enum rtx_code code;
  enum machine_mode mode_calc, mode_got;
  enum machine_mode mode_calc, mode_got;
 
 
  code      = GET_CODE (operands[1]);
  code      = GET_CODE (operands[1]);
  mode_calc = SELECT_CC_MODE (code, or32_compare_op0, or32_compare_op1);
  mode_calc = SELECT_CC_MODE (code, or32_compare_op0, or32_compare_op1);
  mode_got  = GET_MODE (operands[4]);
  mode_got  = GET_MODE (operands[4]);
 
 
  if (mode_calc != mode_got)
  if (mode_calc != mode_got)
    return "\tl.cmov\t%0,%3,%2";        /* reversed */
    return "\tl.cmov\t%0,%3,%2";        /* reversed */
  else
  else
    return "\tl.cmov\t%0,%2,%3";
    return "\tl.cmov\t%0,%2,%3";
 
 
}       /* or32_output_cmov () */
}       /* or32_output_cmov () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Expand a sibcall pattern.
/*!Expand a sibcall pattern.
 
 
   For now this is very simple way for sibcall support (i.e tail call
   For now this is very simple way for sibcall support (i.e tail call
   optimization).
   optimization).
 
 
   @param[in] result     Not sure. RTX for the result location?
   @param[in] result     Not sure. RTX for the result location?
   @param[in] addr       Not sure. RXT for the address to call?
   @param[in] addr       Not sure. RXT for the address to call?
   @param[in] args_size  Not sure. RTX for the size of the args (in bytes?)?  */
   @param[in] args_size  Not sure. RTX for the size of the args (in bytes?)?  */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void
void
or32_expand_sibcall (rtx  result ATTRIBUTE_UNUSED,
or32_expand_sibcall (rtx  result ATTRIBUTE_UNUSED,
                     rtx  addr,
                     rtx  addr,
                     rtx  args_size)
                     rtx  args_size)
{
{
  emit_call_insn (gen_sibcall_internal (addr, args_size));
  emit_call_insn (gen_sibcall_internal (addr, args_size));
 
 
}       /* or32_expand_sibcall () */
}       /* or32_expand_sibcall () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Load a 32-bit constant.
/*!Load a 32-bit constant.
 
 
   We know it can't be done in one insn when we get here, the movsi expander
   We know it can't be done in one insn when we get here, the movsi expander
   guarantees this.
   guarantees this.
 
 
   @param[in] op0  RTX for the destination.
   @param[in] op0  RTX for the destination.
   @param[in] op1  RTX for the (constant) source.                             */
   @param[in] op1  RTX for the (constant) source.                             */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void
void
or32_emit_set_const32 (rtx  op0,
or32_emit_set_const32 (rtx  op0,
                       rtx  op1)
                       rtx  op1)
{
{
  enum machine_mode mode = GET_MODE (op0);
  enum machine_mode mode = GET_MODE (op0);
  rtx temp;
  rtx temp;
 
 
  /* Sanity check that we really can't do it in one instruction. I.e that we
  /* Sanity check that we really can't do it in one instruction. I.e that we
     don't have a 16-bit constant. */
     don't have a 16-bit constant. */
  if (GET_CODE (op1) == CONST_INT)
  if (GET_CODE (op1) == CONST_INT)
    {
    {
      HOST_WIDE_INT val = INTVAL (op1) & GET_MODE_MASK (mode);
      HOST_WIDE_INT val = INTVAL (op1) & GET_MODE_MASK (mode);
 
 
      if ((-32768 <= val) && (val <= 32767))
      if ((-32768 <= val) && (val <= 32767))
        {
        {
          abort ();
          abort ();
        }
        }
    }
    }
 
 
  /* Full 2-insn decomposition is needed.  */
  /* Full 2-insn decomposition is needed.  */
  if (reload_in_progress || reload_completed)
  if (reload_in_progress || reload_completed)
    temp = op0;
    temp = op0;
  else
  else
    temp = gen_reg_rtx (mode);
    temp = gen_reg_rtx (mode);
 
 
  if (GET_CODE (op1) == CONST_INT)
  if (GET_CODE (op1) == CONST_INT)
    {
    {
      /* Emit them as real moves instead of a HIGH/LO_SUM,
      /* Emit them as real moves instead of a HIGH/LO_SUM,
         this way CSE can see everything and reuse intermediate
         this way CSE can see everything and reuse intermediate
         values if it wants.  */
         values if it wants.  */
      emit_insn (gen_rtx_SET (VOIDmode, temp,
      emit_insn (gen_rtx_SET (VOIDmode, temp,
                              GEN_INT (INTVAL (op1)
                              GEN_INT (INTVAL (op1)
                                       & ~(HOST_WIDE_INT) 0xffff)));
                                       & ~(HOST_WIDE_INT) 0xffff)));
 
 
      emit_insn (gen_rtx_SET (VOIDmode,
      emit_insn (gen_rtx_SET (VOIDmode,
                              op0,
                              op0,
                              gen_rtx_IOR (mode, temp,
                              gen_rtx_IOR (mode, temp,
                                           GEN_INT (INTVAL (op1) & 0xffff))));
                                           GEN_INT (INTVAL (op1) & 0xffff))));
    }
    }
  else
  else
    {
    {
      /* since or32 bfd can not deal with relocs that are not of type
      /* since or32 bfd can not deal with relocs that are not of type
         OR32_CONSTH_RELOC + OR32_CONST_RELOC (ie move high must be
         OR32_CONSTH_RELOC + OR32_CONST_RELOC (ie move high must be
         followed by exactly one lo_sum)
         followed by exactly one lo_sum)
       */
       */
      emit_insn (gen_movsi_insn_big (op0, op1));
      emit_insn (gen_movsi_insn_big (op0, op1));
    }
    }
}       /* or32_emit_set_const32 () */
}       /* or32_emit_set_const32 () */
 
 
 
 
/* ========================================================================== */
/* ========================================================================== */
/* Target hook functions.
/* Target hook functions.
 
 
   These are initialized at the end of this file, to avoid having to
   These are initialized at the end of this file, to avoid having to
   predeclare all the functions. They are only needed here, so are static.    */
   predeclare all the functions. They are only needed here, so are static.    */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Set up the stack and frame pointer (if desired) for the function.
/*!Set up the stack and frame pointer (if desired) for the function.
 
 
   If defined, a function that outputs the assembler code for entry to a
   If defined, a function that outputs the assembler code for entry to a
   function. The prologue is responsible for setting up the stack frame,
   function. The prologue is responsible for setting up the stack frame,
   initializing the frame pointer register, saving registers that must be
   initializing the frame pointer register, saving registers that must be
   saved, and allocating "size" additional bytes of storage for the local
   saved, and allocating "size" additional bytes of storage for the local
   variables. "size" is an integer. "file" is a stdio stream to which the
   variables. "size" is an integer. "file" is a stdio stream to which the
   assembler code should be output.
   assembler code should be output.
 
 
   The label for the beginning of the function need not be output by this
   The label for the beginning of the function need not be output by this
   macro. That has already been done when the macro is run.
   macro. That has already been done when the macro is run.
 
 
   To determine which registers to save, the macro can refer to the array
   To determine which registers to save, the macro can refer to the array
   "regs_ever_live": element "r" is nonzero if hard register "r" is used
   "regs_ever_live": element "r" is nonzero if hard register "r" is used
   anywhere within the function.  This implies the function prologue should
   anywhere within the function.  This implies the function prologue should
   save register r, provided it is not one of the call-used
   save register r, provided it is not one of the call-used
   registers. (TARGET_ASM_FUNCTION_EPILOGUE must likewise use
   registers. (TARGET_ASM_FUNCTION_EPILOGUE must likewise use
   "regs_ever_live".)
   "regs_ever_live".)
 
 
   On machines that have "register windows", the function entry code does not
   On machines that have "register windows", the function entry code does not
   save on the stack the registers that are in the windows, even if they are
   save on the stack the registers that are in the windows, even if they are
   supposed to be preserved by function calls; instead it takes appropriate
   supposed to be preserved by function calls; instead it takes appropriate
   steps to “push” the register stack, if any non-call-used registers are used
   steps to “push” the register stack, if any non-call-used registers are used
   in the function.
   in the function.
 
 
   On machines where functions may or may not have frame-pointers, the
   On machines where functions may or may not have frame-pointers, the
   function entry code must vary accordingly; it must set up the frame pointer
   function entry code must vary accordingly; it must set up the frame pointer
   if one is wanted, and not otherwise. To determine whether a frame pointer
   if one is wanted, and not otherwise. To determine whether a frame pointer
   is in wanted, the macro can refer to the variable frame_pointer_needed. The
   is in wanted, the macro can refer to the variable frame_pointer_needed. The
   variable’s value will be 1 at run time in a function that needs a frame
   variable’s value will be 1 at run time in a function that needs a frame
   pointer. See the section on "Eliminating Frame Pointer and Arg Pointer" in
   pointer. See the section on "Eliminating Frame Pointer and Arg Pointer" in
   the "Target Description Macros and Functions" chapter of the GCC internals
   the "Target Description Macros and Functions" chapter of the GCC internals
   manual.
   manual.
 
 
   The function entry code is responsible for allocating any stack space
   The function entry code is responsible for allocating any stack space
   required for the function. This stack space consists of the regions listed
   required for the function. This stack space consists of the regions listed
   below. In most cases, these regions are allocated in the order listed, with
   below. In most cases, these regions are allocated in the order listed, with
   the last listed region closest to the top of the stack (the lowest address
   the last listed region closest to the top of the stack (the lowest address
   if STACK_GROWS_DOWNWARD is defined, and the highest address if it is not
   if STACK_GROWS_DOWNWARD is defined, and the highest address if it is not
   defined). You can use a different order for a machine if doing so is more
   defined). You can use a different order for a machine if doing so is more
   convenient or required for compatibility reasons. Except in cases where
   convenient or required for compatibility reasons. Except in cases where
   required by standard or by a debugger, there is no reason why the stack
   required by standard or by a debugger, there is no reason why the stack
   layout used by GCC need agree with that used by other compilers for a
   layout used by GCC need agree with that used by other compilers for a
   machine.
   machine.
 
 
   @param[in] file  File handle for any generated code.
   @param[in] file  File handle for any generated code.
   @param[in] size  Number of bytes of storage needed for local variables.    */
   @param[in] size  Number of bytes of storage needed for local variables.    */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void
static void
or32_output_function_prologue (FILE          *file,
or32_output_function_prologue (FILE          *file,
                               HOST_WIDE_INT  size)
                               HOST_WIDE_INT  size)
{
{
  int save_area;
  int save_area;
  int gpr_save_area;
  int gpr_save_area;
  int lr_save_area;
  int lr_save_area;
  int fp_save_area;
  int fp_save_area;
  int stack_size;
  int stack_size;
  int regno;
  int regno;
 
 
  /* If we are doing the prologue using the "prologue" pattern in the machine
  /* If we are doing the prologue using the "prologue" pattern in the machine
     description, do nothing more here.
     description, do nothing more here.
 
 
     JPB 30-Aug-10: Surely that is not correct. If this option is set, we
     JPB 30-Aug-10: Surely that is not correct. If this option is set, we
     should never even be called! */
     should never even be called! */
  if (TARGET_SCHED_LOGUE)
  if (TARGET_SCHED_LOGUE)
    return;
    return;
 
 
  if (size < 0)
  if (size < 0)
    abort ();
    abort ();
 
 
  /* Work out and log the frame size */
  /* Work out and log the frame size */
  stack_size = calculate_stack_size (size, &lr_save_area, &fp_save_area,
  stack_size = calculate_stack_size (size, &lr_save_area, &fp_save_area,
                                     &gpr_save_area, &save_area);
                                     &gpr_save_area, &save_area);
 
 
  fprintf (file,
  fprintf (file,
           "\n\t# gpr_save_area %d size %ld crtl->outgoing_args_size %d\n",
           "\n\t# gpr_save_area %d size %ld crtl->outgoing_args_size %d\n",
           gpr_save_area, size, crtl->outgoing_args_size);
           gpr_save_area, size, crtl->outgoing_args_size);
 
 
  /* Decrement the stack pointer by the total frame size (if we have a
  /* Decrement the stack pointer by the total frame size (if we have a
     frame). */
     frame). */
  if (stack_size > 0)
  if (stack_size > 0)
    {
    {
      /* Special code for large stack frames */
      /* Special code for large stack frames */
      if (stack_size >= 0x8000)
      if (stack_size >= 0x8000)
        {
        {
          fprintf (file, "\tl.movhi\tr%d,hi(%d)\n", GP_ARG_RETURN, stack_size);
          fprintf (file, "\tl.movhi\tr%d,hi(%d)\n", GP_ARG_RETURN, stack_size);
          fprintf (file, "\tl.ori\tr%d,r%d,lo(%d)\n", GP_ARG_RETURN,
          fprintf (file, "\tl.ori\tr%d,r%d,lo(%d)\n", GP_ARG_RETURN,
                   GP_ARG_RETURN, stack_size);
                   GP_ARG_RETURN, stack_size);
          fprintf (file, "\tl.sub\tr%d,r%d,r%d\n", STACK_POINTER_REGNUM,
          fprintf (file, "\tl.sub\tr%d,r%d,r%d\n", STACK_POINTER_REGNUM,
                   STACK_POINTER_REGNUM, GP_ARG_RETURN);
                   STACK_POINTER_REGNUM, GP_ARG_RETURN);
        }
        }
      else
      else
        {
        {
          fprintf (file, "\tl.addi\tr%d,r%d,%d\n", STACK_POINTER_REGNUM,
          fprintf (file, "\tl.addi\tr%d,r%d,%d\n", STACK_POINTER_REGNUM,
                   STACK_POINTER_REGNUM, -stack_size);
                   STACK_POINTER_REGNUM, -stack_size);
        }
        }
 
 
      /* Update the DWARF2 CFA using the new stack pointer. After this the CFA
      /* Update the DWARF2 CFA using the new stack pointer. After this the CFA
         will be the SP + frame size, i.e. the FP (or start of frame if we
         will be the SP + frame size, i.e. the FP (or start of frame if we
         don't actually have a FP). All register refs should relate to this. */
         don't actually have a FP). All register refs should relate to this. */
      if (dwarf2out_do_frame ())
      if (dwarf2out_do_frame ())
        {
        {
          char *l = dwarf2out_cfi_label (false);
          char *l = dwarf2out_cfi_label (false);
 
 
          dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, stack_size);
          dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, stack_size);
        }
        }
    }
    }
 
 
  /* Update the frame pointer if necessary */
  /* Update the frame pointer if necessary */
  if (fp_save_area)
  if (fp_save_area)
    {
    {
      char *l     = dwarf2out_cfi_label (false);
      char *l     = dwarf2out_cfi_label (false);
      int  offset = OR32_ALIGN (crtl->outgoing_args_size, 4) + lr_save_area;
      int  offset = OR32_ALIGN (crtl->outgoing_args_size, 4) + lr_save_area;
 
 
      fprintf (file, "\tl.sw\t%d(r%d),r%d\n", offset,
      fprintf (file, "\tl.sw\t%d(r%d),r%d\n", offset,
               STACK_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM);
               STACK_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM);
 
 
      if (stack_size >= 0x8000)
      if (stack_size >= 0x8000)
        fprintf (file, "\tl.add\tr%d,r%d,r%d\n", HARD_FRAME_POINTER_REGNUM,
        fprintf (file, "\tl.add\tr%d,r%d,r%d\n", HARD_FRAME_POINTER_REGNUM,
                 STACK_POINTER_REGNUM, GP_ARG_RETURN);
                 STACK_POINTER_REGNUM, GP_ARG_RETURN);
      else
      else
        fprintf (file, "\tl.addi\tr%d,r%d,%d\n", HARD_FRAME_POINTER_REGNUM,
        fprintf (file, "\tl.addi\tr%d,r%d,%d\n", HARD_FRAME_POINTER_REGNUM,
                 STACK_POINTER_REGNUM, stack_size);
                 STACK_POINTER_REGNUM, stack_size);
 
 
      /* The CFA is already pointing at the start of our frame (i.e. the new
      /* The CFA is already pointing at the start of our frame (i.e. the new
         FP). The old FP has been saved relative to the SP, so we need to use
         FP). The old FP has been saved relative to the SP, so we need to use
         stack_size to work out where. */
         stack_size to work out where. */
      dwarf2out_reg_save (l, HARD_FRAME_POINTER_REGNUM, offset - stack_size);
      dwarf2out_reg_save (l, HARD_FRAME_POINTER_REGNUM, offset - stack_size);
    }
    }
 
 
  /* Save the return address if necessary */
  /* Save the return address if necessary */
  if (lr_save_area)
  if (lr_save_area)
    {
    {
      char *l     = dwarf2out_cfi_label (false);
      char *l     = dwarf2out_cfi_label (false);
      int  offset = OR32_ALIGN (crtl->outgoing_args_size, 4);
      int  offset = OR32_ALIGN (crtl->outgoing_args_size, 4);
 
 
      fprintf (file, "\tl.sw\t%d(r%d),r%d\n", offset, STACK_POINTER_REGNUM,
      fprintf (file, "\tl.sw\t%d(r%d),r%d\n", offset, STACK_POINTER_REGNUM,
               LINK_REGNUM);
               LINK_REGNUM);
 
 
      /* The CFA is already pointing at the start of our frame (i.e. the new
      /* The CFA is already pointing at the start of our frame (i.e. the new
         FP). The LR has been saved relative to the SP, so we need to use
         FP). The LR has been saved relative to the SP, so we need to use
         stack_size to work out where. */
         stack_size to work out where. */
      dwarf2out_reg_save (l, HARD_FRAME_POINTER_REGNUM, offset - stack_size);
      dwarf2out_reg_save (l, HARD_FRAME_POINTER_REGNUM, offset - stack_size);
    }
    }
 
 
  save_area = (OR32_ALIGN (crtl->outgoing_args_size, 4)
  save_area = (OR32_ALIGN (crtl->outgoing_args_size, 4)
               + lr_save_area + fp_save_area);
               + lr_save_area + fp_save_area);
 
 
  /* Save any callee saved registers */
  /* Save any callee saved registers */
  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
    {
    {
      if (df_regs_ever_live_p(regno) && !call_used_regs[regno])
      if (df_regs_ever_live_p(regno) && !call_used_regs[regno])
        {
        {
          char *l = dwarf2out_cfi_label (false);
          char *l = dwarf2out_cfi_label (false);
 
 
          fprintf (file, "\tl.sw\t%d(r%d),r%d\n", save_area,
          fprintf (file, "\tl.sw\t%d(r%d),r%d\n", save_area,
                   STACK_POINTER_REGNUM, regno);
                   STACK_POINTER_REGNUM, regno);
 
 
          /* The CFA is already pointing at the start of our frame (i.e. the
          /* The CFA is already pointing at the start of our frame (i.e. the
             new FP). The register has been saved relative to the SP, so we
             new FP). The register has been saved relative to the SP, so we
             need to use stack_size to work out where. */
             need to use stack_size to work out where. */
          dwarf2out_reg_save (l, HARD_FRAME_POINTER_REGNUM,
          dwarf2out_reg_save (l, HARD_FRAME_POINTER_REGNUM,
                              save_area - stack_size);
                              save_area - stack_size);
          save_area += 4;
          save_area += 4;
        }
        }
    }
    }
}       /* or32_output_function_prologue () */
}       /* or32_output_function_prologue () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Do any necessary cleanup after a function to restore stack, frame, and regs.
/*!Do any necessary cleanup after a function to restore stack, frame, and regs.
 
 
   This is a function that outputs the assembler code for exit from a
   This is a function that outputs the assembler code for exit from a
   function. The epilogue is responsible for restoring the saved registers and
   function. The epilogue is responsible for restoring the saved registers and
   stack pointer to their values when the function was called, and returning
   stack pointer to their values when the function was called, and returning
   control to the caller. This macro takes the same arguments as the macro
   control to the caller. This macro takes the same arguments as the macro
   TARGET_ASM_FUNCTION_PROLOGUE, and the registers to restore are determined
   TARGET_ASM_FUNCTION_PROLOGUE, and the registers to restore are determined
   from regs_ever_live and CALL_USED_REGISTERS in the same way (@see
   from regs_ever_live and CALL_USED_REGISTERS in the same way (@see
   or32_output_function_prologue ()) .
   or32_output_function_prologue ()) .
 
 
   On some machines, there is a single instruction that does all the work of
   On some machines, there is a single instruction that does all the work of
   returning from the function. On these machines, give that instruction the
   returning from the function. On these machines, give that instruction the
   name "return" (in the machine definition) and do not define the macro
   name "return" (in the machine definition) and do not define the macro
   TARGET_ASM_FUNCTION_EPILOGUE at all.
   TARGET_ASM_FUNCTION_EPILOGUE at all.
 
 
   Do not define a pattern named "return" if you want the
   Do not define a pattern named "return" if you want the
   TARGET_ASM_FUNCTION_EPILOGUE to be used. If you want the target switches to
   TARGET_ASM_FUNCTION_EPILOGUE to be used. If you want the target switches to
   control whether return instructions or epilogues are used, define a
   control whether return instructions or epilogues are used, define a
   "return" pattern with a validity condition that tests the target switches
   "return" pattern with a validity condition that tests the target switches
   appropriately. If the "return" pattern’s validity condition is false,
   appropriately. If the "return" pattern’s validity condition is false,
   epilogues will be used.
   epilogues will be used.
 
 
   On machines where functions may or may not have frame-pointers, the
   On machines where functions may or may not have frame-pointers, the
   function exit code must vary accordingly. Sometimes the code for these two
   function exit code must vary accordingly. Sometimes the code for these two
   cases is completely different. To determine whether a frame pointer is
   cases is completely different. To determine whether a frame pointer is
   wanted, the macro can refer to the variable frame_pointer_needed. The
   wanted, the macro can refer to the variable frame_pointer_needed. The
   variable’s value will be 1 when compiling a function that needs a frame
   variable’s value will be 1 when compiling a function that needs a frame
   pointer.
   pointer.
 
 
   Normally, TARGET_ASM_FUNCTION_PROLOGUE and TARGET_ASM_FUNCTION_EPILOGUE
   Normally, TARGET_ASM_FUNCTION_PROLOGUE and TARGET_ASM_FUNCTION_EPILOGUE
   must treat leaf functions specially. The C variable
   must treat leaf functions specially. The C variable
   "current_function_is_leaf" is nonzero for such a function. See "Handling
   "current_function_is_leaf" is nonzero for such a function. See "Handling
   Leaf Functions" in the "Target Description Macros and Functions" section of
   Leaf Functions" in the "Target Description Macros and Functions" section of
   the GCC internals manual.
   the GCC internals manual.
 
 
   On some machines, some functions pop their arguments on exit while others
   On some machines, some functions pop their arguments on exit while others
   leave that for the caller to do. For example, the 68020 when given "-mrtd"
   leave that for the caller to do. For example, the 68020 when given "-mrtd"
   pops arguments in functions that take a fixed number of arguments.
   pops arguments in functions that take a fixed number of arguments.
 
 
   Your definition of the macro RETURN_POPS_ARGS decides which functions pop
   Your definition of the macro RETURN_POPS_ARGS decides which functions pop
   their own arguments. TARGET_ASM_FUNCTION_EPILOGUE needs to know what was
   their own arguments. TARGET_ASM_FUNCTION_EPILOGUE needs to know what was
   decided.  The number of bytes of the current function’s arguments that this
   decided.  The number of bytes of the current function’s arguments that this
   function should pop is available in "crtl->args.pops_args". See "How Scalar
   function should pop is available in "crtl->args.pops_args". See "How Scalar
   Function Values Are Returned" in the "Target Description Macros and
   Function Values Are Returned" in the "Target Description Macros and
   Functions" section of the GCC internals manual.
   Functions" section of the GCC internals manual.
 
 
   @param[in] file  File handle for any generated code.
   @param[in] file  File handle for any generated code.
   @param[in] size  Number of bytes of storage needed for local variables.    */
   @param[in] size  Number of bytes of storage needed for local variables.    */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void
static void
or32_output_function_epilogue (FILE * file, HOST_WIDE_INT size)
or32_output_function_epilogue (FILE * file, HOST_WIDE_INT size)
{
{
  int save_area;
  int save_area;
  int gpr_save_area;
  int gpr_save_area;
  int lr_save_area;
  int lr_save_area;
  int fp_save_area;
  int fp_save_area;
  int stack_size;
  int stack_size;
  int regno;
  int regno;
 
 
  /* If we are doing the epilogue using the "epilogue" pattern in the machine
  /* If we are doing the epilogue using the "epilogue" pattern in the machine
     description, do nothing more here.
     description, do nothing more here.
 
 
     JPB 30-Aug-10: Surely that is not correct. If this option is set, we
     JPB 30-Aug-10: Surely that is not correct. If this option is set, we
     should never even be called! */
     should never even be called! */
  if (TARGET_SCHED_LOGUE)
  if (TARGET_SCHED_LOGUE)
    return;
    return;
 
 
  /* Work out the frame size */
  /* Work out the frame size */
  stack_size = calculate_stack_size (size, &lr_save_area, &fp_save_area,
  stack_size = calculate_stack_size (size, &lr_save_area, &fp_save_area,
                                     &gpr_save_area, &save_area);
                                     &gpr_save_area, &save_area);
 
 
  /* Restore the return address if necessary */
  /* Restore the return address if necessary */
  if (lr_save_area)
  if (lr_save_area)
    {
    {
      fprintf (file, "\tl.lwz\tr%d,%d(r%d)\n", LINK_REGNUM,
      fprintf (file, "\tl.lwz\tr%d,%d(r%d)\n", LINK_REGNUM,
               OR32_ALIGN (crtl->outgoing_args_size, 4),
               OR32_ALIGN (crtl->outgoing_args_size, 4),
               STACK_POINTER_REGNUM);
               STACK_POINTER_REGNUM);
    }
    }
 
 
  /* Restore the frame pointer if necessary */
  /* Restore the frame pointer if necessary */
  if (fp_save_area)
  if (fp_save_area)
    {
    {
      fprintf (file, "\tl.lwz\tr%d,%d(r%d)\n", HARD_FRAME_POINTER_REGNUM,
      fprintf (file, "\tl.lwz\tr%d,%d(r%d)\n", HARD_FRAME_POINTER_REGNUM,
               OR32_ALIGN (crtl->outgoing_args_size, 4)
               OR32_ALIGN (crtl->outgoing_args_size, 4)
               + lr_save_area, STACK_POINTER_REGNUM);
               + lr_save_area, STACK_POINTER_REGNUM);
    }
    }
 
 
  save_area = (OR32_ALIGN (crtl->outgoing_args_size, 4)
  save_area = (OR32_ALIGN (crtl->outgoing_args_size, 4)
               + lr_save_area + fp_save_area);
               + lr_save_area + fp_save_area);
 
 
  /* Restore any callee-saved registers */
  /* Restore any callee-saved registers */
  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
    {
    {
      if (df_regs_ever_live_p(regno) && !call_used_regs[regno])
      if (df_regs_ever_live_p(regno) && !call_used_regs[regno])
        {
        {
          fprintf (file, "\tl.lwz\tr%d,%d(r%d)\n", regno, save_area,
          fprintf (file, "\tl.lwz\tr%d,%d(r%d)\n", regno, save_area,
                   STACK_POINTER_REGNUM);
                   STACK_POINTER_REGNUM);
          save_area += 4;
          save_area += 4;
        }
        }
    }
    }
 
 
  /* Restore the stack pointer (if necessary) */
  /* Restore the stack pointer (if necessary) */
  if (stack_size >= 0x8000)
  if (stack_size >= 0x8000)
    {
    {
      fprintf (file, "\tl.movhi\tr3,hi(%d)\n", stack_size);
      fprintf (file, "\tl.movhi\tr3,hi(%d)\n", stack_size);
      fprintf (file, "\tl.ori\tr3,r3,lo(%d)\n", stack_size);
      fprintf (file, "\tl.ori\tr3,r3,lo(%d)\n", stack_size);
 
 
      fprintf (file, "\tl.jr\tr%d\n", LINK_REGNUM);
      fprintf (file, "\tl.jr\tr%d\n", LINK_REGNUM);
 
 
      fprintf (file, "\tl.add\tr%d,r%d,r3\n", STACK_POINTER_REGNUM,
      fprintf (file, "\tl.add\tr%d,r%d,r3\n", STACK_POINTER_REGNUM,
               STACK_POINTER_REGNUM);
               STACK_POINTER_REGNUM);
    }
    }
  else if (stack_size > 0)
  else if (stack_size > 0)
    {
    {
      fprintf (file, "\tl.jr\tr%d\n", LINK_REGNUM);
      fprintf (file, "\tl.jr\tr%d\n", LINK_REGNUM);
 
 
      fprintf (file, "\tl.addi\tr%d,r%d,%d\n", STACK_POINTER_REGNUM,
      fprintf (file, "\tl.addi\tr%d,r%d,%d\n", STACK_POINTER_REGNUM,
               STACK_POINTER_REGNUM, stack_size);
               STACK_POINTER_REGNUM, stack_size);
    }
    }
  else
  else
    {
    {
      fprintf (file, "\tl.jr\tr%d\n", LINK_REGNUM);
      fprintf (file, "\tl.jr\tr%d\n", LINK_REGNUM);
 
 
      fprintf (file, "\tl.nop\n");              /* Delay slot */
      fprintf (file, "\tl.nop\n");              /* Delay slot */
    }
    }
}       /* or32_output_function_epilogue () */
}       /* or32_output_function_epilogue () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Define where a function returns values.
/*!Define where a function returns values.
 
 
   Define this to return an RTX representing the place where a function
   Define this to return an RTX representing the place where a function
   returns or receives a value of data type ret type, a tree node representing
   returns or receives a value of data type ret type, a tree node representing
   a data type.  "func" is a tree node representing FUNCTION_DECL or
   a data type.  "func" is a tree node representing FUNCTION_DECL or
   FUNCTION_TYPE of a function being called. If "outgoing" is false, the hook
   FUNCTION_TYPE of a function being called. If "outgoing" is false, the hook
   should compute the register in which the caller will see the return
   should compute the register in which the caller will see the return
   value. Otherwise, the hook should return an RTX representing the place
   value. Otherwise, the hook should return an RTX representing the place
   where a function returns a value.
   where a function returns a value.
 
 
   On many machines, only TYPE_MODE ("ret_type") is relevant. (Actually, on
   On many machines, only TYPE_MODE ("ret_type") is relevant. (Actually, on
   most machines, scalar values are returned in the same place regardless of
   most machines, scalar values are returned in the same place regardless of
   mode.) The value of the expression is usually a reg RTX for the hard
   mode.) The value of the expression is usually a reg RTX for the hard
   register where the return value is stored. The value can also be a parallel
   register where the return value is stored. The value can also be a parallel
   RTX, if the return value is in multiple places. See FUNCTION_ARG for an
   RTX, if the return value is in multiple places. See FUNCTION_ARG for an
   explanation of the parallel form. Note that the callee will populate every
   explanation of the parallel form. Note that the callee will populate every
   location specified in the parallel, but if the first element of the
   location specified in the parallel, but if the first element of the
   parallel contains the whole return value, callers will use that element as
   parallel contains the whole return value, callers will use that element as
   the canonical location and ignore the others. The m68k port uses this type
   the canonical location and ignore the others. The m68k port uses this type
   of parallel to return pointers in both ‘%a0’ (the canonical location) and
   of parallel to return pointers in both ‘%a0’ (the canonical location) and
   ‘%d0’.
   ‘%d0’.
 
 
   If TARGET_PROMOTE_FUNCTION_RETURN returns true, you must apply the same
   If TARGET_PROMOTE_FUNCTION_RETURN returns true, you must apply the same
   promotion rules specified in PROMOTE_MODE if valtype is a scalar type.
   promotion rules specified in PROMOTE_MODE if valtype is a scalar type.
 
 
   If the precise function being called is known, "func" is a tree node
   If the precise function being called is known, "func" is a tree node
   (FUNCTION_DECL) for it; otherwise, "func" is a null pointer. This makes it
   (FUNCTION_DECL) for it; otherwise, "func" is a null pointer. This makes it
   possible to use a different value-returning convention for specific
   possible to use a different value-returning convention for specific
   functions when all their calls are known.
   functions when all their calls are known.
 
 
   Some target machines have "register windows" so that the register in which
   Some target machines have "register windows" so that the register in which
   a function returns its value is not the same as the one in which the caller
   a function returns its value is not the same as the one in which the caller
   sees the value. For such machines, you should return different RTX
   sees the value. For such machines, you should return different RTX
   depending on outgoing.
   depending on outgoing.
 
 
   TARGET_FUNCTION_VALUE is not used for return values with aggregate data
   TARGET_FUNCTION_VALUE is not used for return values with aggregate data
   types, because these are returned in another way. See
   types, because these are returned in another way. See
   TARGET_STRUCT_VALUE_RTX and related macros.
   TARGET_STRUCT_VALUE_RTX and related macros.
 
 
   For the OR32, we can just use the result of LIBCALL_VALUE, since all
   For the OR32, we can just use the result of LIBCALL_VALUE, since all
   functions return their result in the same place (register rv = r11).
   functions return their result in the same place (register rv = r11).
 
 
   JPB 30-Aug-10: What about 64-bit scalar returns (long long int, double),
   JPB 30-Aug-10: What about 64-bit scalar returns (long long int, double),
                  which also use rvh (=r12)?
                  which also use rvh (=r12)?
 
 
   @param[in] ret_type  The return type of the function.
   @param[in] ret_type  The return type of the function.
   @param[in] func      Tree representing function being called.
   @param[in] func      Tree representing function being called.
   @param[in] outgoing  Non-zero (TRUE) if the result represents where the
   @param[in] outgoing  Non-zero (TRUE) if the result represents where the
                        function places the results, zero (FALSE) if the
                        function places the results, zero (FALSE) if the
                        result represents where the caller sees the result.
                        result represents where the caller sees the result.
 
 
   @return  A RTX representing where the result can be found.                 */
   @return  A RTX representing where the result can be found.                 */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static rtx
static rtx
or32_function_value (const_tree  ret_type,
or32_function_value (const_tree  ret_type,
                     const_tree  func ATTRIBUTE_UNUSED,
                     const_tree  func ATTRIBUTE_UNUSED,
                     bool        outgoing ATTRIBUTE_UNUSED)
                     bool        outgoing ATTRIBUTE_UNUSED)
{
{
  return LIBCALL_VALUE (TYPE_MODE(ret_type));
  return LIBCALL_VALUE (TYPE_MODE(ret_type));
 
 
}       /* or32_function_value () */
}       /* or32_function_value () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Check if a function is suitable for tail call optimization.
/*!Check if a function is suitable for tail call optimization.
 
 
   True if it is OK to do sibling call optimization for the specified call
   True if it is OK to do sibling call optimization for the specified call
   expression "exp". "decl" will be the called function, or NULL if this is an
   expression "exp". "decl" will be the called function, or NULL if this is an
   indirect call.
   indirect call.
 
 
   It is not uncommon for limitations of calling conventions to prevent tail
   It is not uncommon for limitations of calling conventions to prevent tail
   calls to functions outside the current unit of translation, or during PIC
   calls to functions outside the current unit of translation, or during PIC
   compilation. The hook is used to enforce these restrictions, as the sibcall
   compilation. The hook is used to enforce these restrictions, as the sibcall
   md pattern can not fail, or fall over to a “normal” call. The criteria for
   md pattern can not fail, or fall over to a “normal” call. The criteria for
   successful sibling call optimization may vary greatly between different
   successful sibling call optimization may vary greatly between different
   architectures.
   architectures.
 
 
   For the OR32, we currently allow sibcall optimization whenever
   For the OR32, we currently allow sibcall optimization whenever
   -foptimize-sibling-calls is enabled.
   -foptimize-sibling-calls is enabled.
 
 
   @param[in] decl  The function for which we may optimize
   @param[in] decl  The function for which we may optimize
   @param[in] exp   The call expression which is candidate for optimization.
   @param[in] exp   The call expression which is candidate for optimization.
 
 
   @return  Non-zero (TRUE) if sibcall optimization is permitted, zero (FALSE)
   @return  Non-zero (TRUE) if sibcall optimization is permitted, zero (FALSE)
            otherwise.                                                        */
            otherwise.                                                        */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static bool
static bool
or32_function_ok_for_sibcall (tree  decl ATTRIBUTE_UNUSED,
or32_function_ok_for_sibcall (tree  decl ATTRIBUTE_UNUSED,
                              tree  exp ATTRIBUTE_UNUSED)
                              tree  exp ATTRIBUTE_UNUSED)
{
{
  /* Assume up to 31 registers of 4 bytes might be saved.  */
  /* Assume up to 31 registers of 4 bytes might be saved.  */
  return or32_redzone >= 31 * 4;
  return or32_redzone >= 31 * 4;
}       /* or32_function_ok_for_sibcall () */
}       /* or32_function_ok_for_sibcall () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Should an argument be passed by reference.
/*!Should an argument be passed by reference.
 
 
   This target hook should return true if an argument at the position
   This target hook should return true if an argument at the position
   indicated by "cum" should be passed by reference. This predicate is queried
   indicated by "cum" should be passed by reference. This predicate is queried
   after target independent reasons for being passed by reference, such as
   after target independent reasons for being passed by reference, such as
   TREE_ADDRESSABLE ("type").
   TREE_ADDRESSABLE ("type").
 
 
   If the hook returns TRUE, a copy of that argument is made in memory and a
   If the hook returns TRUE, a copy of that argument is made in memory and a
   pointer to the argument is passed instead of the argument itself. The
   pointer to the argument is passed instead of the argument itself. The
   pointer is passed in whatever way is appropriate for passing a pointer to
   pointer is passed in whatever way is appropriate for passing a pointer to
   that type.
   that type.
 
 
   For the OR32, all aggregates and arguments greater than 8 bytes are passed
   For the OR32, all aggregates and arguments greater than 8 bytes are passed
   this way.
   this way.
 
 
   @param[in] cum    Position of argument under consideration.
   @param[in] cum    Position of argument under consideration.
   @param[in[ mode   Not sure what this relates to.
   @param[in[ mode   Not sure what this relates to.
   @param[in] type   Type of the argument.
   @param[in] type   Type of the argument.
   @param[in] named  Not sure what this relates to.
   @param[in] named  Not sure what this relates to.
 
 
   @return  Non-zero (TRUE) if the argument should be passed by reference,
   @return  Non-zero (TRUE) if the argument should be passed by reference,
            zero (FALSE) otherwise.                                           */
            zero (FALSE) otherwise.                                           */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static bool
static bool
or32_pass_by_reference (CUMULATIVE_ARGS   *cum ATTRIBUTE_UNUSED,
or32_pass_by_reference (CUMULATIVE_ARGS   *cum ATTRIBUTE_UNUSED,
                        enum machine_mode  mode ATTRIBUTE_UNUSED,
                        enum machine_mode  mode ATTRIBUTE_UNUSED,
                        const_tree         type,
                        const_tree         type,
                        bool               named ATTRIBUTE_UNUSED)
                        bool               named ATTRIBUTE_UNUSED)
{
{
  return (type && (AGGREGATE_TYPE_P (type) || int_size_in_bytes (type) > 8));
  return (type && (AGGREGATE_TYPE_P (type) || int_size_in_bytes (type) > 8));
 
 
}       /* or32_pass_by_reference () */
}       /* or32_pass_by_reference () */
 
 
 
 
#if 0
#if 0
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Is a frame pointer required?
/*!Is a frame pointer required?
 
 
   This target hook should return TRUE if a function must have and use a frame
   This target hook should return TRUE if a function must have and use a frame
   pointer.  This target hook is called in the reload pass. If its return
   pointer.  This target hook is called in the reload pass. If its return
   value is TRUE the function will have a frame pointer.
   value is TRUE the function will have a frame pointer.
 
 
   This target hook can in principle examine the current function and decide
   This target hook can in principle examine the current function and decide
   according to the facts, but on most machines the constant false or the
   according to the facts, but on most machines the constant false or the
   constant true suffices.  Use FALSE when the machine allows code to be
   constant true suffices.  Use FALSE when the machine allows code to be
   generated with no frame pointer, and doing so saves some time or space. Use
   generated with no frame pointer, and doing so saves some time or space. Use
   TRUE when there is no possible advantage to avoiding a frame pointer.
   TRUE when there is no possible advantage to avoiding a frame pointer.
 
 
   In certain cases, the compiler does not know how to produce valid code
   In certain cases, the compiler does not know how to produce valid code
   without a frame pointer. The compiler recognizes those cases and
   without a frame pointer. The compiler recognizes those cases and
   automatically gives the function a frame pointer regardless of what
   automatically gives the function a frame pointer regardless of what
   TARGET_FRAME_POINTER_REQUIRED returns.  You don’t need to worry about them.
   TARGET_FRAME_POINTER_REQUIRED returns.  You don’t need to worry about them.
 
 
   In a function that does not require a frame pointer, the frame pointer
   In a function that does not require a frame pointer, the frame pointer
   register can be allocated for ordinary usage, unless you mark it as a fixed
   register can be allocated for ordinary usage, unless you mark it as a fixed
   register. See FIXED_REGISTERS for more information.
   register. See FIXED_REGISTERS for more information.
 
 
   Default return value is false.
   Default return value is false.
 
 
   For the OR32 we do not need the frame pointer, so the default would have
   For the OR32 we do not need the frame pointer, so the default would have
   sufficed.
   sufficed.
 
 
   JPB 30-Aug-10: The version supplied returned TRUE, which is patently the
   JPB 30-Aug-10: The version supplied returned TRUE, which is patently the
                  wrong answer. This function really could be eliminated and
                  wrong answer. This function really could be eliminated and
                  the default used.
                  the default used.
 
 
   @return  Non-zero (TRUE) if a frame pointer is not required, zero (FALSE)
   @return  Non-zero (TRUE) if a frame pointer is not required, zero (FALSE)
            otherwise.                                                        */
            otherwise.                                                        */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static bool
static bool
or32_frame_pointer_required (void)
or32_frame_pointer_required (void)
{
{
        return 1;
        return 1;
 
 
}       /* or32_frame_pointer_required () */
}       /* or32_frame_pointer_required () */
#endif
#endif
 
 
int
int
or32_initial_elimination_offset(int from, int to)
or32_initial_elimination_offset(int from, int to)
{
{
  or32_compute_frame_size (get_frame_size ());
  or32_compute_frame_size (get_frame_size ());
  return ((from == FRAME_POINTER_REGNUM
  return ((from == FRAME_POINTER_REGNUM
           ? frame_info.gpr_offset : frame_info.gpr_frame)
           ? frame_info.gpr_offset : frame_info.gpr_frame)
          + (to == STACK_POINTER_REGNUM ? frame_info.late_frame : 0));
          + (to == STACK_POINTER_REGNUM ? frame_info.late_frame : 0));
}
}
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!How many bytes at the beginning of an argument must be put into registers.
/*!How many bytes at the beginning of an argument must be put into registers.
 
 
   This target hook returns the number of bytes at the beginning of an
   This target hook returns the number of bytes at the beginning of an
   argument that must be put in registers. The value must be zero for
   argument that must be put in registers. The value must be zero for
   arguments that are passed entirely in registers or that are entirely pushed
   arguments that are passed entirely in registers or that are entirely pushed
   on the stack.
   on the stack.
 
 
   On some machines, certain arguments must be passed partially in registers
   On some machines, certain arguments must be passed partially in registers
   and partially in memory. On these machines, typically the first few words
   and partially in memory. On these machines, typically the first few words
   of arguments a re passed in registers, and the rest on the stack. If a
   of arguments a re passed in registers, and the rest on the stack. If a
   multi-word argument (a double or a structure) crosses that boundary, its
   multi-word argument (a double or a structure) crosses that boundary, its
   first few words must be passed in registers and the rest must be
   first few words must be passed in registers and the rest must be
   pushed. This macro tells the compiler when this occurs, and how many bytes
   pushed. This macro tells the compiler when this occurs, and how many bytes
   should go in registers.
   should go in registers.
 
 
   FUNCTION_ARG for these arguments should return the first register to be
   FUNCTION_ARG for these arguments should return the first register to be
   used by the caller for this argument; likewise FUNCTION_INCOMING_ARG, for
   used by the caller for this argument; likewise FUNCTION_INCOMING_ARG, for
   the called function.
   the called function.
 
 
   On the OR32 we never split argumetns between registers and memory.
   On the OR32 we never split argumetns between registers and memory.
 
 
   JPB 30-Aug-10: Is this correct? Surely we should allow this. The ABI spec
   JPB 30-Aug-10: Is this correct? Surely we should allow this. The ABI spec
                  is incomplete on this point.
                  is incomplete on this point.
 
 
   @param[in] cum    Position of argument under consideration.
   @param[in] cum    Position of argument under consideration.
   @param[in[ mode   Not sure what this relates to.
   @param[in[ mode   Not sure what this relates to.
   @param[in] type   Type of the argument.
   @param[in] type   Type of the argument.
   @param[in] named  Not sure what this relates to.
   @param[in] named  Not sure what this relates to.
 
 
   @return  The number of bytes of the argument to go into registers          */
   @return  The number of bytes of the argument to go into registers          */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static int
static int
or32_arg_partial_bytes (CUMULATIVE_ARGS   *cum ATTRIBUTE_UNUSED,
or32_arg_partial_bytes (CUMULATIVE_ARGS   *cum ATTRIBUTE_UNUSED,
                        enum machine_mode  mode ATTRIBUTE_UNUSED,
                        enum machine_mode  mode ATTRIBUTE_UNUSED,
                        tree               type ATTRIBUTE_UNUSED,
                        tree               type ATTRIBUTE_UNUSED,
                        bool               named ATTRIBUTE_UNUSED)
                        bool               named ATTRIBUTE_UNUSED)
{
{
  return 0;
  return 0;
 
 
}       /* or32_arg_partial_bytes () */
}       /* or32_arg_partial_bytes () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Promote the mode of a function's arguments/return value.
/*!Promote the mode of a function's arguments/return value.
 
 
   Like PROMOTE_MODE, but it is applied to outgoing function arguments or
   Like PROMOTE_MODE, but it is applied to outgoing function arguments or
   function return values. The target hook should return the new mode and
   function return values. The target hook should return the new mode and
   possibly change "*punsignedp" if the promotion should change
   possibly change "*punsignedp" if the promotion should change
   signedness. This function is called only for scalar or pointer types.
   signedness. This function is called only for scalar or pointer types.
 
 
   "for_return" allows to distinguish the promotion of arguments and return
   "for_return" allows to distinguish the promotion of arguments and return
   values. If it is 1, a return value is being promoted and
   values. If it is 1, a return value is being promoted and
   TARGET_FUNCTION_VALUE must perform the same promotions done here. If it is
   TARGET_FUNCTION_VALUE must perform the same promotions done here. If it is
   2, the returned mode should be that of the register in which an incoming
   2, the returned mode should be that of the register in which an incoming
   parameter is copied, or the outgoing result is computed; then the hook
   parameter is copied, or the outgoing result is computed; then the hook
   should return the same mode as PROMOTE_MODE, though the signedness may be
   should return the same mode as PROMOTE_MODE, though the signedness may be
   different.
   different.
 
 
   The default is to not promote arguments and return values. You can also
   The default is to not promote arguments and return values. You can also
   define the hook to "default_promote_function_mode_always_promote" if you
   define the hook to "default_promote_function_mode_always_promote" if you
   would like to apply the same rules given by PROMOTE_MODE.
   would like to apply the same rules given by PROMOTE_MODE.
 
 
   For the OR32, if the size of the mode is integral and less than 4, we
   For the OR32, if the size of the mode is integral and less than 4, we
   promote to SImode, otherwise we return the mode we are supplied.
   promote to SImode, otherwise we return the mode we are supplied.
 
 
   @param[in]  type        Not sure. Type of the argument?
   @param[in]  type        Not sure. Type of the argument?
   @param[in]  mode        The mode of argument/return value to consider.
   @param[in]  mode        The mode of argument/return value to consider.
   @param[out] punsignedp  Signedness of the value.
   @param[out] punsignedp  Signedness of the value.
   @param[in]  fntype      Not sure. Type of the function?
   @param[in]  fntype      Not sure. Type of the function?
   @param[in]  for_return  1 if a return value, 2 if an incoming value.
   @param[in]  for_return  1 if a return value, 2 if an incoming value.
 
 
   @return  The new mode.                                                     */
   @return  The new mode.                                                     */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static enum machine_mode
static enum machine_mode
or32_promote_function_mode (const_tree         type ATTRIBUTE_UNUSED,
or32_promote_function_mode (const_tree         type ATTRIBUTE_UNUSED,
                            enum machine_mode  mode,
                            enum machine_mode  mode,
                            int               *punsignedp ATTRIBUTE_UNUSED,
                            int               *punsignedp ATTRIBUTE_UNUSED,
                            const_tree         fntype ATTRIBUTE_UNUSED,
                            const_tree         fntype ATTRIBUTE_UNUSED,
                            int                for_return ATTRIBUTE_UNUSED)
                            int                for_return ATTRIBUTE_UNUSED)
{
{
  return (   (GET_MODE_CLASS (mode) == MODE_INT)
  return (   (GET_MODE_CLASS (mode) == MODE_INT)
          && (GET_MODE_SIZE (mode) < 4)) ? SImode : mode;
          && (GET_MODE_SIZE (mode) < 4)) ? SImode : mode;
 
 
}       /* or32_promote_function_mode () */
}       /* or32_promote_function_mode () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Is this a legitimate address?
/*!Is this a legitimate address?
 
 
  A function that returns whether x (an RTX) is a legitimate memory address on
  A function that returns whether x (an RTX) is a legitimate memory address on
  the target machine for a memory operand of mode mode.
  the target machine for a memory operand of mode mode.
 
 
  Legitimate addresses are defined in two variants: a strict variant and a
  Legitimate addresses are defined in two variants: a strict variant and a
  non-strict one.  The strict parameter chooses which variant is desired by
  non-strict one.  The strict parameter chooses which variant is desired by
  the caller.
  the caller.
 
 
  The strict variant is used in the reload pass. It must be defined so that
  The strict variant is used in the reload pass. It must be defined so that
  any pseudo- register that has not been allocated a hard register is
  any pseudo- register that has not been allocated a hard register is
  considered a memory reference.  This is because in contexts where some kind
  considered a memory reference.  This is because in contexts where some kind
  of register is required, a pseudo-register with no hard register must be
  of register is required, a pseudo-register with no hard register must be
  rejected. For non-hard registers, the strict variant should look up the
  rejected. For non-hard registers, the strict variant should look up the
  reg_renumber array; it should then proceed using the hard register number in
  reg_renumber array; it should then proceed using the hard register number in
  the array, or treat the pseudo as a memory reference if the array holds -1.
  the array, or treat the pseudo as a memory reference if the array holds -1.
 
 
  The non-strict variant is used in other passes. It must be defined to accept
  The non-strict variant is used in other passes. It must be defined to accept
  all pseudo-registers in every context where some kind of register is
  all pseudo-registers in every context where some kind of register is
  required.
  required.
 
 
  Normally, constant addresses which are the sum of a symbol_ref and an
  Normally, constant addresses which are the sum of a symbol_ref and an
  integer are stored inside a const RTX to mark them as constant. Therefore,
  integer are stored inside a const RTX to mark them as constant. Therefore,
  there is no need to recognize such sums specifically as legitimate
  there is no need to recognize such sums specifically as legitimate
  addresses. Normally you would simply recognize any const as legitimate.
  addresses. Normally you would simply recognize any const as legitimate.
 
 
  Usually PRINT_OPERAND_ADDRESS is not prepared to handle constant sums that
  Usually PRINT_OPERAND_ADDRESS is not prepared to handle constant sums that
  are not marked with const. It assumes that a naked plus indicates
  are not marked with const. It assumes that a naked plus indicates
  indexing. If so, then you must reject such naked constant sums as
  indexing. If so, then you must reject such naked constant sums as
  illegitimate addresses, so that none of them will be given to
  illegitimate addresses, so that none of them will be given to
  PRINT_OPERAND_ADDRESS.
  PRINT_OPERAND_ADDRESS.
 
 
  On some machines, whether a symbolic address is legitimate depends on the
  On some machines, whether a symbolic address is legitimate depends on the
  section that the address refers to. On these machines, define the target
  section that the address refers to. On these machines, define the target
  hook TARGET_ENCODE_ SECTION_INFO to store the information into the
  hook TARGET_ENCODE_ SECTION_INFO to store the information into the
  symbol_ref, and then check for it here. When you see a const, you will have
  symbol_ref, and then check for it here. When you see a const, you will have
  to look inside it to find the symbol_ref in order to determine the
  to look inside it to find the symbol_ref in order to determine the
  section. See the internals manual section on "Assembler Format" for more
  section. See the internals manual section on "Assembler Format" for more
  info.
  info.
 
 
  Some ports are still using a deprecated legacy substitute for this hook, the
  Some ports are still using a deprecated legacy substitute for this hook, the
  GO_IF_LEGITIMATE_ADDRESS macro. This macro has this syntax:
  GO_IF_LEGITIMATE_ADDRESS macro. This macro has this syntax:
 
 
    #define GO_IF_LEGITIMATE_ADDRESS (mode, x, label )
    #define GO_IF_LEGITIMATE_ADDRESS (mode, x, label )
 
 
  and should goto label if the address x is a valid address on the target
  and should goto label if the address x is a valid address on the target
  machine for a memory operand of mode mode. Whether the strict or non-strict
  machine for a memory operand of mode mode. Whether the strict or non-strict
  variants are desired is defined by the REG_OK_STRICT macro introduced
  variants are desired is defined by the REG_OK_STRICT macro introduced
  earlier in this section. Using the hook is usually simpler because it limits
  earlier in this section. Using the hook is usually simpler because it limits
  the number of files that are recompiled when changes are made.
  the number of files that are recompiled when changes are made.
 
 
   The OR32 only has a single addressing mode, which is a base register with
   The OR32 only has a single addressing mode, which is a base register with
   16-bit displacement. We can accept just 16-bit constants as addresses (they
   16-bit displacement. We can accept just 16-bit constants as addresses (they
   can use r0 as base address, and we can accept plain registers as addresses
   can use r0 as base address, and we can accept plain registers as addresses
   (they can use a displacement of zero).
   (they can use a displacement of zero).
 
 
   @param[in] mode    The mode of the address
   @param[in] mode    The mode of the address
   @param[in] x       The address (RTX)
   @param[in] x       The address (RTX)
   @param[in] strict  Non-zero (TRUE) if we are in "strict" mode, zero (FALSE)
   @param[in] strict  Non-zero (TRUE) if we are in "strict" mode, zero (FALSE)
                      otherwise.
                      otherwise.
 
 
   @return  Non-zero (TRUE) if this is a legitimate address, zero (FALSE)
   @return  Non-zero (TRUE) if this is a legitimate address, zero (FALSE)
            otherwise.                                                        */
            otherwise.                                                        */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static bool
static bool
or32_legitimate_address_p (enum machine_mode  mode ATTRIBUTE_UNUSED,
or32_legitimate_address_p (enum machine_mode  mode ATTRIBUTE_UNUSED,
                           rtx                x,
                           rtx                x,
                           bool               strict)
                           bool               strict)
{
{
  /* You might think 16-bit constants are suitable. They can be built into
  /* You might think 16-bit constants are suitable. They can be built into
     addresses using r0 as the base. However this seems to lead to defective
     addresses using r0 as the base. However this seems to lead to defective
     code. So for now this is a placeholder, and this code is not used.
     code. So for now this is a placeholder, and this code is not used.
 
 
     if (or32_legitimate_displacement_p (mode, x))
     if (or32_legitimate_displacement_p (mode, x))
       {
       {
         return  1;
         return  1;
       }
       }
  */
  */
 
 
  /* Addresses consisting of a register and 16-bit displacement are also
  /* Addresses consisting of a register and 16-bit displacement are also
     suitable. We need the mode, since for double words, we had better be
     suitable. We need the mode, since for double words, we had better be
     able to address the full 8 bytes. */
     able to address the full 8 bytes. */
  if (GET_CODE(x) == PLUS)
  if (GET_CODE(x) == PLUS)
    {
    {
      rtx reg = XEXP(x,0);
      rtx reg = XEXP(x,0);
 
 
      /* If valid register... */
      /* If valid register... */
      if ((GET_CODE(reg) == REG)
      if ((GET_CODE(reg) == REG)
          && or32_regnum_ok_for_base_p (REGNO (reg), strict))
          && or32_regnum_ok_for_base_p (REGNO (reg), strict))
        {
        {
          rtx offset = XEXP(x,1);
          rtx offset = XEXP(x,1);
 
 
          /* ...and valid offset */
          /* ...and valid offset */
          if (or32_legitimate_displacement_p (mode, offset))
          if (or32_legitimate_displacement_p (mode, offset))
            {
            {
              return 1;
              return 1;
            }
            }
        }
        }
    }
    }
 
 
  /* Addresses consisting of just a register are OK. They can be built into
  /* Addresses consisting of just a register are OK. They can be built into
     addresses using an offset of zero (and an offset of four if double
     addresses using an offset of zero (and an offset of four if double
     word). */
     word). */
  if (GET_CODE(x) == REG
  if (GET_CODE(x) == REG
    && or32_regnum_ok_for_base_p(REGNO(x),strict)) {
    && or32_regnum_ok_for_base_p(REGNO(x),strict)) {
      return 1;
      return 1;
  }
  }
 
 
  return 0;
  return 0;
}
}
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Initialize a trampoline for nested functions.
/*!Initialize a trampoline for nested functions.
 
 
   A nested function is defined by *two* pieces of information, the address of
   A nested function is defined by *two* pieces of information, the address of
   the function (like any other function) and a pointer to the frame of the
   the function (like any other function) and a pointer to the frame of the
   enclosing function. The latter is required to allow the nested function to
   enclosing function. The latter is required to allow the nested function to
   access local variables in the enclosing function's frame.
   access local variables in the enclosing function's frame.
 
 
   This represents a problem, since a function in C is represented as an
   This represents a problem, since a function in C is represented as an
   address that can be held in a single variable as a pointer. Requiring two
   address that can be held in a single variable as a pointer. Requiring two
   pointers will not fit.
   pointers will not fit.
 
 
   The solution is documented in "Lexical Closures for C++" by Thomas
   The solution is documented in "Lexical Closures for C++" by Thomas
   M. Breuel (USENIX C++ Conference Proceedings, October 17-21, 1988). The
   M. Breuel (USENIX C++ Conference Proceedings, October 17-21, 1988). The
   nested function is represented by a small block of code and data on the
   nested function is represented by a small block of code and data on the
   enclosing function's stack frame, which sets up a pointer to the enclosing
   enclosing function's stack frame, which sets up a pointer to the enclosing
   function's stack frame (the static chain pointer) in a register defined by
   function's stack frame (the static chain pointer) in a register defined by
   the ABI, and then jumps to the code of the function proper.
   the ABI, and then jumps to the code of the function proper.
 
 
   The function can be represented as a single pointer to this block of code,
   The function can be represented as a single pointer to this block of code,
   known as a trampoline, which when called generates both pointers
   known as a trampoline, which when called generates both pointers
   needed. The nested function (which knows it is a nested function at compile
   needed. The nested function (which knows it is a nested function at compile
   time) can then generate code to access the enclosing frame via the static
   time) can then generate code to access the enclosing frame via the static
   chain register.
   chain register.
 
 
   There is a catch that the trampoline is set up as data, but executed as
   There is a catch that the trampoline is set up as data, but executed as
   instructions. The former will be via the data cache, the latter via the
   instructions. The former will be via the data cache, the latter via the
   instruction cache. There is a risk that a later trampoline will not be seen
   instruction cache. There is a risk that a later trampoline will not be seen
   by the instruction cache, so the wrong code will be executed. So the
   by the instruction cache, so the wrong code will be executed. So the
   instruction cache should be flushed for the trampoline address range.
   instruction cache should be flushed for the trampoline address range.
 
 
   This hook is called to initialize a trampoline. "m_tramp" is an RTX for the
   This hook is called to initialize a trampoline. "m_tramp" is an RTX for the
   memory block for the trampoline; "fndecl" is the FUNCTION_DECL for the
   memory block for the trampoline; "fndecl" is the FUNCTION_DECL for the
   nested function; "static_chain" is an RTX for the static chain value that
   nested function; "static_chain" is an RTX for the static chain value that
   should be passed to the function when it is called.
   should be passed to the function when it is called.
 
 
   If the target defines TARGET_ASM_TRAMPOLINE_TEMPLATE, then the first thing
   If the target defines TARGET_ASM_TRAMPOLINE_TEMPLATE, then the first thing
   this hook should do is emit a block move into "m_tramp" from the memory
   this hook should do is emit a block move into "m_tramp" from the memory
   block returned by assemble_trampoline_template. Note that the block move
   block returned by assemble_trampoline_template. Note that the block move
   need only cover the constant parts of the trampoline. If the target
   need only cover the constant parts of the trampoline. If the target
   isolates the variable parts of the trampoline to the end, not all
   isolates the variable parts of the trampoline to the end, not all
   TRAMPOLINE_SIZE bytes need be copied.
   TRAMPOLINE_SIZE bytes need be copied.
 
 
   If the target requires any other actions, such as flushing caches or
   If the target requires any other actions, such as flushing caches or
   enabling stack execution, these actions should be performed after
   enabling stack execution, these actions should be performed after
   initializing the trampoline proper.
   initializing the trampoline proper.
 
 
   For the OR32, no static chain register is used. We choose to use the return
   For the OR32, no static chain register is used. We choose to use the return
   value (rv) register. The code is based on that for MIPS.
   value (rv) register. The code is based on that for MIPS.
   The trampoline code is:
   The trampoline code is:
 
 
              l.movhi r11,hi(end_addr)
              l.movhi r11,hi(end_addr)
              l.ori   r11,lo(end_addr)
              l.ori   r11,lo(end_addr)
              l.lwz   r13,4(r11)
              l.lwz   r13,4(r11)
              l.jr    r13
              l.jr    r13
              l.lwz   r11,0(r11)
              l.lwz   r11,0(r11)
      end_addr:
      end_addr:
              .word   <static chain>
              .word   <static chain>
              .word   <nested_function>
              .word   <nested_function>
 
 
   @note For the OR32 we need to flush the instruction cache, which is a
   @note For the OR32 we need to flush the instruction cache, which is a
         privileged operation. Needs fixing.
         privileged operation. Needs fixing.
 
 
   @param[in] m_tramp      The lowest address of the trampoline on the stack.
   @param[in] m_tramp      The lowest address of the trampoline on the stack.
   @param[in] fndecl       Declaration of the enclosing function.
   @param[in] fndecl       Declaration of the enclosing function.
   @param[in] chain_value  Static chain pointer to pass to the nested
   @param[in] chain_value  Static chain pointer to pass to the nested
                           function.                                          */
                           function.                                          */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void
static void
or32_trampoline_init (rtx   m_tramp,
or32_trampoline_init (rtx   m_tramp,
                      tree  fndecl,
                      tree  fndecl,
                      rtx   chain_value)
                      rtx   chain_value)
{
{
  rtx  addr;                            /* Start address of the trampoline */
  rtx  addr;                            /* Start address of the trampoline */
  rtx  end_addr;                        /* End address of the code block */
  rtx  end_addr;                        /* End address of the code block */
 
 
  rtx  high;                            /* RTX for the high part of end_addr */
  rtx  high;                            /* RTX for the high part of end_addr */
  rtx  low;                             /* RTX for the low part of end_addr */
  rtx  low;                             /* RTX for the low part of end_addr */
  rtx  opcode;                          /* RTX for generated opcodes */
  rtx  opcode;                          /* RTX for generated opcodes */
  rtx  mem;                             /* RTX for trampoline memory */
  rtx  mem;                             /* RTX for trampoline memory */
 
 
  rtx trampoline[5];                    /* The trampoline code */
  rtx trampoline[5];                    /* The trampoline code */
 
 
  unsigned int  i;                      /* Index into trampoline */
  unsigned int  i;                      /* Index into trampoline */
  unsigned int  j;                      /* General counter */
  unsigned int  j;                      /* General counter */
 
 
  HOST_WIDE_INT  end_addr_offset;         /* Offset to end of code */
  HOST_WIDE_INT  end_addr_offset;         /* Offset to end of code */
  HOST_WIDE_INT  static_chain_offset;     /* Offset to stack chain word */
  HOST_WIDE_INT  static_chain_offset;     /* Offset to stack chain word */
  HOST_WIDE_INT  target_function_offset;  /* Offset to func address word */
  HOST_WIDE_INT  target_function_offset;  /* Offset to func address word */
 
 
  /* Work out the offsets of the pointers from the start of the trampoline
  /* Work out the offsets of the pointers from the start of the trampoline
     code.  */
     code.  */
  end_addr_offset        = or32_trampoline_code_size ();
  end_addr_offset        = or32_trampoline_code_size ();
  static_chain_offset    = end_addr_offset;
  static_chain_offset    = end_addr_offset;
  target_function_offset = static_chain_offset + GET_MODE_SIZE (ptr_mode);
  target_function_offset = static_chain_offset + GET_MODE_SIZE (ptr_mode);
 
 
  /* Get pointers in registers to the beginning and end of the code block.  */
  /* Get pointers in registers to the beginning and end of the code block.  */
  addr     = force_reg (Pmode, XEXP (m_tramp, 0));
  addr     = force_reg (Pmode, XEXP (m_tramp, 0));
  end_addr = or32_force_binary (Pmode, PLUS, addr, GEN_INT (end_addr_offset));
  end_addr = or32_force_binary (Pmode, PLUS, addr, GEN_INT (end_addr_offset));
 
 
  /* Build up the code in TRAMPOLINE.
  /* Build up the code in TRAMPOLINE.
 
 
              l.movhi r11,hi(end_addr)
              l.movhi r11,hi(end_addr)
              l.ori   r11,lo(end_addr)
              l.ori   r11,lo(end_addr)
              l.lwz   r13,4(r11)
              l.lwz   r13,4(r11)
              l.jr    r13
              l.jr    r13
              l.lwz   r11,0(r11)
              l.lwz   r11,0(r11)
       end_addr:
       end_addr:
  */
  */
 
 
  i = 0;
  i = 0;
 
 
  /* Break out the high and low parts of the end_addr */
  /* Break out the high and low parts of the end_addr */
  high = expand_simple_binop (SImode, LSHIFTRT, end_addr, GEN_INT (16),
  high = expand_simple_binop (SImode, LSHIFTRT, end_addr, GEN_INT (16),
                              NULL, false, OPTAB_WIDEN);
                              NULL, false, OPTAB_WIDEN);
  low  = convert_to_mode (SImode, gen_lowpart (HImode, end_addr), true);
  low  = convert_to_mode (SImode, gen_lowpart (HImode, end_addr), true);
 
 
  /* Emit the l.movhi, adding an operation to OR in the high bits from the
  /* Emit the l.movhi, adding an operation to OR in the high bits from the
     RTX. */
     RTX. */
  opcode = gen_int_mode (OR32_MOVHI (11, 0), SImode);
  opcode = gen_int_mode (OR32_MOVHI (11, 0), SImode);
  trampoline[i++] = expand_simple_binop (SImode, IOR, opcode, high, NULL,
  trampoline[i++] = expand_simple_binop (SImode, IOR, opcode, high, NULL,
                                         false, OPTAB_WIDEN);
                                         false, OPTAB_WIDEN);
 
 
  /* Emit the l.ori, adding an operations to OR in the low bits from the
  /* Emit the l.ori, adding an operations to OR in the low bits from the
     RTX. */
     RTX. */
  opcode = gen_int_mode (OR32_ORI (11, 11, 0), SImode);
  opcode = gen_int_mode (OR32_ORI (11, 11, 0), SImode);
  trampoline[i++] = expand_simple_binop (SImode, IOR, opcode, low, NULL,
  trampoline[i++] = expand_simple_binop (SImode, IOR, opcode, low, NULL,
                                         false, OPTAB_WIDEN);
                                         false, OPTAB_WIDEN);
 
 
  /* Emit the l.lwz of the function address. No bits to OR in here, so we can
  /* Emit the l.lwz of the function address. No bits to OR in here, so we can
     do the opcode directly. */
     do the opcode directly. */
  trampoline[i++] =
  trampoline[i++] =
    gen_int_mode (OR32_LWZ (13, 11, target_function_offset - end_addr_offset),
    gen_int_mode (OR32_LWZ (13, 11, target_function_offset - end_addr_offset),
                  SImode);
                  SImode);
 
 
  /* Emit the l.jr of the function. No bits to OR in here, so we can do the
  /* Emit the l.jr of the function. No bits to OR in here, so we can do the
     opcode directly. */
     opcode directly. */
  trampoline[i++] = gen_int_mode (OR32_JR (13), SImode);
  trampoline[i++] = gen_int_mode (OR32_JR (13), SImode);
 
 
  /* Emit the l.lwz of the static chain. No bits to OR in here, so we can
  /* Emit the l.lwz of the static chain. No bits to OR in here, so we can
     do the opcode directly. */
     do the opcode directly. */
  trampoline[i++] =
  trampoline[i++] =
    gen_int_mode (OR32_LWZ (STATIC_CHAIN_REGNUM, 11,
    gen_int_mode (OR32_LWZ (STATIC_CHAIN_REGNUM, 11,
                            static_chain_offset - end_addr_offset), SImode);
                            static_chain_offset - end_addr_offset), SImode);
 
 
  /* Copy the trampoline code.  Leave any padding uninitialized.  */
  /* Copy the trampoline code.  Leave any padding uninitialized.  */
  for (j = 0; j < i; j++)
  for (j = 0; j < i; j++)
    {
    {
      mem = adjust_address (m_tramp, SImode, j * GET_MODE_SIZE (SImode));
      mem = adjust_address (m_tramp, SImode, j * GET_MODE_SIZE (SImode));
      or32_emit_move (mem, trampoline[j]);
      or32_emit_move (mem, trampoline[j]);
    }
    }
 
 
  /* Set up the static chain pointer field.  */
  /* Set up the static chain pointer field.  */
  mem = adjust_address (m_tramp, ptr_mode, static_chain_offset);
  mem = adjust_address (m_tramp, ptr_mode, static_chain_offset);
  or32_emit_move (mem, chain_value);
  or32_emit_move (mem, chain_value);
 
 
  /* Set up the target function field.  */
  /* Set up the target function field.  */
  mem = adjust_address (m_tramp, ptr_mode, target_function_offset);
  mem = adjust_address (m_tramp, ptr_mode, target_function_offset);
  or32_emit_move (mem, XEXP (DECL_RTL (fndecl), 0));
  or32_emit_move (mem, XEXP (DECL_RTL (fndecl), 0));
 
 
  /* Flushing the trampoline from the instruction cache needs to be done
  /* Flushing the trampoline from the instruction cache needs to be done
     here. */
     here. */
 
 
}       /* or32_trampoline_init () */
}       /* or32_trampoline_init () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Provide support for DW_AT_calling_convention
/*!Provide support for DW_AT_calling_convention
 
 
   Define this to enable the dwarf attribute DW_AT_calling_convention to be
   Define this to enable the dwarf attribute DW_AT_calling_convention to be
   emitted for each function. Instead of an integer return the enum value for
   emitted for each function. Instead of an integer return the enum value for
   the DW_CC_ tag.
   the DW_CC_ tag.
 
 
   To support optional call frame debugging information, you must also define
   To support optional call frame debugging information, you must also define
   INCOMING_RETURN_ADDR_RTX and either set RTX_FRAME_RELATED_P on the prologue
   INCOMING_RETURN_ADDR_RTX and either set RTX_FRAME_RELATED_P on the prologue
   insns if you use RTL for the prologue, or call "dwarf2out_def_cfa" and
   insns if you use RTL for the prologue, or call "dwarf2out_def_cfa" and
   "dwarf2out_reg_save" as appropriate from TARGET_ASM_FUNCTION_PROLOGUE if
   "dwarf2out_reg_save" as appropriate from TARGET_ASM_FUNCTION_PROLOGUE if
   you don’t.
   you don’t.
 
 
   For the OR32, it should be sufficient to return DW_CC_normal in all cases.
   For the OR32, it should be sufficient to return DW_CC_normal in all cases.
 
 
   @param[in] function  The function requiring debug information
   @param[in] function  The function requiring debug information
 
 
   @return  The enum of the DW_CC tag.                                        */
   @return  The enum of the DW_CC tag.                                        */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static int
static int
or32_dwarf_calling_convention (const_tree  function ATTRIBUTE_UNUSED)
or32_dwarf_calling_convention (const_tree  function ATTRIBUTE_UNUSED)
{
{
  return  DW_CC_normal;
  return  DW_CC_normal;
 
 
}       /* or32_dwarf_calling_convention () */
}       /* or32_dwarf_calling_convention () */
 
 
/* If DELTA doesn't fit into a 16 bit signed number, emit instructions to
/* If DELTA doesn't fit into a 16 bit signed number, emit instructions to
   add the highpart to DST; return the signed-16-bit lowpart of DELTA.
   add the highpart to DST; return the signed-16-bit lowpart of DELTA.
   TMP_REGNO is a register that may be used to load a constant.  */
   TMP_REGNO is a register that may be used to load a constant.  */
static HOST_WIDE_INT
static HOST_WIDE_INT
or32_output_highadd (FILE *file,
or32_output_highadd (FILE *file,
                     const char *dst, int tmp_regno, HOST_WIDE_INT delta)
                     const char *dst, int tmp_regno, HOST_WIDE_INT delta)
{
{
  if (delta < -32768 || delta > 32767)
  if (delta < -32768 || delta > 32767)
    {
    {
      if (delta >= -65536 && delta < 65534)
      if (delta >= -65536 && delta < 65534)
        {
        {
          asm_fprintf (file, "\tl.addi\t%s,%s,%d\n",
          asm_fprintf (file, "\tl.addi\t%s,%s,%d\n",
                       dst, dst, (int) (delta + 1) >> 1);
                       dst, dst, (int) (delta + 1) >> 1);
          delta >>= 1;
          delta >>= 1;
        }
        }
      else
      else
        {
        {
          const char *tmp = reg_names[tmp_regno];
          const char *tmp = reg_names[tmp_regno];
          HOST_WIDE_INT high = (delta + 0x8000) >> 16;
          HOST_WIDE_INT high = (delta + 0x8000) >> 16;
 
 
          gcc_assert (call_used_regs[tmp_regno]);
          gcc_assert (call_used_regs[tmp_regno]);
          asm_fprintf (file, "\tl.movhi\t%s,%d\n" "\tl.add\t%s,%s,%s\n",
          asm_fprintf (file, "\tl.movhi\t%s,%d\n" "\tl.add\t%s,%s,%s\n",
                 tmp, (int) high,
                 tmp, (int) high,
                 dst, dst, tmp);
                 dst, dst, tmp);
          delta -= high << 16;
          delta -= high << 16;
        }
        }
    }
    }
  return delta;
  return delta;
}
}
 
 
/* Output a tailcall to FUNCTION.  The caller will fill in the delay slot.  */
/* Output a tailcall to FUNCTION.  The caller will fill in the delay slot.  */
static void
static void
or32_output_tailcall (FILE *file, tree function)
or32_output_tailcall (FILE *file, tree function)
{
{
  /* We'll need to add more code if we want to fully support PIC.  */
  /* We'll need to add more code if we want to fully support PIC.  */
  gcc_assert (!flag_pic || (*targetm.binds_local_p) (function));
  gcc_assert (!flag_pic || (*targetm.binds_local_p) (function));
 
 
  fputs ("\tl.j\t", file);
  fputs ("\tl.j\t", file);
  assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
  assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
  fputc ('\n', file);
  fputc ('\n', file);
}
}
 
 
static void
static void
or32_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
or32_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
                      HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
                      HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
                      tree function)
                      tree function)
{
{
  int this_regno
  int this_regno
    = aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function) ? 4 : 3;
    = aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function) ? 4 : 3;
  const char *this_name = reg_names[this_regno];
  const char *this_name = reg_names[this_regno];
 
 
 
 
  delta = or32_output_highadd (file, this_name, PROLOGUE_TMP, delta);
  delta = or32_output_highadd (file, this_name, PROLOGUE_TMP, delta);
  if (!vcall_offset)
  if (!vcall_offset)
    or32_output_tailcall (file, function);
    or32_output_tailcall (file, function);
  if (delta || !vcall_offset)
  if (delta || !vcall_offset)
    asm_fprintf (file, "\tl.addi\t%s,%s,%d\n",
    asm_fprintf (file, "\tl.addi\t%s,%s,%d\n",
                 this_name, this_name, (int) delta);
                 this_name, this_name, (int) delta);
 
 
  /* If needed, add *(*THIS + VCALL_OFFSET) to THIS.  */
  /* If needed, add *(*THIS + VCALL_OFFSET) to THIS.  */
  if (vcall_offset != 0)
  if (vcall_offset != 0)
    {
    {
      const char *tmp_name = reg_names[PROLOGUE_TMP];
      const char *tmp_name = reg_names[PROLOGUE_TMP];
 
 
      /* l.lwz tmp,0(this)           --> tmp = *this
      /* l.lwz tmp,0(this)           --> tmp = *this
         l.lwz tmp,vcall_offset(tmp) --> tmp = *(*this + vcall_offset)
         l.lwz tmp,vcall_offset(tmp) --> tmp = *(*this + vcall_offset)
         add this,this,tmp           --> this += *(*this + vcall_offset) */
         add this,this,tmp           --> this += *(*this + vcall_offset) */
 
 
      asm_fprintf (file, "\tl.lwz\t%s,0(%s)\n",
      asm_fprintf (file, "\tl.lwz\t%s,0(%s)\n",
                   tmp_name, this_name);
                   tmp_name, this_name);
      vcall_offset = or32_output_highadd (file, tmp_name,
      vcall_offset = or32_output_highadd (file, tmp_name,
                                          STATIC_CHAIN_REGNUM, vcall_offset);
                                          STATIC_CHAIN_REGNUM, vcall_offset);
      asm_fprintf (file, "\tl.lwz\t%s,%d(%s)\n",
      asm_fprintf (file, "\tl.lwz\t%s,%d(%s)\n",
                   tmp_name, (int) vcall_offset, tmp_name);
                   tmp_name, (int) vcall_offset, tmp_name);
      or32_output_tailcall (file, function);
      or32_output_tailcall (file, function);
      asm_fprintf (file, "\tl.add\t%s,%s,%s\n", this_name, this_name, tmp_name);
      asm_fprintf (file, "\tl.add\t%s,%s,%s\n", this_name, this_name, tmp_name);
    }
    }
}
}
 
 
 
static bool
 
or32_handle_option (size_t code, const char *arg ATTRIBUTE_UNUSED,
 
                    int value ATTRIBUTE_UNUSED)
 
{
 
  switch (code)
 
    {
 
    case OPT_mnewlib:
 
      or32_libc = or32_libc_newlib;
 
      return true;
 
    case OPT_muclibc:
 
      or32_libc = or32_libc_uclibc;
 
      return true;
 
    case OPT_mglibc:
 
      or32_libc = or32_libc_glibc;
 
      return false;
 
    default:
 
      return true;
 
    }
 
}
 
 
 
 
/* ========================================================================== */
/* ========================================================================== */
/* Target hook initialization.
/* Target hook initialization.
 
 
   In most cases these use the static functions declared above. They have
   In most cases these use the static functions declared above. They have
   defaults, so must be undefined first, before being redefined.
   defaults, so must be undefined first, before being redefined.
 
 
   The description of what they do is found with the function above, unless it
   The description of what they do is found with the function above, unless it
   is a standard function or a constant, in which case it is defined here (as
   is a standard function or a constant, in which case it is defined here (as
   with TARGET_ASM_NAMED_SECTION).
   with TARGET_ASM_NAMED_SECTION).
 
 
   The final declaration is of the global "targetm" structure. */
   The final declaration is of the global "targetm" structure. */
 
 
 
 
/* Default target_flags if no switches specified. */
/* Default target_flags if no switches specified. */
#undef  TARGET_DEFAULT_TARGET_FLAGS
#undef  TARGET_DEFAULT_TARGET_FLAGS
#define TARGET_DEFAULT_TARGET_FLAGS (MASK_HARD_MUL | MASK_SCHED_LOGUE)
#define TARGET_DEFAULT_TARGET_FLAGS (MASK_HARD_MUL | MASK_SCHED_LOGUE)
 
 
 
#undef TARGET_HANDLE_OPTION
 
#define TARGET_HANDLE_OPTION or32_handle_option
 
 
/* Output assembly directives to switch to section name. The section should
/* Output assembly directives to switch to section name. The section should
   have attributes as specified by flags, which is a bit mask of the SECTION_*
   have attributes as specified by flags, which is a bit mask of the SECTION_*
   flags defined in ‘output.h’. If decl is non-NULL, it is the VAR_DECL or
   flags defined in ‘output.h’. If decl is non-NULL, it is the VAR_DECL or
   FUNCTION_DECL with which this section is associated.
   FUNCTION_DECL with which this section is associated.
 
 
   For OR32, we use the default ELF sectioning. */
   For OR32, we use the default ELF sectioning. */
#undef  TARGET_ASM_NAMED_SECTION
#undef  TARGET_ASM_NAMED_SECTION
#define TARGET_ASM_NAMED_SECTION  default_elf_asm_named_section
#define TARGET_ASM_NAMED_SECTION  default_elf_asm_named_section
 
 
#undef  TARGET_ASM_FUNCTION_PROLOGUE
#undef  TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE or32_output_function_prologue
#define TARGET_ASM_FUNCTION_PROLOGUE or32_output_function_prologue
 
 
#undef  TARGET_ASM_FUNCTION_EPILOGUE
#undef  TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE or32_output_function_epilogue
#define TARGET_ASM_FUNCTION_EPILOGUE or32_output_function_epilogue
 
 
#undef  TARGET_FUNCTION_VALUE
#undef  TARGET_FUNCTION_VALUE
#define TARGET_FUNCTION_VALUE or32_function_value
#define TARGET_FUNCTION_VALUE or32_function_value
 
 
#undef  TARGET_FUNCTION_OK_FOR_SIBCALL
#undef  TARGET_FUNCTION_OK_FOR_SIBCALL
#define TARGET_FUNCTION_OK_FOR_SIBCALL or32_function_ok_for_sibcall
#define TARGET_FUNCTION_OK_FOR_SIBCALL or32_function_ok_for_sibcall
 
 
#undef  TARGET_PASS_BY_REFERENCE
#undef  TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE or32_pass_by_reference
#define TARGET_PASS_BY_REFERENCE or32_pass_by_reference
 
 
#undef  TARGET_ARG_PARTIAL_BYTES
#undef  TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES or32_arg_partial_bytes
#define TARGET_ARG_PARTIAL_BYTES or32_arg_partial_bytes
 
 
/* This target hook returns TRUE if an argument declared in a prototype as an
/* This target hook returns TRUE if an argument declared in a prototype as an
   integral type smaller than int should actually be passed as an int. In
   integral type smaller than int should actually be passed as an int. In
   addition to avoiding errors in certain cases of mismatch, it also makes for
   addition to avoiding errors in certain cases of mismatch, it also makes for
   better code on certain machines.
   better code on certain machines.
 
 
   The default is to not promote prototypes.
   The default is to not promote prototypes.
 
 
   For the OR32 we do require this, so use a utility hook, which always
   For the OR32 we do require this, so use a utility hook, which always
   returns TRUE. */
   returns TRUE. */
#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_PROMOTE_FUNCTION_MODE
#undef  TARGET_PROMOTE_FUNCTION_MODE
#define TARGET_PROMOTE_FUNCTION_MODE or32_promote_function_mode
#define TARGET_PROMOTE_FUNCTION_MODE or32_promote_function_mode
 
 
#undef  TARGET_LEGITIMATE_ADDRESS_P
#undef  TARGET_LEGITIMATE_ADDRESS_P
#define TARGET_LEGITIMATE_ADDRESS_P  or32_legitimate_address_p
#define TARGET_LEGITIMATE_ADDRESS_P  or32_legitimate_address_p
 
 
#undef  TARGET_TRAMPOLINE_INIT
#undef  TARGET_TRAMPOLINE_INIT
#define TARGET_TRAMPOLINE_INIT  or32_trampoline_init
#define TARGET_TRAMPOLINE_INIT  or32_trampoline_init
 
 
#undef TARGET_DWARF_CALLING_CONVENTION
#undef TARGET_DWARF_CALLING_CONVENTION
#define TARGET_DWARF_CALLING_CONVENTION  or32_dwarf_calling_convention
#define TARGET_DWARF_CALLING_CONVENTION  or32_dwarf_calling_convention
 
 
#undef TARGET_ASM_OUTPUT_MI_THUNK
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK or32_output_mi_thunk
#define TARGET_ASM_OUTPUT_MI_THUNK or32_output_mi_thunk
 
 
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true
 
 
/* uClibc has some instances where (non-coforming to ISO C) a non-varargs
/* uClibc has some instances where (non-coforming to ISO C) a non-varargs
   prototype is in scope when calling that function which is implemented
   prototype is in scope when calling that function which is implemented
   as varargs.  We want this to work at least where none of the anonymous
   as varargs.  We want this to work at least where none of the anonymous
   arguments are used.  I.e. we want the last named argument to be known
   arguments are used.  I.e. we want the last named argument to be known
   as named so it can be passed in a register, varars funtion or not.  */
   as named so it can be passed in a register, varars funtion or not.  */
#undef TARGET_STRICT_ARGUMENT_NAMING
#undef TARGET_STRICT_ARGUMENT_NAMING
#define TARGET_STRICT_ARGUMENT_NAMING hook_bool_CUMULATIVE_ARGS_true
#define TARGET_STRICT_ARGUMENT_NAMING hook_bool_CUMULATIVE_ARGS_true
 
 
/* Trampoline stubs are yet to be written. */
/* Trampoline stubs are yet to be written. */
/* #define TARGET_ASM_TRAMPOLINE_TEMPLATE */
/* #define TARGET_ASM_TRAMPOLINE_TEMPLATE */
/* #define TARGET_TRAMPOLINE_INIT */
/* #define TARGET_TRAMPOLINE_INIT */
 
 
/* Initialize the GCC target structure.  */
/* Initialize the GCC target structure.  */
struct gcc_target targetm = TARGET_INITIALIZER;
struct gcc_target targetm = TARGET_INITIALIZER;
 
 
/* Lay out structs with increased alignment so that they can be accessed
/* Lay out structs with increased alignment so that they can be accessed
   more efficiently.  But don't increase the size of one or two byte
   more efficiently.  But don't increase the size of one or two byte
   structs.  */
   structs.  */
int
int
or32_struct_alignment (tree t)
or32_struct_alignment (tree t)
{
{
  unsigned HOST_WIDE_INT total = 0;
  unsigned HOST_WIDE_INT total = 0;
  int default_align_fields = 0;
  int default_align_fields = 0;
  int special_align_fields = 0;
  int special_align_fields = 0;
  tree field;
  tree field;
  unsigned max_align
  unsigned max_align
    = maximum_field_alignment ? maximum_field_alignment : BIGGEST_ALIGNMENT;
    = maximum_field_alignment ? maximum_field_alignment : BIGGEST_ALIGNMENT;
  bool struct_p;
  bool struct_p;
 
 
  switch (TREE_CODE (t))
  switch (TREE_CODE (t))
    {
    {
    case RECORD_TYPE:
    case RECORD_TYPE:
      struct_p = true; break;
      struct_p = true; break;
    case UNION_TYPE: case QUAL_UNION_TYPE:
    case UNION_TYPE: case QUAL_UNION_TYPE:
      struct_p = false; break;
      struct_p = false; break;
    default: gcc_unreachable ();
    default: gcc_unreachable ();
    }
    }
  /* Skip all non field decls */
  /* Skip all non field decls */
  for (field = TYPE_FIELDS (t); field; field = TREE_CHAIN (field))
  for (field = TYPE_FIELDS (t); field; field = TREE_CHAIN (field))
    {
    {
      unsigned HOST_WIDE_INT field_size;
      unsigned HOST_WIDE_INT field_size;
 
 
      if (TREE_CODE (field) != FIELD_DECL)
      if (TREE_CODE (field) != FIELD_DECL)
        continue;
        continue;
      /* If this is a field in a non-qualified union, or the sole field in
      /* If this is a field in a non-qualified union, or the sole field in
         a struct, and the alignment was set by the user, don't change the
         a struct, and the alignment was set by the user, don't change the
         alignment.
         alignment.
         If the field is a struct/union in a non-qualified union, we already
         If the field is a struct/union in a non-qualified union, we already
         had sufficient opportunity to pad it - if we didn't, that'd be
         had sufficient opportunity to pad it - if we didn't, that'd be
         because the alignment was set as above.
         because the alignment was set as above.
         Likewise if the field is a struct/union and the sole field in a
         Likewise if the field is a struct/union and the sole field in a
         struct.  */
         struct.  */
      if (DECL_USER_ALIGN (field)
      if (DECL_USER_ALIGN (field)
          || TYPE_USER_ALIGN (TREE_TYPE (field))
          || TYPE_USER_ALIGN (TREE_TYPE (field))
          || TREE_CODE (TREE_TYPE (field)) == UNION_TYPE
          || TREE_CODE (TREE_TYPE (field)) == UNION_TYPE
          || TREE_CODE (TREE_TYPE (field)) == QUAL_UNION_TYPE
          || TREE_CODE (TREE_TYPE (field)) == QUAL_UNION_TYPE
          || TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
          || TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
        {
        {
          if (TREE_CODE (t) == UNION_TYPE)
          if (TREE_CODE (t) == UNION_TYPE)
            return 0;
            return 0;
          special_align_fields++;
          special_align_fields++;
        }
        }
      else if (DECL_PACKED (field))
      else if (DECL_PACKED (field))
        special_align_fields++;
        special_align_fields++;
      else
      else
        default_align_fields++;
        default_align_fields++;
      if (!host_integerp (DECL_SIZE (field), 1))
      if (!host_integerp (DECL_SIZE (field), 1))
        field_size = max_align;
        field_size = max_align;
      else
      else
        field_size = tree_low_cst (DECL_SIZE (field), 1);
        field_size = tree_low_cst (DECL_SIZE (field), 1);
      if (field_size >= BIGGEST_ALIGNMENT)
      if (field_size >= BIGGEST_ALIGNMENT)
        total = max_align;
        total = max_align;
      if (struct_p)
      if (struct_p)
        total += field_size;
        total += field_size;
      else
      else
        total = MAX (total, field_size);
        total = MAX (total, field_size);
    }
    }
 
 
  if (!default_align_fields
  if (!default_align_fields
      && (TREE_CODE (t) != RECORD_TYPE || special_align_fields <= 1))
      && (TREE_CODE (t) != RECORD_TYPE || special_align_fields <= 1))
    return 0;
    return 0;
  return total < max_align ? (1U << ceil_log2 (total)) : max_align;
  return total < max_align ? (1U << ceil_log2 (total)) : max_align;
}
}
 
 
/* Increase the alignment of objects so that they are easier to copy.
/* Increase the alignment of objects so that they are easier to copy.
   Note that this can cause more struct copies to be inlined, so code
   Note that this can cause more struct copies to be inlined, so code
   size might increase, but so should perfromance.  */
   size might increase, but so should perfromance.  */
int
int
or32_data_alignment (tree t, int align)
or32_data_alignment (tree t, int align)
{
{
  if (align < FASTEST_ALIGNMENT && TREE_CODE (t) == ARRAY_TYPE)
  if (align < FASTEST_ALIGNMENT && TREE_CODE (t) == ARRAY_TYPE)
    {
    {
      int size = int_size_in_bytes (t);
      int size = int_size_in_bytes (t);
 
 
      return (size > 0 && size < FASTEST_ALIGNMENT / BITS_PER_UNIT
      return (size > 0 && size < FASTEST_ALIGNMENT / BITS_PER_UNIT
              ? (1 << floor_log2 (size)) * BITS_PER_UNIT
              ? (1 << floor_log2 (size)) * BITS_PER_UNIT
              : FASTEST_ALIGNMENT);
              : FASTEST_ALIGNMENT);
    }
    }
  return align;
  return align;
}
}
 
 

powered by: WebSVN 2.1.0

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