OpenCores
URL https://opencores.org/ocsvn/openrisc_me/openrisc_me/trunk

Subversion Repositories openrisc_me

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /openrisc/trunk/gnu-src/gcc-4.2.2/gcc/config/frv
    from Rev 38 to Rev 154
    Reverse comparison

Rev 38 → Rev 154

/predicates.md
0,0 → 1,1549
;; Predicate definitions for Frv.
;; Copyright (C) 2005, 2007 Free Software Foundation, Inc.
;;
;; This file is part of GCC.
;;
;; GCC 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 Software Foundation; either version 3, or (at your option)
;; any later version.
;;
;; GCC is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GCC; see the file COPYING3. If not see
;; <http://www.gnu.org/licenses/>.
 
;; Return true if operand is a GPR register.
 
(define_predicate "integer_register_operand"
(match_code "reg,subreg")
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
 
op = SUBREG_REG (op);
}
 
if (GET_CODE (op) != REG)
return FALSE;
 
return GPR_AP_OR_PSEUDO_P (REGNO (op));
})
 
;; Return 1 is OP is a memory operand, or will be turned into one by
;; reload.
 
(define_predicate "frv_load_operand"
(match_code "reg,subreg,mem")
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (reload_in_progress)
{
rtx tmp = op;
if (GET_CODE (tmp) == SUBREG)
tmp = SUBREG_REG (tmp);
if (GET_CODE (tmp) == REG
&& REGNO (tmp) >= FIRST_PSEUDO_REGISTER)
op = reg_equiv_memory_loc[REGNO (tmp)];
}
 
return op && memory_operand (op, mode);
})
 
;; Return true if operand is a GPR register. Do not allow SUBREG's
;; here, in order to prevent a combine bug.
 
(define_predicate "gpr_no_subreg_operand"
(match_code "reg")
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) != REG)
return FALSE;
 
return GPR_OR_PSEUDO_P (REGNO (op));
})
 
;; Return 1 if operand is a GPR register or a FPR register.
 
(define_predicate "gpr_or_fpr_operand"
(match_code "reg,subreg")
{
int regno;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
 
op = SUBREG_REG (op);
}
 
if (GET_CODE (op) != REG)
return FALSE;
 
regno = REGNO (op);
if (GPR_P (regno) || FPR_P (regno) || regno >= FIRST_PSEUDO_REGISTER)
return TRUE;
 
return FALSE;
})
 
;; Return 1 if operand is a GPR register or 12 bit signed immediate.
 
(define_predicate "gpr_or_int12_operand"
(match_code "reg,subreg,const_int,const")
{
if (GET_CODE (op) == CONST_INT)
return IN_RANGE_P (INTVAL (op), -2048, 2047);
 
if (got12_operand (op, mode))
return true;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
 
op = SUBREG_REG (op);
}
 
if (GET_CODE (op) != REG)
return FALSE;
 
return GPR_OR_PSEUDO_P (REGNO (op));
})
 
;; Return 1 if operand is a GPR register, or a FPR register, or a 12
;; bit signed immediate.
 
(define_predicate "gpr_fpr_or_int12_operand"
(match_code "reg,subreg,const_int")
{
int regno;
 
if (GET_CODE (op) == CONST_INT)
return IN_RANGE_P (INTVAL (op), -2048, 2047);
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
 
op = SUBREG_REG (op);
}
 
if (GET_CODE (op) != REG)
return FALSE;
 
regno = REGNO (op);
if (GPR_P (regno) || FPR_P (regno) || regno >= FIRST_PSEUDO_REGISTER)
return TRUE;
 
return FALSE;
})
 
;; Return 1 if operand is a register or 10 bit signed immediate.
 
(define_predicate "gpr_or_int10_operand"
(match_code "reg,subreg,const_int")
{
if (GET_CODE (op) == CONST_INT)
return IN_RANGE_P (INTVAL (op), -512, 511);
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
 
op = SUBREG_REG (op);
}
 
if (GET_CODE (op) != REG)
return FALSE;
 
return GPR_OR_PSEUDO_P (REGNO (op));
})
 
;; Return 1 if operand is a register or an integer immediate.
 
(define_predicate "gpr_or_int_operand"
(match_code "reg,subreg,const_int")
{
if (GET_CODE (op) == CONST_INT)
return TRUE;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
 
op = SUBREG_REG (op);
}
 
if (GET_CODE (op) != REG)
return FALSE;
 
return GPR_OR_PSEUDO_P (REGNO (op));
})
 
;; Return true if operand is something that can be an input for a move
;; operation.
 
(define_predicate "move_source_operand"
(match_code "reg,subreg,const_int,mem,const_double,const,symbol_ref,label_ref")
{
rtx subreg;
enum rtx_code code;
 
switch (GET_CODE (op))
{
default:
break;
 
case CONST_INT:
case CONST_DOUBLE:
return immediate_operand (op, mode);
 
case SUBREG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
subreg = SUBREG_REG (op);
code = GET_CODE (subreg);
if (code == MEM)
return frv_legitimate_address_p (mode, XEXP (subreg, 0),
reload_completed, FALSE, FALSE);
 
return (code == REG);
 
case REG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
return TRUE;
 
case MEM:
return frv_legitimate_memory_operand (op, mode, FALSE);
}
 
return FALSE;
})
 
;; Return true if operand is something that can be an output for a
;; move operation.
 
(define_predicate "move_destination_operand"
(match_code "reg,subreg,mem")
{
rtx subreg;
enum rtx_code code;
 
switch (GET_CODE (op))
{
default:
break;
 
case SUBREG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
subreg = SUBREG_REG (op);
code = GET_CODE (subreg);
if (code == MEM)
return frv_legitimate_address_p (mode, XEXP (subreg, 0),
reload_completed, FALSE, FALSE);
 
return (code == REG);
 
case REG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
return TRUE;
 
case MEM:
return frv_legitimate_memory_operand (op, mode, FALSE);
}
 
return FALSE;
})
 
;; Return true if we the operand is a valid destination for a movcc_fp
;; instruction. This means rejecting fcc_operands, since we need
;; scratch registers to write to them.
 
(define_predicate "movcc_fp_destination_operand"
(match_code "reg,subreg,mem")
{
if (fcc_operand (op, mode))
return FALSE;
 
return move_destination_operand (op, mode);
})
 
;; Return true if operand is something that can be an input for a
;; conditional move operation.
 
(define_predicate "condexec_source_operand"
(match_code "reg,subreg,const_int,mem,const_double")
{
rtx subreg;
enum rtx_code code;
 
switch (GET_CODE (op))
{
default:
break;
 
case CONST_INT:
case CONST_DOUBLE:
return ZERO_P (op);
 
case SUBREG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
subreg = SUBREG_REG (op);
code = GET_CODE (subreg);
if (code == MEM)
return frv_legitimate_address_p (mode, XEXP (subreg, 0),
reload_completed, TRUE, FALSE);
 
return (code == REG);
 
case REG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
return TRUE;
 
case MEM:
return frv_legitimate_memory_operand (op, mode, TRUE);
}
 
return FALSE;
})
 
;; Return true if operand is something that can be an output for a
;; conditional move operation.
 
(define_predicate "condexec_dest_operand"
(match_code "reg,subreg,mem")
{
rtx subreg;
enum rtx_code code;
 
switch (GET_CODE (op))
{
default:
break;
 
case SUBREG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
subreg = SUBREG_REG (op);
code = GET_CODE (subreg);
if (code == MEM)
return frv_legitimate_address_p (mode, XEXP (subreg, 0),
reload_completed, TRUE, FALSE);
 
return (code == REG);
 
case REG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
return TRUE;
 
case MEM:
return frv_legitimate_memory_operand (op, mode, TRUE);
}
 
return FALSE;
})
 
;; Return true if operand is a register of any flavor or a 0 of the
;; appropriate type.
 
(define_predicate "reg_or_0_operand"
(match_code "reg,subreg,const_int,const_double")
{
switch (GET_CODE (op))
{
default:
break;
 
case REG:
case SUBREG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
return register_operand (op, mode);
 
case CONST_INT:
case CONST_DOUBLE:
return ZERO_P (op);
}
 
return FALSE;
})
 
;; Return true if operand is the link register.
 
(define_predicate "lr_operand"
(match_code "reg")
{
if (GET_CODE (op) != REG)
return FALSE;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (REGNO (op) != LR_REGNO && REGNO (op) < FIRST_PSEUDO_REGISTER)
return FALSE;
 
return TRUE;
})
 
;; Return true if operand is a gpr register or a valid memory operand.
 
(define_predicate "gpr_or_memory_operand"
(match_code "reg,subreg,mem")
{
return (integer_register_operand (op, mode)
|| frv_legitimate_memory_operand (op, mode, FALSE));
})
 
;; Return true if operand is a gpr register, a valid memory operand,
;; or a memory operand that can be made valid using an additional gpr
;; register.
 
(define_predicate "gpr_or_memory_operand_with_scratch"
(match_code "reg,subreg,mem")
{
rtx addr;
 
if (gpr_or_memory_operand (op, mode))
return TRUE;
 
if (GET_CODE (op) != MEM)
return FALSE;
 
if (GET_MODE (op) != mode)
return FALSE;
 
addr = XEXP (op, 0);
 
if (GET_CODE (addr) != PLUS)
return FALSE;
if (!integer_register_operand (XEXP (addr, 0), Pmode))
return FALSE;
 
if (GET_CODE (XEXP (addr, 1)) != CONST_INT)
return FALSE;
 
return TRUE;
})
 
;; Return true if operand is a fpr register or a valid memory
;; operation.
 
(define_predicate "fpr_or_memory_operand"
(match_code "reg,subreg,mem")
{
return (fpr_operand (op, mode)
|| frv_legitimate_memory_operand (op, mode, FALSE));
})
 
;; Return 1 if operand is a 12 bit signed immediate.
 
(define_predicate "int12_operand"
(match_code "const_int")
{
if (GET_CODE (op) != CONST_INT)
return FALSE;
 
return IN_RANGE_P (INTVAL (op), -2048, 2047);
})
 
;; Return 1 if operand is an integer constant that takes 2
;; instructions to load up and can be split into sethi/setlo
;; instructions..
 
(define_predicate "int_2word_operand"
(match_code "const_int,const_double,symbol_ref,label_ref,const")
{
HOST_WIDE_INT value;
REAL_VALUE_TYPE rv;
long l;
 
switch (GET_CODE (op))
{
default:
break;
 
case LABEL_REF:
if (TARGET_FDPIC)
return FALSE;
return (flag_pic == 0);
 
case CONST:
if (flag_pic || TARGET_FDPIC)
return FALSE;
 
op = XEXP (op, 0);
if (GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT)
op = XEXP (op, 0);
return GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF;
 
case SYMBOL_REF:
if (TARGET_FDPIC)
return FALSE;
/* small data references are already 1 word */
return (flag_pic == 0) && (! SYMBOL_REF_SMALL_P (op));
 
case CONST_INT:
return ! IN_RANGE_P (INTVAL (op), -32768, 32767);
 
case CONST_DOUBLE:
if (GET_MODE (op) == SFmode)
{
REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
REAL_VALUE_TO_TARGET_SINGLE (rv, l);
value = l;
return ! IN_RANGE_P (value, -32768, 32767);
}
else if (GET_MODE (op) == VOIDmode)
{
value = CONST_DOUBLE_LOW (op);
return ! IN_RANGE_P (value, -32768, 32767);
}
break;
}
 
return FALSE;
})
 
;; Return true if operand is the uClinux PIC register.
 
(define_predicate "fdpic_operand"
(match_code "reg")
{
if (!TARGET_FDPIC)
return FALSE;
 
if (GET_CODE (op) != REG)
return FALSE;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (REGNO (op) != FDPIC_REGNO && REGNO (op) < FIRST_PSEUDO_REGISTER)
return FALSE;
 
return TRUE;
})
 
;; TODO: Add a comment here.
 
(define_predicate "fdpic_fptr_operand"
(match_code "reg")
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) != REG)
return FALSE;
if (REGNO (op) != FDPIC_FPTR_REGNO && REGNO (op) < FIRST_PSEUDO_REGISTER)
return FALSE;
return TRUE;
})
 
;; An address operand that may use a pair of registers, an addressing
;; mode that we reject in general.
 
(define_predicate "ldd_address_operand"
(match_code "reg,subreg,plus")
{
if (GET_MODE (op) != mode && GET_MODE (op) != VOIDmode)
return FALSE;
 
return frv_legitimate_address_p (DImode, op, reload_completed, FALSE, TRUE);
})
 
;; TODO: Add a comment here.
 
(define_predicate "got12_operand"
(match_code "const")
{
struct frv_unspec unspec;
 
if (frv_const_unspec_p (op, &unspec))
switch (unspec.reloc)
{
case R_FRV_GOT12:
case R_FRV_GOTOFF12:
case R_FRV_FUNCDESC_GOT12:
case R_FRV_FUNCDESC_GOTOFF12:
case R_FRV_GPREL12:
case R_FRV_TLSMOFF12:
return true;
}
return false;
})
 
;; Return true if OP is a valid const-unspec expression.
 
(define_predicate "const_unspec_operand"
(match_code "const")
{
struct frv_unspec unspec;
 
return frv_const_unspec_p (op, &unspec);
})
 
;; Return true if operand is an icc register.
 
(define_predicate "icc_operand"
(match_code "reg")
{
int regno;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) != REG)
return FALSE;
 
regno = REGNO (op);
return ICC_OR_PSEUDO_P (regno);
})
 
;; Return true if operand is an fcc register.
 
(define_predicate "fcc_operand"
(match_code "reg")
{
int regno;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) != REG)
return FALSE;
 
regno = REGNO (op);
return FCC_OR_PSEUDO_P (regno);
})
 
;; Return true if operand is either an fcc or icc register.
 
(define_predicate "cc_operand"
(match_code "reg")
{
int regno;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) != REG)
return FALSE;
 
regno = REGNO (op);
if (CC_OR_PSEUDO_P (regno))
return TRUE;
 
return FALSE;
})
 
;; Return true if operand is an integer CCR register.
 
(define_predicate "icr_operand"
(match_code "reg")
{
int regno;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) != REG)
return FALSE;
 
regno = REGNO (op);
return ICR_OR_PSEUDO_P (regno);
})
 
;; Return true if operand is an fcc register.
 
(define_predicate "fcr_operand"
(match_code "reg")
{
int regno;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) != REG)
return FALSE;
 
regno = REGNO (op);
return FCR_OR_PSEUDO_P (regno);
})
 
;; Return true if operand is either an fcc or icc register.
 
(define_predicate "cr_operand"
(match_code "reg")
{
int regno;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) != REG)
return FALSE;
 
regno = REGNO (op);
if (CR_OR_PSEUDO_P (regno))
return TRUE;
 
return FALSE;
})
 
;; Return true if operand is a FPR register.
 
(define_predicate "fpr_operand"
(match_code "reg,subreg")
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
 
op = SUBREG_REG (op);
}
 
if (GET_CODE (op) != REG)
return FALSE;
 
return FPR_OR_PSEUDO_P (REGNO (op));
})
 
;; Return true if operand is an even GPR or FPR register.
 
(define_predicate "even_reg_operand"
(match_code "reg,subreg")
{
int regno;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
 
op = SUBREG_REG (op);
}
 
if (GET_CODE (op) != REG)
return FALSE;
 
regno = REGNO (op);
if (regno >= FIRST_PSEUDO_REGISTER)
return TRUE;
 
if (GPR_P (regno))
return (((regno - GPR_FIRST) & 1) == 0);
 
if (FPR_P (regno))
return (((regno - FPR_FIRST) & 1) == 0);
 
return FALSE;
})
 
;; Return true if operand is an odd GPR register.
 
(define_predicate "odd_reg_operand"
(match_code "reg,subreg")
{
int regno;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
 
op = SUBREG_REG (op);
}
 
if (GET_CODE (op) != REG)
return FALSE;
 
regno = REGNO (op);
/* Assume that reload will give us an even register. */
if (regno >= FIRST_PSEUDO_REGISTER)
return FALSE;
 
if (GPR_P (regno))
return (((regno - GPR_FIRST) & 1) != 0);
 
if (FPR_P (regno))
return (((regno - FPR_FIRST) & 1) != 0);
 
return FALSE;
})
 
;; Return true if operand is an even GPR register.
 
(define_predicate "even_gpr_operand"
(match_code "reg,subreg")
{
int regno;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
 
op = SUBREG_REG (op);
}
 
if (GET_CODE (op) != REG)
return FALSE;
 
regno = REGNO (op);
if (regno >= FIRST_PSEUDO_REGISTER)
return TRUE;
 
if (! GPR_P (regno))
return FALSE;
 
return (((regno - GPR_FIRST) & 1) == 0);
})
 
;; Return true if operand is an odd GPR register.
 
(define_predicate "odd_gpr_operand"
(match_code "reg,subreg")
{
int regno;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
 
op = SUBREG_REG (op);
}
 
if (GET_CODE (op) != REG)
return FALSE;
 
regno = REGNO (op);
/* Assume that reload will give us an even register. */
if (regno >= FIRST_PSEUDO_REGISTER)
return FALSE;
 
if (! GPR_P (regno))
return FALSE;
 
return (((regno - GPR_FIRST) & 1) != 0);
})
 
;; Return true if operand is a quad aligned FPR register.
 
(define_predicate "quad_fpr_operand"
(match_code "reg,subreg")
{
int regno;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
 
op = SUBREG_REG (op);
}
 
if (GET_CODE (op) != REG)
return FALSE;
 
regno = REGNO (op);
if (regno >= FIRST_PSEUDO_REGISTER)
return TRUE;
 
if (! FPR_P (regno))
return FALSE;
 
return (((regno - FPR_FIRST) & 3) == 0);
})
 
;; Return true if operand is an even FPR register.
 
(define_predicate "even_fpr_operand"
(match_code "reg,subreg")
{
int regno;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
 
op = SUBREG_REG (op);
}
 
if (GET_CODE (op) != REG)
return FALSE;
 
regno = REGNO (op);
if (regno >= FIRST_PSEUDO_REGISTER)
return TRUE;
 
if (! FPR_P (regno))
return FALSE;
 
return (((regno - FPR_FIRST) & 1) == 0);
})
 
;; Return true if operand is an odd FPR register.
 
(define_predicate "odd_fpr_operand"
(match_code "reg,subreg")
{
int regno;
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
 
op = SUBREG_REG (op);
}
 
if (GET_CODE (op) != REG)
return FALSE;
 
regno = REGNO (op);
/* Assume that reload will give us an even register. */
if (regno >= FIRST_PSEUDO_REGISTER)
return FALSE;
 
if (! FPR_P (regno))
return FALSE;
 
return (((regno - FPR_FIRST) & 1) != 0);
})
 
;; Return true if operand is a 2 word memory address that can be
;; loaded in one instruction to load or store. We assume the stack
;; and frame pointers are suitably aligned, and variables in the small
;; data area. FIXME -- at some we should recognize other globals and
;; statics. We can't assume that any old pointer is aligned, given
;; that arguments could be passed on an odd word on the stack and the
;; address taken and passed through to another function.
 
(define_predicate "dbl_memory_one_insn_operand"
(match_code "mem")
{
rtx addr;
rtx addr_reg;
 
if (! TARGET_DWORD)
return FALSE;
 
if (GET_CODE (op) != MEM)
return FALSE;
 
if (mode != VOIDmode && GET_MODE_SIZE (mode) != 2*UNITS_PER_WORD)
return FALSE;
 
addr = XEXP (op, 0);
if (GET_CODE (addr) == REG)
addr_reg = addr;
 
else if (GET_CODE (addr) == PLUS)
{
rtx addr0 = XEXP (addr, 0);
rtx addr1 = XEXP (addr, 1);
 
if (GET_CODE (addr0) != REG)
return FALSE;
 
if (got12_operand (addr1, VOIDmode))
return TRUE;
 
if (GET_CODE (addr1) != CONST_INT)
return FALSE;
 
if ((INTVAL (addr1) & 7) != 0)
return FALSE;
 
addr_reg = addr0;
}
 
else
return FALSE;
 
if (addr_reg == frame_pointer_rtx || addr_reg == stack_pointer_rtx)
return TRUE;
 
return FALSE;
})
 
;; Return true if operand is a 2 word memory address that needs to use
;; two instructions to load or store.
 
(define_predicate "dbl_memory_two_insn_operand"
(match_code "mem")
{
if (GET_CODE (op) != MEM)
return FALSE;
 
if (mode != VOIDmode && GET_MODE_SIZE (mode) != 2*UNITS_PER_WORD)
return FALSE;
 
if (! TARGET_DWORD)
return TRUE;
 
return ! dbl_memory_one_insn_operand (op, mode);
})
 
;; Return true if operand is a memory reference suitable for a call.
 
(define_predicate "call_operand"
(match_code "reg,subreg,const_int,const,symbol_ref")
{
if (GET_MODE (op) != mode && mode != VOIDmode && GET_CODE (op) != CONST_INT)
return FALSE;
 
if (GET_CODE (op) == SYMBOL_REF)
return !TARGET_LONG_CALLS || SYMBOL_REF_LOCAL_P (op);
 
/* Note this doesn't allow reg+reg or reg+imm12 addressing (which should
never occur anyway), but prevents reload from not handling the case
properly of a call through a pointer on a function that calls
vfork/setjmp, etc. due to the need to flush all of the registers to stack. */
return gpr_or_int12_operand (op, mode);
})
 
;; Return true if operand is a memory reference suitable for a
;; sibcall.
 
(define_predicate "sibcall_operand"
(match_code "reg,subreg,const_int,const")
{
if (GET_MODE (op) != mode && mode != VOIDmode && GET_CODE (op) != CONST_INT)
return FALSE;
 
/* Note this doesn't allow reg+reg or reg+imm12 addressing (which should
never occur anyway), but prevents reload from not handling the case
properly of a call through a pointer on a function that calls
vfork/setjmp, etc. due to the need to flush all of the registers to stack. */
return gpr_or_int12_operand (op, mode);
})
 
;; Return 1 if operand is an integer constant with the bottom 16 bits
;; clear.
 
(define_predicate "upper_int16_operand"
(match_code "const_int")
{
if (GET_CODE (op) != CONST_INT)
return FALSE;
 
return ((INTVAL (op) & 0xffff) == 0);
})
 
;; Return 1 if operand is a 16 bit unsigned immediate.
 
(define_predicate "uint16_operand"
(match_code "const_int")
{
if (GET_CODE (op) != CONST_INT)
return FALSE;
 
return IN_RANGE_P (INTVAL (op), 0, 0xffff);
})
 
;; Returns 1 if OP is either a SYMBOL_REF or a constant.
 
(define_predicate "symbolic_operand"
(match_code "symbol_ref,const_int")
{
enum rtx_code c = GET_CODE (op);
 
if (c == CONST)
{
/* Allow (const:SI (plus:SI (symbol_ref) (const_int))). */
return GET_MODE (op) == SImode
&& GET_CODE (XEXP (op, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF
&& GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT;
}
 
return c == SYMBOL_REF || c == CONST_INT;
})
 
;; Return true if operator is a kind of relational operator.
 
(define_predicate "relational_operator"
(match_code "eq,ne,le,lt,ge,gt,leu,ltu,geu,gtu")
{
return (integer_relational_operator (op, mode)
|| float_relational_operator (op, mode));
})
 
;; Return true if OP is a relational operator suitable for CCmode,
;; CC_UNSmode or CC_NZmode.
 
(define_predicate "integer_relational_operator"
(match_code "eq,ne,le,lt,ge,gt,leu,ltu,geu,gtu")
{
if (mode != VOIDmode && mode != GET_MODE (op))
return FALSE;
 
/* The allowable relations depend on the mode of the ICC register. */
switch (GET_CODE (op))
{
default:
return FALSE;
 
case EQ:
case NE:
case LT:
case GE:
return (GET_MODE (XEXP (op, 0)) == CC_NZmode
|| GET_MODE (XEXP (op, 0)) == CCmode);
 
case LE:
case GT:
return GET_MODE (XEXP (op, 0)) == CCmode;
 
case GTU:
case GEU:
case LTU:
case LEU:
return (GET_MODE (XEXP (op, 0)) == CC_NZmode
|| GET_MODE (XEXP (op, 0)) == CC_UNSmode);
}
})
 
;; Return true if operator is a floating point relational operator.
 
(define_predicate "float_relational_operator"
(match_code "eq,ne,le,lt,ge,gt")
{
if (mode != VOIDmode && mode != GET_MODE (op))
return FALSE;
 
switch (GET_CODE (op))
{
default:
return FALSE;
 
case EQ: case NE:
case LE: case LT:
case GE: case GT:
#if 0
case UEQ: case UNE:
case ULE: case ULT:
case UGE: case UGT:
case ORDERED:
case UNORDERED:
#endif
return GET_MODE (XEXP (op, 0)) == CC_FPmode;
}
})
 
;; Return true if operator is EQ/NE of a conditional execution
;; register.
 
(define_predicate "ccr_eqne_operator"
(match_code "eq,ne")
{
enum machine_mode op_mode = GET_MODE (op);
rtx op0;
rtx op1;
int regno;
 
if (mode != VOIDmode && op_mode != mode)
return FALSE;
 
switch (GET_CODE (op))
{
default:
return FALSE;
 
case EQ:
case NE:
break;
}
 
op1 = XEXP (op, 1);
if (op1 != const0_rtx)
return FALSE;
 
op0 = XEXP (op, 0);
if (GET_CODE (op0) != REG)
return FALSE;
 
regno = REGNO (op0);
if (op_mode == CC_CCRmode && CR_OR_PSEUDO_P (regno))
return TRUE;
 
return FALSE;
})
 
;; Return true if operator is a minimum or maximum operator (both
;; signed and unsigned).
 
(define_predicate "minmax_operator"
(match_code "smin,smax,umin,umax")
{
if (mode != VOIDmode && mode != GET_MODE (op))
return FALSE;
 
switch (GET_CODE (op))
{
default:
return FALSE;
 
case SMIN:
case SMAX:
case UMIN:
case UMAX:
break;
}
 
if (! integer_register_operand (XEXP (op, 0), mode))
return FALSE;
 
if (! gpr_or_int10_operand (XEXP (op, 1), mode))
return FALSE;
 
return TRUE;
})
 
;; Return true if operator is an integer binary operator that can
;; executed conditionally and takes 1 cycle.
 
(define_predicate "condexec_si_binary_operator"
(match_code "plus,minus,and,ior,xor,ashift,ashiftrt,lshiftrt")
{
enum machine_mode op_mode = GET_MODE (op);
 
if (mode != VOIDmode && op_mode != mode)
return FALSE;
 
switch (GET_CODE (op))
{
default:
return FALSE;
 
case PLUS:
case MINUS:
case AND:
case IOR:
case XOR:
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
return TRUE;
}
})
 
;; Return true if operator is an integer binary operator that can be
;; executed conditionally by a media instruction.
 
(define_predicate "condexec_si_media_operator"
(match_code "and,ior,xor")
{
enum machine_mode op_mode = GET_MODE (op);
 
if (mode != VOIDmode && op_mode != mode)
return FALSE;
 
switch (GET_CODE (op))
{
default:
return FALSE;
 
case AND:
case IOR:
case XOR:
return TRUE;
}
})
 
;; Return true if operator is an integer division operator that can
;; executed conditionally.
 
(define_predicate "condexec_si_divide_operator"
(match_code "div,udiv")
{
enum machine_mode op_mode = GET_MODE (op);
 
if (mode != VOIDmode && op_mode != mode)
return FALSE;
 
switch (GET_CODE (op))
{
default:
return FALSE;
 
case DIV:
case UDIV:
return TRUE;
}
})
 
;; Return true if operator is an integer unary operator that can
;; executed conditionally.
 
(define_predicate "condexec_si_unary_operator"
(match_code "not,neg")
{
enum machine_mode op_mode = GET_MODE (op);
 
if (mode != VOIDmode && op_mode != mode)
return FALSE;
 
switch (GET_CODE (op))
{
default:
return FALSE;
 
case NEG:
case NOT:
return TRUE;
}
})
 
;; Return true if operator is an addition or subtraction
;; expression. Such expressions can be evaluated conditionally by
;; floating-point instructions.
 
(define_predicate "condexec_sf_add_operator"
(match_code "plus,minus")
{
enum machine_mode op_mode = GET_MODE (op);
 
if (mode != VOIDmode && op_mode != mode)
return FALSE;
 
switch (GET_CODE (op))
{
default:
return FALSE;
 
case PLUS:
case MINUS:
return TRUE;
}
})
 
;; Return true if operator is a conversion-type expression that can be
;; evaluated conditionally by floating-point instructions.
 
(define_predicate "condexec_sf_conv_operator"
(match_code "abs,neg")
{
enum machine_mode op_mode = GET_MODE (op);
 
if (mode != VOIDmode && op_mode != mode)
return FALSE;
 
switch (GET_CODE (op))
{
default:
return FALSE;
 
case NEG:
case ABS:
return TRUE;
}
})
 
;; Return true if OP is an integer binary operator that can be
;; combined with a (set ... (compare:CC_NZ ...)) pattern.
 
(define_predicate "intop_compare_operator"
(match_code "plus,minus,and,ior,xor,ashift,ashiftrt,lshiftrt")
{
if (mode != VOIDmode && GET_MODE (op) != mode)
return FALSE;
 
switch (GET_CODE (op))
{
default:
return FALSE;
 
case PLUS:
case MINUS:
case AND:
case IOR:
case XOR:
case ASHIFTRT:
case LSHIFTRT:
return GET_MODE (op) == SImode;
}
})
 
;; Return 1 if operand is a register or 6 bit signed immediate.
 
(define_predicate "fpr_or_int6_operand"
(match_code "reg,subreg,const_int")
{
if (GET_CODE (op) == CONST_INT)
return IN_RANGE_P (INTVAL (op), -32, 31);
 
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
 
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
 
op = SUBREG_REG (op);
}
 
if (GET_CODE (op) != REG)
return FALSE;
 
return FPR_OR_PSEUDO_P (REGNO (op));
})
 
;; Return 1 if operand is a 6 bit signed immediate.
 
(define_predicate "int6_operand"
(match_code "const_int")
{
if (GET_CODE (op) != CONST_INT)
return FALSE;
 
return IN_RANGE_P (INTVAL (op), -32, 31);
})
 
;; Return 1 if operand is a 5 bit signed immediate.
 
(define_predicate "int5_operand"
(match_code "const_int")
{
return GET_CODE (op) == CONST_INT && IN_RANGE_P (INTVAL (op), -16, 15);
})
 
;; Return 1 if operand is a 5 bit unsigned immediate.
 
(define_predicate "uint5_operand"
(match_code "const_int")
{
return GET_CODE (op) == CONST_INT && IN_RANGE_P (INTVAL (op), 0, 31);
})
 
;; Return 1 if operand is a 4 bit unsigned immediate.
 
(define_predicate "uint4_operand"
(match_code "const_int")
{
return GET_CODE (op) == CONST_INT && IN_RANGE_P (INTVAL (op), 0, 15);
})
 
;; Return 1 if operand is a 1 bit unsigned immediate (0 or 1).
 
(define_predicate "uint1_operand"
(match_code "const_int")
{
return GET_CODE (op) == CONST_INT && IN_RANGE_P (INTVAL (op), 0, 1);
})
 
;; Return 1 if operand is a valid ACC register number.
 
(define_predicate "acc_operand"
(match_code "reg,subreg")
{
return ((mode == VOIDmode || mode == GET_MODE (op))
&& REG_P (op) && ACC_P (REGNO (op))
&& ((REGNO (op) - ACC_FIRST) & ~ACC_MASK) == 0);
})
 
;; Return 1 if operand is a valid even ACC register number.
 
(define_predicate "even_acc_operand"
(match_code "reg,subreg")
{
return acc_operand (op, mode) && ((REGNO (op) - ACC_FIRST) & 1) == 0;
})
 
;; Return 1 if operand is zero or four.
 
(define_predicate "quad_acc_operand"
(match_code "reg,subreg")
{
return acc_operand (op, mode) && ((REGNO (op) - ACC_FIRST) & 3) == 0;
})
 
;; Return 1 if operand is a valid ACCG register number.
 
(define_predicate "accg_operand"
(match_code "reg,subreg")
{
return ((mode == VOIDmode || mode == GET_MODE (op))
&& REG_P (op) && ACCG_P (REGNO (op))
&& ((REGNO (op) - ACCG_FIRST) & ~ACC_MASK) == 0);
})
/frv.h
0,0 → 1,2971
/* Target macros for the FRV port of GCC.
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007
Free Software Foundation, Inc.
Contributed by Red Hat Inc.
 
This file is part of GCC.
 
GCC 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 Software Foundation; either version 3, or (at your
option) any later version.
 
GCC is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
#ifndef __FRV_H__
#define __FRV_H__
 
/* Frv general purpose macros. */
/* Align an address. */
#define ADDR_ALIGN(addr,align) (((addr) + (align) - 1) & ~((align) - 1))
 
/* Return true if a value is inside a range. */
#define IN_RANGE_P(VALUE, LOW, HIGH) \
( (((HOST_WIDE_INT)(VALUE)) >= (HOST_WIDE_INT)(LOW)) \
&& (((HOST_WIDE_INT)(VALUE)) <= ((HOST_WIDE_INT)(HIGH))))
 
/* Driver configuration. */
 
/* A C expression which determines whether the option `-CHAR' takes arguments.
The value should be the number of arguments that option takes-zero, for many
options.
 
By default, this macro is defined to handle the standard options properly.
You need not define it unless you wish to add additional options which take
arguments.
 
Defined in svr4.h. */
#undef SWITCH_TAKES_ARG
#define SWITCH_TAKES_ARG(CHAR) \
(DEFAULT_SWITCH_TAKES_ARG (CHAR) || (CHAR) == 'G')
 
/* A C expression which determines whether the option `-NAME' takes arguments.
The value should be the number of arguments that option takes-zero, for many
options. This macro rather than `SWITCH_TAKES_ARG' is used for
multi-character option names.
 
By default, this macro is defined as `DEFAULT_WORD_SWITCH_TAKES_ARG', which
handles the standard options properly. You need not define
`WORD_SWITCH_TAKES_ARG' unless you wish to add additional options which take
arguments. Any redefinition should call `DEFAULT_WORD_SWITCH_TAKES_ARG' and
then check for additional options.
 
Defined in svr4.h. */
#undef WORD_SWITCH_TAKES_ARG
 
/* -fpic and -fPIC used to imply the -mlibrary-pic multilib, but with
FDPIC which multilib to use depends on whether FDPIC is in use or
not. The trick we use is to introduce -multilib-library-pic as a
pseudo-flag that selects the library-pic multilib, and map fpic
and fPIC to it only if fdpic is not selected. Also, if fdpic is
selected and no PIC/PIE options are present, we imply -fPIE.
Otherwise, if -fpic or -fPIC are enabled and we're optimizing for
speed, or if we have -On with n>=3, enable inlining of PLTs. As
for -mgprel-ro, we want to enable it by default, but not for -fpic or
-fpie. */
 
#define DRIVER_SELF_SPECS SUBTARGET_DRIVER_SELF_SPECS \
"%{mno-pack:\
%{!mhard-float:-msoft-float}\
%{!mmedia:-mno-media}}\
%{!mfdpic:%{fpic|fPIC: -multilib-library-pic}}\
%{mfdpic:%{!fpic:%{!fpie:%{!fPIC:%{!fPIE:\
%{!fno-pic:%{!fno-pie:%{!fno-PIC:%{!fno-PIE:-fPIE}}}}}}}} \
%{!mno-inline-plt:%{O*:%{!O0:%{!Os:%{fpic|fPIC:-minline-plt} \
%{!fpic:%{!fPIC:%{!O:%{!O1:%{!O2:-minline-plt}}}}}}}}} \
%{!mno-gprel-ro:%{!fpic:%{!fpie:-mgprel-ro}}}} \
"
#ifndef SUBTARGET_DRIVER_SELF_SPECS
# define SUBTARGET_DRIVER_SELF_SPECS
#endif
 
/* A C string constant that tells the GCC driver program options to pass to
the assembler. It can also specify how to translate options you give to GNU
CC into options for GCC to pass to the assembler. See the file `sun3.h'
for an example of this.
 
Do not define this macro if it does not need to do anything.
 
Defined in svr4.h. */
#undef ASM_SPEC
#define ASM_SPEC "\
%{G*} %{v} %{n} %{T} %{Ym,*} %{Yd,*} %{Wa,*:%*} \
%{mtomcat-stats} \
%{!mno-eflags: \
%{mcpu=*} \
%{mgpr-*} %{mfpr-*} \
%{msoft-float} %{mhard-float} \
%{mdword} %{mno-dword} \
%{mdouble} %{mno-double} \
%{mmedia} %{mno-media} \
%{mmuladd} %{mno-muladd} \
%{mpack} %{mno-pack} \
%{mno-fdpic:-mnopic} %{mfdpic} \
%{fpic|fpie: -mpic} %{fPIC|fPIE: -mPIC} %{mlibrary-pic}}"
 
/* Another C string constant used much like `LINK_SPEC'. The difference
between the two is that `STARTFILE_SPEC' is used at the very beginning of
the command given to the linker.
 
If this macro is not defined, a default is provided that loads the standard
C startup file from the usual place. See `gcc.c'.
 
Defined in svr4.h. */
#undef STARTFILE_SPEC
#define STARTFILE_SPEC "crt0%O%s frvbegin%O%s"
 
/* Another C string constant used much like `LINK_SPEC'. The difference
between the two is that `ENDFILE_SPEC' is used at the very end of the
command given to the linker.
 
Do not define this macro if it does not need to do anything.
 
Defined in svr4.h. */
#undef ENDFILE_SPEC
#define ENDFILE_SPEC "frvend%O%s"
 
 
#define MASK_DEFAULT_FRV \
(MASK_MEDIA \
| MASK_DOUBLE \
| MASK_MULADD \
| MASK_DWORD \
| MASK_PACK)
 
#define MASK_DEFAULT_FR500 \
(MASK_MEDIA | MASK_DWORD | MASK_PACK)
 
#define MASK_DEFAULT_FR550 \
(MASK_MEDIA | MASK_DWORD | MASK_PACK)
 
#define MASK_DEFAULT_FR450 \
(MASK_GPR_32 \
| MASK_FPR_32 \
| MASK_MEDIA \
| MASK_SOFT_FLOAT \
| MASK_DWORD \
| MASK_PACK)
 
#define MASK_DEFAULT_FR400 \
(MASK_GPR_32 \
| MASK_FPR_32 \
| MASK_MEDIA \
| MASK_ACC_4 \
| MASK_SOFT_FLOAT \
| MASK_DWORD \
| MASK_PACK)
 
#define MASK_DEFAULT_SIMPLE \
(MASK_GPR_32 | MASK_SOFT_FLOAT)
 
/* A C string constant that tells the GCC driver program options to pass to
`cc1'. It can also specify how to translate options you give to GCC into
options for GCC to pass to the `cc1'.
 
Do not define this macro if it does not need to do anything. */
/* For ABI compliance, we need to put bss data into the normal data section. */
#define CC1_SPEC "%{G*}"
 
/* A C string constant that tells the GCC driver program options to pass to
the linker. It can also specify how to translate options you give to GCC
into options for GCC to pass to the linker.
 
Do not define this macro if it does not need to do anything.
 
Defined in svr4.h. */
/* Override the svr4.h version with one that dispenses without the svr4
shared library options, notably -G. */
#undef LINK_SPEC
#define LINK_SPEC "\
%{h*} %{v:-V} \
%{b} \
%{mfdpic:-melf32frvfd -z text} \
%{static:-dn -Bstatic} \
%{shared:-Bdynamic} \
%{symbolic:-Bsymbolic} \
%{G*} \
%{YP,*} \
%{Qy:} %{!Qn:-Qy}"
 
/* Another C string constant used much like `LINK_SPEC'. The difference
between the two is that `LIB_SPEC' is used at the end of the command given
to the linker.
 
If this macro is not defined, a default is provided that loads the standard
C library from the usual place. See `gcc.c'.
 
Defined in svr4.h. */
 
#undef LIB_SPEC
#define LIB_SPEC "--start-group -lc -lsim --end-group"
 
#ifndef CPU_TYPE
#define CPU_TYPE FRV_CPU_FR500
#endif
 
/* Run-time target specifications */
 
#define TARGET_CPU_CPP_BUILTINS() \
do \
{ \
int issue_rate; \
\
builtin_define ("__frv__"); \
builtin_assert ("machine=frv"); \
\
issue_rate = frv_issue_rate (); \
if (issue_rate > 1) \
builtin_define_with_int_value ("__FRV_VLIW__", issue_rate); \
builtin_define_with_int_value ("__FRV_GPR__", NUM_GPRS); \
builtin_define_with_int_value ("__FRV_FPR__", NUM_FPRS); \
builtin_define_with_int_value ("__FRV_ACC__", NUM_ACCS); \
\
switch (frv_cpu_type) \
{ \
case FRV_CPU_GENERIC: \
builtin_define ("__CPU_GENERIC__"); \
break; \
case FRV_CPU_FR550: \
builtin_define ("__CPU_FR550__"); \
break; \
case FRV_CPU_FR500: \
case FRV_CPU_TOMCAT: \
builtin_define ("__CPU_FR500__"); \
break; \
case FRV_CPU_FR450: \
builtin_define ("__CPU_FR450__"); \
break; \
case FRV_CPU_FR405: \
builtin_define ("__CPU_FR405__"); \
break; \
case FRV_CPU_FR400: \
builtin_define ("__CPU_FR400__"); \
break; \
case FRV_CPU_FR300: \
case FRV_CPU_SIMPLE: \
builtin_define ("__CPU_FR300__"); \
break; \
} \
\
if (TARGET_HARD_FLOAT) \
builtin_define ("__FRV_HARD_FLOAT__"); \
if (TARGET_DWORD) \
builtin_define ("__FRV_DWORD__"); \
if (TARGET_FDPIC) \
builtin_define ("__FRV_FDPIC__"); \
if (flag_leading_underscore > 0) \
builtin_define ("__FRV_UNDERSCORE__"); \
} \
while (0)
 
#define TARGET_HAS_FPRS (TARGET_HARD_FLOAT || TARGET_MEDIA)
 
#define NUM_GPRS (TARGET_GPR_32? 32 : 64)
#define NUM_FPRS (!TARGET_HAS_FPRS? 0 : TARGET_FPR_32? 32 : 64)
#define NUM_ACCS (!TARGET_MEDIA? 0 : TARGET_ACC_4? 4 : 8)
 
/* X is a valid accumulator number if (X & ACC_MASK) == X. */
#define ACC_MASK \
(!TARGET_MEDIA ? 0 \
: TARGET_ACC_4 ? 3 \
: frv_cpu_type == FRV_CPU_FR450 ? 11 \
: 7)
 
/* Macros to identify the blend of media instructions available. Revision 1
is the one found on the FR500. Revision 2 includes the changes made for
the FR400.
 
Treat the generic processor as a revision 1 machine for now, for
compatibility with earlier releases. */
 
#define TARGET_MEDIA_REV1 \
(TARGET_MEDIA \
&& (frv_cpu_type == FRV_CPU_GENERIC \
|| frv_cpu_type == FRV_CPU_FR500))
 
#define TARGET_MEDIA_REV2 \
(TARGET_MEDIA \
&& (frv_cpu_type == FRV_CPU_FR400 \
|| frv_cpu_type == FRV_CPU_FR405 \
|| frv_cpu_type == FRV_CPU_FR450 \
|| frv_cpu_type == FRV_CPU_FR550))
 
#define TARGET_MEDIA_FR450 \
(frv_cpu_type == FRV_CPU_FR450)
 
#define TARGET_FR500_FR550_BUILTINS \
(frv_cpu_type == FRV_CPU_FR500 \
|| frv_cpu_type == FRV_CPU_FR550)
 
#define TARGET_FR405_BUILTINS \
(frv_cpu_type == FRV_CPU_FR405 \
|| frv_cpu_type == FRV_CPU_FR450)
 
#ifndef HAVE_AS_TLS
#define HAVE_AS_TLS 0
#endif
 
/* This macro is a C statement to print on `stderr' a string describing the
particular machine description choice. Every machine description should
define `TARGET_VERSION'. For example:
 
#ifdef MOTOROLA
#define TARGET_VERSION \
fprintf (stderr, " (68k, Motorola syntax)");
#else
#define TARGET_VERSION \
fprintf (stderr, " (68k, MIT syntax)");
#endif */
#define TARGET_VERSION fprintf (stderr, _(" (frv)"))
 
/* Sometimes certain combinations of command options do not make sense on a
particular target machine. You can define a macro `OVERRIDE_OPTIONS' to
take account of this. This macro, if defined, is executed once just after
all the command options have been parsed.
 
Don't use this macro to turn on various extra optimizations for `-O'. That
is what `OPTIMIZATION_OPTIONS' is for. */
 
#define OVERRIDE_OPTIONS frv_override_options ()
 
/* Some machines may desire to change what optimizations are performed for
various optimization levels. This macro, if defined, is executed once just
after the optimization level is determined and before the remainder of the
command options have been parsed. Values set in this macro are used as the
default values for the other command line options.
 
LEVEL is the optimization level specified; 2 if `-O2' is specified, 1 if
`-O' is specified, and 0 if neither is specified.
 
SIZE is nonzero if `-Os' is specified, 0 otherwise.
 
You should not use this macro to change options that are not
machine-specific. These should uniformly selected by the same optimization
level on all supported machines. Use this macro to enable machine-specific
optimizations.
 
*Do not examine `write_symbols' in this macro!* The debugging options are
*not supposed to alter the generated code. */
#define OPTIMIZATION_OPTIONS(LEVEL,SIZE) frv_optimization_options (LEVEL, SIZE)
 
 
/* Define this macro if debugging can be performed even without a frame
pointer. If this macro is defined, GCC will turn on the
`-fomit-frame-pointer' option whenever `-O' is specified. */
/* Frv needs a specific frame layout that includes the frame pointer. */
 
#define CAN_DEBUG_WITHOUT_FP
 
#define LABEL_ALIGN_AFTER_BARRIER(LABEL) (TARGET_ALIGN_LABELS ? 3 : 0)
/* Small Data Area Support. */
/* Maximum size of variables that go in .sdata/.sbss.
The -msdata=foo switch also controls how small variables are handled. */
#ifndef SDATA_DEFAULT_SIZE
#define SDATA_DEFAULT_SIZE 8
#endif
 
 
/* Storage Layout */
 
/* Define this macro to have the value 1 if the most significant bit in a byte
has the lowest number; otherwise define it to have the value zero. This
means that bit-field instructions count from the most significant bit. If
the machine has no bit-field instructions, then this must still be defined,
but it doesn't matter which value it is defined to. This macro need not be
a constant.
 
This macro does not affect the way structure fields are packed into bytes or
words; that is controlled by `BYTES_BIG_ENDIAN'. */
#define BITS_BIG_ENDIAN 1
 
/* Define this macro to have the value 1 if the most significant byte in a word
has the lowest number. This macro need not be a constant. */
#define BYTES_BIG_ENDIAN 1
 
/* Define this macro to have the value 1 if, in a multiword object, the most
significant word has the lowest number. This applies to both memory
locations and registers; GCC fundamentally assumes that the order of
words in memory is the same as the order in registers. This macro need not
be a constant. */
#define WORDS_BIG_ENDIAN 1
 
/* Number of storage units in a word; normally 4. */
#define UNITS_PER_WORD 4
 
/* A macro to update MODE and UNSIGNEDP when an object whose type is TYPE and
which has the specified mode and signedness is to be stored in a register.
This macro is only called when TYPE is a scalar type.
 
On most RISC machines, which only have operations that operate on a full
register, define this macro to set M to `word_mode' if M is an integer mode
narrower than `BITS_PER_WORD'. In most cases, only integer modes should be
widened because wider-precision floating-point operations are usually more
expensive than their narrower counterparts.
 
For most machines, the macro definition does not change UNSIGNEDP. However,
some machines, have instructions that preferentially handle either signed or
unsigned quantities of certain modes. For example, on the DEC Alpha, 32-bit
loads from memory and 32-bit add instructions sign-extend the result to 64
bits. On such machines, set UNSIGNEDP according to which kind of extension
is more efficient.
 
Do not define this macro if it would never modify MODE. */
#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \
do \
{ \
if (GET_MODE_CLASS (MODE) == MODE_INT \
&& GET_MODE_SIZE (MODE) < 4) \
(MODE) = SImode; \
} \
while (0)
 
/* Normal alignment required for function parameters on the stack, in bits.
All stack parameters receive at least this much alignment regardless of data
type. On most machines, this is the same as the size of an integer. */
#define PARM_BOUNDARY 32
 
/* Define this macro if you wish to preserve a certain alignment for the stack
pointer. The definition is a C expression for the desired alignment
(measured in bits).
 
If `PUSH_ROUNDING' is not defined, the stack will always be aligned to the
specified boundary. If `PUSH_ROUNDING' is defined and specifies a less
strict alignment than `STACK_BOUNDARY', the stack may be momentarily
unaligned while pushing arguments. */
#define STACK_BOUNDARY 64
 
/* Alignment required for a function entry point, in bits. */
#define FUNCTION_BOUNDARY 128
 
/* Biggest alignment that any data type can require on this machine,
in bits. */
#define BIGGEST_ALIGNMENT 64
 
/* @@@ A hack, needed because libobjc wants to use ADJUST_FIELD_ALIGN for
some reason. */
#ifdef IN_TARGET_LIBS
#define BIGGEST_FIELD_ALIGNMENT 64
#else
/* An expression for the alignment of a structure field FIELD if the
alignment computed in the usual way is COMPUTED. GCC uses this
value instead of the value in `BIGGEST_ALIGNMENT' or
`BIGGEST_FIELD_ALIGNMENT', if defined, for structure fields only. */
#define ADJUST_FIELD_ALIGN(FIELD, COMPUTED) \
frv_adjust_field_align (FIELD, COMPUTED)
#endif
 
/* If defined, a C expression to compute the alignment for a static variable.
TYPE is the data type, and ALIGN is the alignment that the object
would ordinarily have. The value of this macro is used instead of that
alignment to align the object.
 
If this macro is not defined, then ALIGN is used.
 
One use of this macro is to increase alignment of medium-size data to make
it all fit in fewer cache lines. Another is to cause character arrays to be
word-aligned so that `strcpy' calls that copy constants to character arrays
can be done inline. */
#define DATA_ALIGNMENT(TYPE, ALIGN) \
(TREE_CODE (TYPE) == ARRAY_TYPE \
&& TYPE_MODE (TREE_TYPE (TYPE)) == QImode \
&& (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN))
 
/* If defined, a C expression to compute the alignment given to a constant that
is being placed in memory. CONSTANT is the constant and ALIGN is the
alignment that the object would ordinarily have. The value of this macro is
used instead of that alignment to align the object.
 
If this macro is not defined, then ALIGN is used.
 
The typical use of this macro is to increase alignment for string constants
to be word aligned so that `strcpy' calls that copy constants can be done
inline. */
#define CONSTANT_ALIGNMENT(EXP, ALIGN) \
(TREE_CODE (EXP) == STRING_CST \
&& (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN))
 
/* Define this macro to be the value 1 if instructions will fail to work if
given data not on the nominal alignment. If instructions will merely go
slower in that case, define this macro as 0. */
#define STRICT_ALIGNMENT 1
 
/* Define this if you wish to imitate the way many other C compilers handle
alignment of bitfields and the structures that contain them.
 
The behavior is that the type written for a bit-field (`int', `short', or
other integer type) imposes an alignment for the entire structure, as if the
structure really did contain an ordinary field of that type. In addition,
the bit-field is placed within the structure so that it would fit within such
a field, not crossing a boundary for it.
 
Thus, on most machines, a bit-field whose type is written as `int' would not
cross a four-byte boundary, and would force four-byte alignment for the
whole structure. (The alignment used may not be four bytes; it is
controlled by the other alignment parameters.)
 
If the macro is defined, its definition should be a C expression; a nonzero
value for the expression enables this behavior.
 
Note that if this macro is not defined, or its value is zero, some bitfields
may cross more than one alignment boundary. The compiler can support such
references if there are `insv', `extv', and `extzv' insns that can directly
reference memory.
 
The other known way of making bitfields work is to define
`STRUCTURE_SIZE_BOUNDARY' as large as `BIGGEST_ALIGNMENT'. Then every
structure can be accessed with fullwords.
 
Unless the machine has bit-field instructions or you define
`STRUCTURE_SIZE_BOUNDARY' that way, you must define
`PCC_BITFIELD_TYPE_MATTERS' to have a nonzero value.
 
If your aim is to make GCC use the same conventions for laying out
bitfields as are used by another compiler, here is how to investigate what
the other compiler does. Compile and run this program:
 
struct foo1
{
char x;
char :0;
char y;
};
 
struct foo2
{
char x;
int :0;
char y;
};
 
main ()
{
printf ("Size of foo1 is %d\n",
sizeof (struct foo1));
printf ("Size of foo2 is %d\n",
sizeof (struct foo2));
exit (0);
}
 
If this prints 2 and 5, then the compiler's behavior is what you would get
from `PCC_BITFIELD_TYPE_MATTERS'.
 
Defined in svr4.h. */
#define PCC_BITFIELD_TYPE_MATTERS 1
 
/* Layout of Source Language Data Types. */
 
#define CHAR_TYPE_SIZE 8
#define SHORT_TYPE_SIZE 16
#define INT_TYPE_SIZE 32
#define LONG_TYPE_SIZE 32
#define LONG_LONG_TYPE_SIZE 64
#define FLOAT_TYPE_SIZE 32
#define DOUBLE_TYPE_SIZE 64
#define LONG_DOUBLE_TYPE_SIZE 64
 
/* An expression whose value is 1 or 0, according to whether the type `char'
should be signed or unsigned by default. The user can always override this
default with the options `-fsigned-char' and `-funsigned-char'. */
#define DEFAULT_SIGNED_CHAR 1
 
/* General purpose registers. */
#define GPR_FIRST 0 /* First gpr */
#define GPR_LAST (GPR_FIRST + 63) /* Last gpr */
#define GPR_R0 GPR_FIRST /* R0, constant 0 */
#define GPR_FP (GPR_FIRST + 2) /* Frame pointer */
#define GPR_SP (GPR_FIRST + 1) /* Stack pointer */
/* small data register */
#define SDA_BASE_REG ((unsigned)(TARGET_FDPIC ? -1 : flag_pic ? PIC_REGNO : (GPR_FIRST + 16)))
#define PIC_REGNO (GPR_FIRST + (TARGET_FDPIC?15:17)) /* PIC register. */
#define FDPIC_FPTR_REGNO (GPR_FIRST + 14) /* uClinux PIC function pointer register. */
#define FDPIC_REGNO (GPR_FIRST + 15) /* uClinux PIC register. */
 
#define OUR_FDPIC_REG get_hard_reg_initial_val (SImode, FDPIC_REGNO)
 
#define FPR_FIRST 64 /* First FP reg */
#define FPR_LAST 127 /* Last FP reg */
 
#define GPR_TEMP_NUM frv_condexec_temps /* # gprs to reserve for temps */
 
/* We reserve the last CR and CCR in each category to be used as a reload
register to reload the CR/CCR registers. This is a kludge. */
#define CC_FIRST 128 /* First ICC/FCC reg */
#define CC_LAST 135 /* Last ICC/FCC reg */
#define ICC_FIRST (CC_FIRST + 4) /* First ICC reg */
#define ICC_LAST (CC_FIRST + 7) /* Last ICC reg */
#define ICC_TEMP (CC_FIRST + 7) /* Temporary ICC reg */
#define FCC_FIRST (CC_FIRST) /* First FCC reg */
#define FCC_LAST (CC_FIRST + 3) /* Last FCC reg */
 
/* Amount to shift a value to locate a ICC or FCC register in the CCR
register and shift it to the bottom 4 bits. */
#define CC_SHIFT_RIGHT(REGNO) (((REGNO) - CC_FIRST) << 2)
 
/* Mask to isolate a single ICC/FCC value. */
#define CC_MASK 0xf
 
/* Masks to isolate the various bits in an ICC field. */
#define ICC_MASK_N 0x8 /* negative */
#define ICC_MASK_Z 0x4 /* zero */
#define ICC_MASK_V 0x2 /* overflow */
#define ICC_MASK_C 0x1 /* carry */
 
/* Mask to isolate the N/Z flags in an ICC. */
#define ICC_MASK_NZ (ICC_MASK_N | ICC_MASK_Z)
 
/* Mask to isolate the Z/C flags in an ICC. */
#define ICC_MASK_ZC (ICC_MASK_Z | ICC_MASK_C)
 
/* Masks to isolate the various bits in a FCC field. */
#define FCC_MASK_E 0x8 /* equal */
#define FCC_MASK_L 0x4 /* less than */
#define FCC_MASK_G 0x2 /* greater than */
#define FCC_MASK_U 0x1 /* unordered */
 
/* For CCR registers, the machine wants CR4..CR7 to be used for integer
code and CR0..CR3 to be used for floating point. */
#define CR_FIRST 136 /* First CCR */
#define CR_LAST 143 /* Last CCR */
#define CR_NUM (CR_LAST-CR_FIRST+1) /* # of CCRs (8) */
#define ICR_FIRST (CR_FIRST + 4) /* First integer CCR */
#define ICR_LAST (CR_FIRST + 7) /* Last integer CCR */
#define ICR_TEMP ICR_LAST /* Temp integer CCR */
#define FCR_FIRST (CR_FIRST + 0) /* First float CCR */
#define FCR_LAST (CR_FIRST + 3) /* Last float CCR */
 
/* Amount to shift a value to locate a CR register in the CCCR special purpose
register and shift it to the bottom 2 bits. */
#define CR_SHIFT_RIGHT(REGNO) (((REGNO) - CR_FIRST) << 1)
 
/* Mask to isolate a single CR value. */
#define CR_MASK 0x3
 
#define ACC_FIRST 144 /* First acc register */
#define ACC_LAST 155 /* Last acc register */
 
#define ACCG_FIRST 156 /* First accg register */
#define ACCG_LAST 167 /* Last accg register */
 
#define AP_FIRST 168 /* fake argument pointer */
 
#define SPR_FIRST 169
#define SPR_LAST 172
#define LR_REGNO (SPR_FIRST)
#define LCR_REGNO (SPR_FIRST + 1)
#define IACC_FIRST (SPR_FIRST + 2)
#define IACC_LAST (SPR_FIRST + 3)
 
#define GPR_P(R) IN_RANGE_P (R, GPR_FIRST, GPR_LAST)
#define GPR_OR_AP_P(R) (GPR_P (R) || (R) == ARG_POINTER_REGNUM)
#define FPR_P(R) IN_RANGE_P (R, FPR_FIRST, FPR_LAST)
#define CC_P(R) IN_RANGE_P (R, CC_FIRST, CC_LAST)
#define ICC_P(R) IN_RANGE_P (R, ICC_FIRST, ICC_LAST)
#define FCC_P(R) IN_RANGE_P (R, FCC_FIRST, FCC_LAST)
#define CR_P(R) IN_RANGE_P (R, CR_FIRST, CR_LAST)
#define ICR_P(R) IN_RANGE_P (R, ICR_FIRST, ICR_LAST)
#define FCR_P(R) IN_RANGE_P (R, FCR_FIRST, FCR_LAST)
#define ACC_P(R) IN_RANGE_P (R, ACC_FIRST, ACC_LAST)
#define ACCG_P(R) IN_RANGE_P (R, ACCG_FIRST, ACCG_LAST)
#define SPR_P(R) IN_RANGE_P (R, SPR_FIRST, SPR_LAST)
 
#define GPR_OR_PSEUDO_P(R) (GPR_P (R) || (R) >= FIRST_PSEUDO_REGISTER)
#define FPR_OR_PSEUDO_P(R) (FPR_P (R) || (R) >= FIRST_PSEUDO_REGISTER)
#define GPR_AP_OR_PSEUDO_P(R) (GPR_OR_AP_P (R) || (R) >= FIRST_PSEUDO_REGISTER)
#define CC_OR_PSEUDO_P(R) (CC_P (R) || (R) >= FIRST_PSEUDO_REGISTER)
#define ICC_OR_PSEUDO_P(R) (ICC_P (R) || (R) >= FIRST_PSEUDO_REGISTER)
#define FCC_OR_PSEUDO_P(R) (FCC_P (R) || (R) >= FIRST_PSEUDO_REGISTER)
#define CR_OR_PSEUDO_P(R) (CR_P (R) || (R) >= FIRST_PSEUDO_REGISTER)
#define ICR_OR_PSEUDO_P(R) (ICR_P (R) || (R) >= FIRST_PSEUDO_REGISTER)
#define FCR_OR_PSEUDO_P(R) (FCR_P (R) || (R) >= FIRST_PSEUDO_REGISTER)
#define ACC_OR_PSEUDO_P(R) (ACC_P (R) || (R) >= FIRST_PSEUDO_REGISTER)
#define ACCG_OR_PSEUDO_P(R) (ACCG_P (R) || (R) >= FIRST_PSEUDO_REGISTER)
 
#define MAX_STACK_IMMEDIATE_OFFSET 2047
 
/* Register Basics. */
 
/* Number of hardware registers known to the compiler. They receive numbers 0
through `FIRST_PSEUDO_REGISTER-1'; thus, the first pseudo register's number
really is assigned the number `FIRST_PSEUDO_REGISTER'. */
#define FIRST_PSEUDO_REGISTER (SPR_LAST + 1)
 
/* The first/last register that can contain the arguments to a function. */
#define FIRST_ARG_REGNUM (GPR_FIRST + 8)
#define LAST_ARG_REGNUM (FIRST_ARG_REGNUM + FRV_NUM_ARG_REGS - 1)
 
/* Registers used by the exception handling functions. These should be
registers that are not otherwise used by the calling sequence. */
#define FIRST_EH_REGNUM 14
#define LAST_EH_REGNUM 15
 
/* Scratch registers used in the prologue, epilogue and thunks.
OFFSET_REGNO is for loading constant addends that are too big for a
single instruction. TEMP_REGNO is used for transferring SPRs to and from
the stack, and various other activities. */
#define OFFSET_REGNO 4
#define TEMP_REGNO 5
 
/* Registers used in the prologue. OLD_SP_REGNO is the old stack pointer,
which is sometimes used to set up the frame pointer. */
#define OLD_SP_REGNO 6
 
/* Registers used in the epilogue. STACKADJ_REGNO stores the exception
handler's stack adjustment. */
#define STACKADJ_REGNO 6
 
/* Registers used in thunks. JMP_REGNO is used for loading the target
address. */
#define JUMP_REGNO 6
 
#define EH_RETURN_DATA_REGNO(N) ((N) <= (LAST_EH_REGNUM - FIRST_EH_REGNUM)? \
(N) + FIRST_EH_REGNUM : INVALID_REGNUM)
#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (SImode, STACKADJ_REGNO)
#define EH_RETURN_HANDLER_RTX RETURN_ADDR_RTX (0, frame_pointer_rtx)
 
#define EPILOGUE_USES(REGNO) ((REGNO) == LR_REGNO)
 
/* An initializer that says which registers are used for fixed purposes all
throughout the compiled code and are therefore not available for general
allocation. These would include the stack pointer, the frame pointer
(except on machines where that can be used as a general register when no
frame pointer is needed), the program counter on machines where that is
considered one of the addressable registers, and any other numbered register
with a standard use.
 
This information is expressed as a sequence of numbers, separated by commas
and surrounded by braces. The Nth number is 1 if register N is fixed, 0
otherwise.
 
The table initialized from this macro, and the table initialized by the
following one, may be overridden at run time either automatically, by the
actions of the macro `CONDITIONAL_REGISTER_USAGE', or by the user with the
command options `-ffixed-REG', `-fcall-used-REG' and `-fcall-saved-REG'. */
 
/* gr0 -- Hard Zero
gr1 -- Stack Pointer
gr2 -- Frame Pointer
gr3 -- Hidden Parameter
gr16 -- Small Data reserved
gr17 -- Pic reserved
gr28 -- OS reserved
gr29 -- OS reserved
gr30 -- OS reserved
gr31 -- OS reserved
cr3 -- reserved to reload FCC registers.
cr7 -- reserved to reload ICC registers. */
#define FIXED_REGISTERS \
{ /* Integer Registers */ \
1, 1, 1, 1, 0, 0, 0, 0, /* 000-007, gr0 - gr7 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 008-015, gr8 - gr15 */ \
1, 1, 0, 0, 0, 0, 0, 0, /* 016-023, gr16 - gr23 */ \
0, 0, 0, 0, 1, 1, 1, 1, /* 024-031, gr24 - gr31 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 032-039, gr32 - gr39 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 040-040, gr48 - gr47 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 048-055, gr48 - gr55 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 056-063, gr56 - gr63 */ \
/* Float Registers */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 064-071, fr0 - fr7 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 072-079, fr8 - fr15 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 080-087, fr16 - fr23 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 088-095, fr24 - fr31 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 096-103, fr32 - fr39 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 104-111, fr48 - fr47 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 112-119, fr48 - fr55 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 120-127, fr56 - fr63 */ \
/* Condition Code Registers */ \
0, 0, 0, 0, /* 128-131, fcc0 - fcc3 */ \
0, 0, 0, 1, /* 132-135, icc0 - icc3 */ \
/* Conditional execution Registers (CCR) */ \
0, 0, 0, 0, 0, 0, 0, 1, /* 136-143, cr0 - cr7 */ \
/* Accumulators */ \
1, 1, 1, 1, 1, 1, 1, 1, /* 144-151, acc0 - acc7 */ \
1, 1, 1, 1, /* 152-155, acc8 - acc11 */ \
1, 1, 1, 1, 1, 1, 1, 1, /* 156-163, accg0 - accg7 */ \
1, 1, 1, 1, /* 164-167, accg8 - accg11 */ \
/* Other registers */ \
1, /* 168, AP - fake arg ptr */ \
0, /* 169, LR - Link register*/ \
0, /* 170, LCR - Loop count reg*/ \
1, 1 /* 171-172, iacc0 */ \
}
 
/* Like `FIXED_REGISTERS' but has 1 for each register that is clobbered (in
general) by function calls as well as for fixed registers. This macro
therefore identifies the registers that are not available for general
allocation of values that must live across function calls.
 
If a register has 0 in `CALL_USED_REGISTERS', the compiler automatically
saves it on function entry and restores it on function exit, if the register
is used within the function. */
#define CALL_USED_REGISTERS \
{ /* Integer Registers */ \
1, 1, 1, 1, 1, 1, 1, 1, /* 000-007, gr0 - gr7 */ \
1, 1, 1, 1, 1, 1, 1, 1, /* 008-015, gr8 - gr15 */ \
1, 1, 0, 0, 0, 0, 0, 0, /* 016-023, gr16 - gr23 */ \
0, 0, 0, 0, 1, 1, 1, 1, /* 024-031, gr24 - gr31 */ \
1, 1, 1, 1, 1, 1, 1, 1, /* 032-039, gr32 - gr39 */ \
1, 1, 1, 1, 1, 1, 1, 1, /* 040-040, gr48 - gr47 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 048-055, gr48 - gr55 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 056-063, gr56 - gr63 */ \
/* Float Registers */ \
1, 1, 1, 1, 1, 1, 1, 1, /* 064-071, fr0 - fr7 */ \
1, 1, 1, 1, 1, 1, 1, 1, /* 072-079, fr8 - fr15 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 080-087, fr16 - fr23 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 088-095, fr24 - fr31 */ \
1, 1, 1, 1, 1, 1, 1, 1, /* 096-103, fr32 - fr39 */ \
1, 1, 1, 1, 1, 1, 1, 1, /* 104-111, fr48 - fr47 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 112-119, fr48 - fr55 */ \
0, 0, 0, 0, 0, 0, 0, 0, /* 120-127, fr56 - fr63 */ \
/* Condition Code Registers */ \
1, 1, 1, 1, /* 128-131, fcc0 - fcc3 */ \
1, 1, 1, 1, /* 132-135, icc0 - icc3 */ \
/* Conditional execution Registers (CCR) */ \
1, 1, 1, 1, 1, 1, 1, 1, /* 136-143, cr0 - cr7 */ \
/* Accumulators */ \
1, 1, 1, 1, 1, 1, 1, 1, /* 144-151, acc0 - acc7 */ \
1, 1, 1, 1, /* 152-155, acc8 - acc11 */ \
1, 1, 1, 1, 1, 1, 1, 1, /* 156-163, accg0 - accg7 */ \
1, 1, 1, 1, /* 164-167, accg8 - accg11 */ \
/* Other registers */ \
1, /* 168, AP - fake arg ptr */ \
1, /* 169, LR - Link register*/ \
1, /* 170, LCR - Loop count reg */ \
1, 1 /* 171-172, iacc0 */ \
}
 
/* Zero or more C statements that may conditionally modify two variables
`fixed_regs' and `call_used_regs' (both of type `char []') after they have
been initialized from the two preceding macros.
 
This is necessary in case the fixed or call-clobbered registers depend on
target flags.
 
You need not define this macro if it has no work to do.
 
If the usage of an entire class of registers depends on the target flags,
you may indicate this to GCC by using this macro to modify `fixed_regs' and
`call_used_regs' to 1 for each of the registers in the classes which should
not be used by GCC. Also define the macro `REG_CLASS_FROM_LETTER' to return
`NO_REGS' if it is called with a letter for a class that shouldn't be used.
 
(However, if this class is not included in `GENERAL_REGS' and all of the
insn patterns whose constraints permit this class are controlled by target
switches, then GCC will automatically avoid using these registers when the
target switches are opposed to them.) */
 
#define CONDITIONAL_REGISTER_USAGE frv_conditional_register_usage ()
 
/* Order of allocation of registers. */
 
/* If defined, an initializer for a vector of integers, containing the numbers
of hard registers in the order in which GCC should prefer to use them
(from most preferred to least).
 
If this macro is not defined, registers are used lowest numbered first (all
else being equal).
 
One use of this macro is on machines where the highest numbered registers
must always be saved and the save-multiple-registers instruction supports
only sequences of consecutive registers. On such machines, define
`REG_ALLOC_ORDER' to be an initializer that lists the highest numbered
allocatable register first. */
 
/* On the FRV, allocate GR16 and GR17 after other saved registers so that we
have a better chance of allocating 2 registers at a time and can use the
double word load/store instructions in the prologue. */
#define REG_ALLOC_ORDER \
{ \
/* volatile registers */ \
GPR_FIRST + 4, GPR_FIRST + 5, GPR_FIRST + 6, GPR_FIRST + 7, \
GPR_FIRST + 8, GPR_FIRST + 9, GPR_FIRST + 10, GPR_FIRST + 11, \
GPR_FIRST + 12, GPR_FIRST + 13, GPR_FIRST + 14, GPR_FIRST + 15, \
GPR_FIRST + 32, GPR_FIRST + 33, GPR_FIRST + 34, GPR_FIRST + 35, \
GPR_FIRST + 36, GPR_FIRST + 37, GPR_FIRST + 38, GPR_FIRST + 39, \
GPR_FIRST + 40, GPR_FIRST + 41, GPR_FIRST + 42, GPR_FIRST + 43, \
GPR_FIRST + 44, GPR_FIRST + 45, GPR_FIRST + 46, GPR_FIRST + 47, \
\
FPR_FIRST + 0, FPR_FIRST + 1, FPR_FIRST + 2, FPR_FIRST + 3, \
FPR_FIRST + 4, FPR_FIRST + 5, FPR_FIRST + 6, FPR_FIRST + 7, \
FPR_FIRST + 8, FPR_FIRST + 9, FPR_FIRST + 10, FPR_FIRST + 11, \
FPR_FIRST + 12, FPR_FIRST + 13, FPR_FIRST + 14, FPR_FIRST + 15, \
FPR_FIRST + 32, FPR_FIRST + 33, FPR_FIRST + 34, FPR_FIRST + 35, \
FPR_FIRST + 36, FPR_FIRST + 37, FPR_FIRST + 38, FPR_FIRST + 39, \
FPR_FIRST + 40, FPR_FIRST + 41, FPR_FIRST + 42, FPR_FIRST + 43, \
FPR_FIRST + 44, FPR_FIRST + 45, FPR_FIRST + 46, FPR_FIRST + 47, \
\
ICC_FIRST + 0, ICC_FIRST + 1, ICC_FIRST + 2, ICC_FIRST + 3, \
FCC_FIRST + 0, FCC_FIRST + 1, FCC_FIRST + 2, FCC_FIRST + 3, \
CR_FIRST + 0, CR_FIRST + 1, CR_FIRST + 2, CR_FIRST + 3, \
CR_FIRST + 4, CR_FIRST + 5, CR_FIRST + 6, CR_FIRST + 7, \
\
/* saved registers */ \
GPR_FIRST + 18, GPR_FIRST + 19, \
GPR_FIRST + 20, GPR_FIRST + 21, GPR_FIRST + 22, GPR_FIRST + 23, \
GPR_FIRST + 24, GPR_FIRST + 25, GPR_FIRST + 26, GPR_FIRST + 27, \
GPR_FIRST + 48, GPR_FIRST + 49, GPR_FIRST + 50, GPR_FIRST + 51, \
GPR_FIRST + 52, GPR_FIRST + 53, GPR_FIRST + 54, GPR_FIRST + 55, \
GPR_FIRST + 56, GPR_FIRST + 57, GPR_FIRST + 58, GPR_FIRST + 59, \
GPR_FIRST + 60, GPR_FIRST + 61, GPR_FIRST + 62, GPR_FIRST + 63, \
GPR_FIRST + 16, GPR_FIRST + 17, \
\
FPR_FIRST + 16, FPR_FIRST + 17, FPR_FIRST + 18, FPR_FIRST + 19, \
FPR_FIRST + 20, FPR_FIRST + 21, FPR_FIRST + 22, FPR_FIRST + 23, \
FPR_FIRST + 24, FPR_FIRST + 25, FPR_FIRST + 26, FPR_FIRST + 27, \
FPR_FIRST + 28, FPR_FIRST + 29, FPR_FIRST + 30, FPR_FIRST + 31, \
FPR_FIRST + 48, FPR_FIRST + 49, FPR_FIRST + 50, FPR_FIRST + 51, \
FPR_FIRST + 52, FPR_FIRST + 53, FPR_FIRST + 54, FPR_FIRST + 55, \
FPR_FIRST + 56, FPR_FIRST + 57, FPR_FIRST + 58, FPR_FIRST + 59, \
FPR_FIRST + 60, FPR_FIRST + 61, FPR_FIRST + 62, FPR_FIRST + 63, \
\
/* special or fixed registers */ \
GPR_FIRST + 0, GPR_FIRST + 1, GPR_FIRST + 2, GPR_FIRST + 3, \
GPR_FIRST + 28, GPR_FIRST + 29, GPR_FIRST + 30, GPR_FIRST + 31, \
ACC_FIRST + 0, ACC_FIRST + 1, ACC_FIRST + 2, ACC_FIRST + 3, \
ACC_FIRST + 4, ACC_FIRST + 5, ACC_FIRST + 6, ACC_FIRST + 7, \
ACC_FIRST + 8, ACC_FIRST + 9, ACC_FIRST + 10, ACC_FIRST + 11, \
ACCG_FIRST + 0, ACCG_FIRST + 1, ACCG_FIRST + 2, ACCG_FIRST + 3, \
ACCG_FIRST + 4, ACCG_FIRST + 5, ACCG_FIRST + 6, ACCG_FIRST + 7, \
ACCG_FIRST + 8, ACCG_FIRST + 9, ACCG_FIRST + 10, ACCG_FIRST + 11, \
AP_FIRST, LR_REGNO, LCR_REGNO, \
IACC_FIRST + 0, IACC_FIRST + 1 \
}
 
/* How Values Fit in Registers. */
 
/* A C expression for the number of consecutive hard registers, starting at
register number REGNO, required to hold a value of mode MODE.
 
On a machine where all registers are exactly one word, a suitable definition
of this macro is
 
#define HARD_REGNO_NREGS(REGNO, MODE) \
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) \
/ UNITS_PER_WORD)) */
 
/* On the FRV, make the CC modes take 3 words in the integer registers, so that
we can build the appropriate instructions to properly reload the values. */
#define HARD_REGNO_NREGS(REGNO, MODE) frv_hard_regno_nregs (REGNO, MODE)
 
/* A C expression that is nonzero if it is permissible to store a value of mode
MODE in hard register number REGNO (or in several registers starting with
that one). For a machine where all registers are equivalent, a suitable
definition is
 
#define HARD_REGNO_MODE_OK(REGNO, MODE) 1
 
It is not necessary for this macro to check for the numbers of fixed
registers, because the allocation mechanism considers them to be always
occupied.
 
On some machines, double-precision values must be kept in even/odd register
pairs. The way to implement that is to define this macro to reject odd
register numbers for such modes.
 
The minimum requirement for a mode to be OK in a register is that the
`movMODE' instruction pattern support moves between the register and any
other hard register for which the mode is OK; and that moving a value into
the register and back out not alter it.
 
Since the same instruction used to move `SImode' will work for all narrower
integer modes, it is not necessary on any machine for `HARD_REGNO_MODE_OK'
to distinguish between these modes, provided you define patterns `movhi',
etc., to take advantage of this. This is useful because of the interaction
between `HARD_REGNO_MODE_OK' and `MODES_TIEABLE_P'; it is very desirable for
all integer modes to be tieable.
 
Many machines have special registers for floating point arithmetic. Often
people assume that floating point machine modes are allowed only in floating
point registers. This is not true. Any registers that can hold integers
can safely *hold* a floating point machine mode, whether or not floating
arithmetic can be done on it in those registers. Integer move instructions
can be used to move the values.
 
On some machines, though, the converse is true: fixed-point machine modes
may not go in floating registers. This is true if the floating registers
normalize any value stored in them, because storing a non-floating value
there would garble it. In this case, `HARD_REGNO_MODE_OK' should reject
fixed-point machine modes in floating registers. But if the floating
registers do not automatically normalize, if you can store any bit pattern
in one and retrieve it unchanged without a trap, then any machine mode may
go in a floating register, so you can define this macro to say so.
 
The primary significance of special floating registers is rather that they
are the registers acceptable in floating point arithmetic instructions.
However, this is of no concern to `HARD_REGNO_MODE_OK'. You handle it by
writing the proper constraints for those instructions.
 
On some machines, the floating registers are especially slow to access, so
that it is better to store a value in a stack frame than in such a register
if floating point arithmetic is not being done. As long as the floating
registers are not in class `GENERAL_REGS', they will not be used unless some
pattern's constraint asks for one. */
#define HARD_REGNO_MODE_OK(REGNO, MODE) frv_hard_regno_mode_ok (REGNO, MODE)
 
/* A C expression that is nonzero if it is desirable to choose register
allocation so as to avoid move instructions between a value of mode MODE1
and a value of mode MODE2.
 
If `HARD_REGNO_MODE_OK (R, MODE1)' and `HARD_REGNO_MODE_OK (R, MODE2)' are
ever different for any R, then `MODES_TIEABLE_P (MODE1, MODE2)' must be
zero. */
#define MODES_TIEABLE_P(MODE1, MODE2) (MODE1 == MODE2)
 
/* Define this macro if the compiler should avoid copies to/from CCmode
registers. You should only define this macro if support fo copying to/from
CCmode is incomplete. */
#define AVOID_CCMODE_COPIES
 
/* Register Classes. */
 
/* An enumeral type that must be defined with all the register class names as
enumeral values. `NO_REGS' must be first. `ALL_REGS' must be the last
register class, followed by one more enumeral value, `LIM_REG_CLASSES',
which is not a register class but rather tells how many classes there are.
 
Each register class has a number, which is the value of casting the class
name to type `int'. The number serves as an index in many of the tables
described below. */
enum reg_class
{
NO_REGS,
ICC_REGS,
FCC_REGS,
CC_REGS,
ICR_REGS,
FCR_REGS,
CR_REGS,
LCR_REG,
LR_REG,
GR8_REGS,
GR9_REGS,
GR89_REGS,
FDPIC_REGS,
FDPIC_FPTR_REGS,
FDPIC_CALL_REGS,
SPR_REGS,
QUAD_ACC_REGS,
EVEN_ACC_REGS,
ACC_REGS,
ACCG_REGS,
QUAD_FPR_REGS,
FEVEN_REGS,
FPR_REGS,
QUAD_REGS,
EVEN_REGS,
GPR_REGS,
ALL_REGS,
LIM_REG_CLASSES
};
 
#define GENERAL_REGS GPR_REGS
 
/* The number of distinct register classes, defined as follows:
 
#define N_REG_CLASSES (int) LIM_REG_CLASSES */
#define N_REG_CLASSES ((int) LIM_REG_CLASSES)
 
/* An initializer containing the names of the register classes as C string
constants. These names are used in writing some of the debugging dumps. */
#define REG_CLASS_NAMES { \
"NO_REGS", \
"ICC_REGS", \
"FCC_REGS", \
"CC_REGS", \
"ICR_REGS", \
"FCR_REGS", \
"CR_REGS", \
"LCR_REG", \
"LR_REG", \
"GR8_REGS", \
"GR9_REGS", \
"GR89_REGS", \
"FDPIC_REGS", \
"FDPIC_FPTR_REGS", \
"FDPIC_CALL_REGS", \
"SPR_REGS", \
"QUAD_ACC_REGS", \
"EVEN_ACC_REGS", \
"ACC_REGS", \
"ACCG_REGS", \
"QUAD_FPR_REGS", \
"FEVEN_REGS", \
"FPR_REGS", \
"QUAD_REGS", \
"EVEN_REGS", \
"GPR_REGS", \
"ALL_REGS" \
}
 
/* An initializer containing the contents of the register classes, as integers
which are bit masks. The Nth integer specifies the contents of class N.
The way the integer MASK is interpreted is that register R is in the class
if `MASK & (1 << R)' is 1.
 
When the machine has more than 32 registers, an integer does not suffice.
Then the integers are replaced by sub-initializers, braced groupings
containing several integers. Each sub-initializer must be suitable as an
initializer for the type `HARD_REG_SET' which is defined in
`hard-reg-set.h'. */
#define REG_CLASS_CONTENTS \
{ /* gr0-gr31 gr32-gr63 fr0-fr31 fr32-fr-63 cc/ccr/acc ap/spr */ \
{ 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0}, /* NO_REGS */\
{ 0x00000000,0x00000000,0x00000000,0x00000000,0x000000f0,0x0}, /* ICC_REGS */\
{ 0x00000000,0x00000000,0x00000000,0x00000000,0x0000000f,0x0}, /* FCC_REGS */\
{ 0x00000000,0x00000000,0x00000000,0x00000000,0x000000ff,0x0}, /* CC_REGS */\
{ 0x00000000,0x00000000,0x00000000,0x00000000,0x0000f000,0x0}, /* ICR_REGS */\
{ 0x00000000,0x00000000,0x00000000,0x00000000,0x00000f00,0x0}, /* FCR_REGS */\
{ 0x00000000,0x00000000,0x00000000,0x00000000,0x0000ff00,0x0}, /* CR_REGS */\
{ 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x400}, /* LCR_REGS */\
{ 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x200}, /* LR_REGS */\
{ 0x00000100,0x00000000,0x00000000,0x00000000,0x00000000,0x0}, /* GR8_REGS */\
{ 0x00000200,0x00000000,0x00000000,0x00000000,0x00000000,0x0}, /* GR9_REGS */\
{ 0x00000300,0x00000000,0x00000000,0x00000000,0x00000000,0x0}, /* GR89_REGS */\
{ 0x00008000,0x00000000,0x00000000,0x00000000,0x00000000,0x0}, /* FDPIC_REGS */\
{ 0x00004000,0x00000000,0x00000000,0x00000000,0x00000000,0x0}, /* FDPIC_FPTR_REGS */\
{ 0x0000c000,0x00000000,0x00000000,0x00000000,0x00000000,0x0}, /* FDPIC_CALL_REGS */\
{ 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x1e00}, /* SPR_REGS */\
{ 0x00000000,0x00000000,0x00000000,0x00000000,0x0fff0000,0x0}, /* QUAD_ACC */\
{ 0x00000000,0x00000000,0x00000000,0x00000000,0x0fff0000,0x0}, /* EVEN_ACC */\
{ 0x00000000,0x00000000,0x00000000,0x00000000,0x0fff0000,0x0}, /* ACC_REGS */\
{ 0x00000000,0x00000000,0x00000000,0x00000000,0xf0000000,0xff}, /* ACCG_REGS*/\
{ 0x00000000,0x00000000,0xffffffff,0xffffffff,0x00000000,0x0}, /* QUAD_FPR */\
{ 0x00000000,0x00000000,0xffffffff,0xffffffff,0x00000000,0x0}, /* FEVEN_REG*/\
{ 0x00000000,0x00000000,0xffffffff,0xffffffff,0x00000000,0x0}, /* FPR_REGS */\
{ 0x0ffffffc,0xffffffff,0x00000000,0x00000000,0x00000000,0x0}, /* QUAD_REGS*/\
{ 0xfffffffc,0xffffffff,0x00000000,0x00000000,0x00000000,0x0}, /* EVEN_REGS*/\
{ 0xffffffff,0xffffffff,0x00000000,0x00000000,0x00000000,0x100}, /* GPR_REGS */\
{ 0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0x1fff}, /* ALL_REGS */\
}
 
/* A C expression whose value is a register class containing hard register
REGNO. In general there is more than one such class; choose a class which
is "minimal", meaning that no smaller class also contains the register. */
 
extern enum reg_class regno_reg_class[];
#define REGNO_REG_CLASS(REGNO) regno_reg_class [REGNO]
 
/* A macro whose definition is the name of the class to which a valid base
register must belong. A base register is one used in an address which is
the register value plus a displacement. */
#define BASE_REG_CLASS GPR_REGS
 
/* A macro whose definition is the name of the class to which a valid index
register must belong. An index register is one used in an address where its
value is either multiplied by a scale factor or added to another register
(as well as added to a displacement). */
#define INDEX_REG_CLASS GPR_REGS
 
/* A C expression which defines the machine-dependent operand constraint
letters for register classes. If CHAR is such a letter, the value should be
the register class corresponding to it. Otherwise, the value should be
`NO_REGS'. The register letter `r', corresponding to class `GENERAL_REGS',
will not be passed to this macro; you do not need to handle it.
 
The following letters are unavailable, due to being used as
constraints:
'0'..'9'
'<', '>'
'E', 'F', 'G', 'H'
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'
'Q', 'R', 'S', 'T', 'U'
'V', 'X'
'g', 'i', 'm', 'n', 'o', 'p', 'r', 's' */
 
extern enum reg_class reg_class_from_letter[];
#define REG_CLASS_FROM_LETTER(CHAR) reg_class_from_letter [(unsigned char)(CHAR)]
 
/* A C expression which is nonzero if register number NUM is suitable for use
as a base register in operand addresses. It may be either a suitable hard
register or a pseudo register that has been allocated such a hard register. */
#define REGNO_OK_FOR_BASE_P(NUM) \
((NUM) < FIRST_PSEUDO_REGISTER \
? GPR_P (NUM) \
: (reg_renumber [NUM] >= 0 && GPR_P (reg_renumber [NUM])))
 
/* A C expression which is nonzero if register number NUM is suitable for use
as an index register in operand addresses. It may be either a suitable hard
register or a pseudo register that has been allocated such a hard register.
 
The difference between an index register and a base register is that the
index register may be scaled. If an address involves the sum of two
registers, neither one of them scaled, then either one may be labeled the
"base" and the other the "index"; but whichever labeling is used must fit
the machine's constraints of which registers may serve in each capacity.
The compiler will try both labelings, looking for one that is valid, and
will reload one or both registers only if neither labeling works. */
#define REGNO_OK_FOR_INDEX_P(NUM) \
((NUM) < FIRST_PSEUDO_REGISTER \
? GPR_P (NUM) \
: (reg_renumber [NUM] >= 0 && GPR_P (reg_renumber [NUM])))
 
/* A C expression that places additional restrictions on the register class to
use when it is necessary to copy value X into a register in class CLASS.
The value is a register class; perhaps CLASS, or perhaps another, smaller
class. On many machines, the following definition is safe:
 
#define PREFERRED_RELOAD_CLASS(X,CLASS) CLASS
 
Sometimes returning a more restrictive class makes better code. For
example, on the 68000, when X is an integer constant that is in range for a
`moveq' instruction, the value of this macro is always `DATA_REGS' as long
as CLASS includes the data registers. Requiring a data register guarantees
that a `moveq' will be used.
 
If X is a `const_double', by returning `NO_REGS' you can force X into a
memory constant. This is useful on certain machines where immediate
floating values cannot be loaded into certain kinds of registers.
 
This declaration must be present. */
#define PREFERRED_RELOAD_CLASS(X, CLASS) CLASS
 
#define SECONDARY_INPUT_RELOAD_CLASS(CLASS, MODE, X) \
frv_secondary_reload_class (CLASS, MODE, X, TRUE)
 
#define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, X) \
frv_secondary_reload_class (CLASS, MODE, X, FALSE)
 
/* A C expression whose value is nonzero if pseudos that have been assigned to
registers of class CLASS would likely be spilled because registers of CLASS
are needed for spill registers.
 
The default value of this macro returns 1 if CLASS has exactly one register
and zero otherwise. On most machines, this default should be used. Only
define this macro to some other expression if pseudo allocated by
`local-alloc.c' end up in memory because their hard registers were needed
for spill registers. If this macro returns nonzero for those classes, those
pseudos will only be allocated by `global.c', which knows how to reallocate
the pseudo to another register. If there would not be another register
available for reallocation, you should not change the definition of this
macro since the only effect of such a definition would be to slow down
register allocation. */
#define CLASS_LIKELY_SPILLED_P(CLASS) frv_class_likely_spilled_p (CLASS)
 
/* A C expression for the maximum number of consecutive registers of
class CLASS needed to hold a value of mode MODE.
 
This is closely related to the macro `HARD_REGNO_NREGS'. In fact, the value
of the macro `CLASS_MAX_NREGS (CLASS, MODE)' should be the maximum value of
`HARD_REGNO_NREGS (REGNO, MODE)' for all REGNO values in the class CLASS.
 
This macro helps control the handling of multiple-word values in
the reload pass.
 
This declaration is required. */
#define CLASS_MAX_NREGS(CLASS, MODE) frv_class_max_nregs (CLASS, MODE)
 
#define ZERO_P(x) (x == CONST0_RTX (GET_MODE (x)))
 
/* 6 bit signed immediate. */
#define CONST_OK_FOR_I(VALUE) IN_RANGE_P(VALUE, -32, 31)
/* 10 bit signed immediate. */
#define CONST_OK_FOR_J(VALUE) IN_RANGE_P(VALUE, -512, 511)
/* Unused */
#define CONST_OK_FOR_K(VALUE) 0
/* 16 bit signed immediate. */
#define CONST_OK_FOR_L(VALUE) IN_RANGE_P(VALUE, -32768, 32767)
/* 16 bit unsigned immediate. */
#define CONST_OK_FOR_M(VALUE) IN_RANGE_P (VALUE, 0, 65535)
/* 12 bit signed immediate that is negative. */
#define CONST_OK_FOR_N(VALUE) IN_RANGE_P(VALUE, -2048, -1)
/* Zero */
#define CONST_OK_FOR_O(VALUE) ((VALUE) == 0)
/* 12 bit signed immediate that is negative. */
#define CONST_OK_FOR_P(VALUE) IN_RANGE_P(VALUE, 1, 2047)
 
/* A C expression that defines the machine-dependent operand constraint letters
(`I', `J', `K', .. 'P') that specify particular ranges of integer values.
If C is one of those letters, the expression should check that VALUE, an
integer, is in the appropriate range and return 1 if so, 0 otherwise. If C
is not one of those letters, the value should be 0 regardless of VALUE. */
#define CONST_OK_FOR_LETTER_P(VALUE, C) \
( (C) == 'I' ? CONST_OK_FOR_I (VALUE) \
: (C) == 'J' ? CONST_OK_FOR_J (VALUE) \
: (C) == 'K' ? CONST_OK_FOR_K (VALUE) \
: (C) == 'L' ? CONST_OK_FOR_L (VALUE) \
: (C) == 'M' ? CONST_OK_FOR_M (VALUE) \
: (C) == 'N' ? CONST_OK_FOR_N (VALUE) \
: (C) == 'O' ? CONST_OK_FOR_O (VALUE) \
: (C) == 'P' ? CONST_OK_FOR_P (VALUE) \
: 0)
 
 
/* A C expression that defines the machine-dependent operand constraint letters
(`G', `H') that specify particular ranges of `const_double' values.
 
If C is one of those letters, the expression should check that VALUE, an RTX
of code `const_double', is in the appropriate range and return 1 if so, 0
otherwise. If C is not one of those letters, the value should be 0
regardless of VALUE.
 
`const_double' is used for all floating-point constants and for `DImode'
fixed-point constants. A given letter can accept either or both kinds of
values. It can use `GET_MODE' to distinguish between these kinds. */
 
#define CONST_DOUBLE_OK_FOR_G(VALUE) \
((GET_MODE (VALUE) == VOIDmode \
&& CONST_DOUBLE_LOW (VALUE) == 0 \
&& CONST_DOUBLE_HIGH (VALUE) == 0) \
|| ((GET_MODE (VALUE) == SFmode \
|| GET_MODE (VALUE) == DFmode) \
&& (VALUE) == CONST0_RTX (GET_MODE (VALUE))))
 
#define CONST_DOUBLE_OK_FOR_H(VALUE) 0
 
#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \
( (C) == 'G' ? CONST_DOUBLE_OK_FOR_G (VALUE) \
: (C) == 'H' ? CONST_DOUBLE_OK_FOR_H (VALUE) \
: 0)
 
/* A C expression that defines the optional machine-dependent constraint
letters (`Q', `R', `S', `T', `U') that can be used to segregate specific
types of operands, usually memory references, for the target machine.
Normally this macro will not be defined. If it is required for a particular
target machine, it should return 1 if VALUE corresponds to the operand type
represented by the constraint letter C. If C is not defined as an extra
constraint, the value returned should be 0 regardless of VALUE.
 
For example, on the ROMP, load instructions cannot have their output in r0
if the memory reference contains a symbolic address. Constraint letter `Q'
is defined as representing a memory address that does *not* contain a
symbolic address. An alternative is specified with a `Q' constraint on the
input and `r' on the output. The next alternative specifies `m' on the
input and a register class that does not include r0 on the output. */
 
/* 12-bit relocations. */
#define EXTRA_CONSTRAINT_FOR_Q(VALUE) \
(got12_operand (VALUE, GET_MODE (VALUE)))
 
/* Double word memory ops that take one instruction. */
#define EXTRA_CONSTRAINT_FOR_R(VALUE) \
(dbl_memory_one_insn_operand (VALUE, GET_MODE (VALUE)))
 
/* SYMBOL_REF */
#define EXTRA_CONSTRAINT_FOR_S(VALUE) \
(CONSTANT_P (VALUE) && call_operand (VALUE, VOIDmode))
 
/* Double word memory ops that take two instructions. */
#define EXTRA_CONSTRAINT_FOR_T(VALUE) \
(dbl_memory_two_insn_operand (VALUE, GET_MODE (VALUE)))
 
/* Memory operand for conditional execution. */
#define EXTRA_CONSTRAINT_FOR_U(VALUE) \
(condexec_memory_operand (VALUE, GET_MODE (VALUE)))
 
#define EXTRA_CONSTRAINT(VALUE, C) \
( (C) == 'Q' ? EXTRA_CONSTRAINT_FOR_Q (VALUE) \
: (C) == 'R' ? EXTRA_CONSTRAINT_FOR_R (VALUE) \
: (C) == 'S' ? EXTRA_CONSTRAINT_FOR_S (VALUE) \
: (C) == 'T' ? EXTRA_CONSTRAINT_FOR_T (VALUE) \
: (C) == 'U' ? EXTRA_CONSTRAINT_FOR_U (VALUE) \
: 0)
 
#define EXTRA_MEMORY_CONSTRAINT(C,STR) \
((C) == 'U' || (C) == 'R' || (C) == 'T')
 
#define CONSTRAINT_LEN(C, STR) \
((C) == 'D' ? 3 : DEFAULT_CONSTRAINT_LEN ((C), (STR)))
 
#define REG_CLASS_FROM_CONSTRAINT(C, STR) \
(((C) == 'D' && (STR)[1] == '8' && (STR)[2] == '9') ? GR89_REGS : \
((C) == 'D' && (STR)[1] == '0' && (STR)[2] == '9') ? GR9_REGS : \
((C) == 'D' && (STR)[1] == '0' && (STR)[2] == '8') ? GR8_REGS : \
((C) == 'D' && (STR)[1] == '1' && (STR)[2] == '4') ? FDPIC_FPTR_REGS : \
((C) == 'D' && (STR)[1] == '1' && (STR)[2] == '5') ? FDPIC_REGS : \
REG_CLASS_FROM_LETTER ((C)))
 
/* Basic Stack Layout. */
 
/* Structure to describe information about a saved range of registers */
 
typedef struct frv_stack_regs {
const char * name; /* name of the register ranges */
int first; /* first register in the range */
int last; /* last register in the range */
int size_1word; /* # of bytes to be stored via 1 word stores */
int size_2words; /* # of bytes to be stored via 2 word stores */
unsigned char field_p; /* true if the registers are a single SPR */
unsigned char dword_p; /* true if we can do dword stores */
unsigned char special_p; /* true if the regs have a fixed save loc. */
} frv_stack_regs_t;
 
/* Register ranges to look into saving. */
#define STACK_REGS_GPR 0 /* Gprs (normally gr16..gr31, gr48..gr63) */
#define STACK_REGS_FPR 1 /* Fprs (normally fr16..fr31, fr48..fr63) */
#define STACK_REGS_LR 2 /* LR register */
#define STACK_REGS_CC 3 /* CCrs (normally not saved) */
#define STACK_REGS_LCR 5 /* lcr register */
#define STACK_REGS_STDARG 6 /* stdarg registers */
#define STACK_REGS_STRUCT 7 /* structure return (gr3) */
#define STACK_REGS_FP 8 /* FP register */
#define STACK_REGS_MAX 9 /* # of register ranges */
 
/* Values for save_p field. */
#define REG_SAVE_NO_SAVE 0 /* register not saved */
#define REG_SAVE_1WORD 1 /* save the register */
#define REG_SAVE_2WORDS 2 /* save register and register+1 */
 
/* Structure used to define the frv stack. */
 
typedef struct frv_stack {
int total_size; /* total bytes allocated for stack */
int vars_size; /* variable save area size */
int parameter_size; /* outgoing parameter size */
int stdarg_size; /* size of regs needed to be saved for stdarg */
int regs_size; /* size of the saved registers */
int regs_size_1word; /* # of bytes to be stored via 1 word stores */
int regs_size_2words; /* # of bytes to be stored via 2 word stores */
int header_size; /* size of the old FP, struct ret., LR save */
int pretend_size; /* size of pretend args */
int vars_offset; /* offset to save local variables from new SP*/
int regs_offset; /* offset to save registers from new SP */
/* register range information */
frv_stack_regs_t regs[STACK_REGS_MAX];
/* offset to store each register */
int reg_offset[FIRST_PSEUDO_REGISTER];
/* whether to save register (& reg+1) */
unsigned char save_p[FIRST_PSEUDO_REGISTER];
} frv_stack_t;
 
/* Define this macro if pushing a word onto the stack moves the stack pointer
to a smaller address. */
#define STACK_GROWS_DOWNWARD 1
 
/* Define this macro to nonzero if the addresses of local variable slots
are at negative offsets from the frame pointer. */
#define FRAME_GROWS_DOWNWARD 1
 
/* Offset from the frame pointer to the first local variable slot to be
allocated.
 
If `FRAME_GROWS_DOWNWARD', find the next slot's offset by subtracting the
first slot's length from `STARTING_FRAME_OFFSET'. Otherwise, it is found by
adding the length of the first slot to the value `STARTING_FRAME_OFFSET'. */
#define STARTING_FRAME_OFFSET 0
 
/* Offset from the stack pointer register to the first location at which
outgoing arguments are placed. If not specified, the default value of zero
is used. This is the proper value for most machines.
 
If `ARGS_GROW_DOWNWARD', this is the offset to the location above the first
location at which outgoing arguments are placed. */
#define STACK_POINTER_OFFSET 0
 
/* Offset from the argument pointer register to the first argument's address.
On some machines it may depend on the data type of the function.
 
If `ARGS_GROW_DOWNWARD', this is the offset to the location above the first
argument's address. */
#define FIRST_PARM_OFFSET(FUNDECL) 0
 
/* A C expression whose value is RTL representing the address in a stack frame
where the pointer to the caller's frame is stored. Assume that FRAMEADDR is
an RTL expression for the address of the stack frame itself.
 
If you don't define this macro, the default is to return the value of
FRAMEADDR--that is, the stack frame address is also the address of the stack
word that points to the previous frame. */
#define DYNAMIC_CHAIN_ADDRESS(FRAMEADDR) frv_dynamic_chain_address (FRAMEADDR)
 
/* A C expression whose value is RTL representing the value of the return
address for the frame COUNT steps up from the current frame, after the
prologue. FRAMEADDR is the frame pointer of the COUNT frame, or the frame
pointer of the COUNT - 1 frame if `RETURN_ADDR_IN_PREVIOUS_FRAME' is
defined.
 
The value of the expression must always be the correct address when COUNT is
zero, but may be `NULL_RTX' if there is not way to determine the return
address of other frames. */
#define RETURN_ADDR_RTX(COUNT, FRAMEADDR) frv_return_addr_rtx (COUNT, FRAMEADDR)
 
#define RETURN_POINTER_REGNUM LR_REGNO
 
/* A C expression whose value is RTL representing the location of the incoming
return address at the beginning of any function, before the prologue. This
RTL is either a `REG', indicating that the return value is saved in `REG',
or a `MEM' representing a location in the stack.
 
You only need to define this macro if you want to support call frame
debugging information like that provided by DWARF 2. */
#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (SImode, RETURN_POINTER_REGNUM)
 
/* Register That Address the Stack Frame. */
 
/* The register number of the stack pointer register, which must also be a
fixed register according to `FIXED_REGISTERS'. On most machines, the
hardware determines which register this is. */
#define STACK_POINTER_REGNUM (GPR_FIRST + 1)
 
/* The register number of the frame pointer register, which is used to access
automatic variables in the stack frame. On some machines, the hardware
determines which register this is. On other machines, you can choose any
register you wish for this purpose. */
#define FRAME_POINTER_REGNUM (GPR_FIRST + 2)
 
/* The register number of the arg pointer register, which is used to access the
function's argument list. On some machines, this is the same as the frame
pointer register. On some machines, the hardware determines which register
this is. On other machines, you can choose any register you wish for this
purpose. If this is not the same register as the frame pointer register,
then you must mark it as a fixed register according to `FIXED_REGISTERS', or
arrange to be able to eliminate it. */
 
/* On frv this is a fake register that is eliminated in
terms of either the frame pointer or stack pointer. */
#define ARG_POINTER_REGNUM AP_FIRST
 
/* Register numbers used for passing a function's static chain pointer. If
register windows are used, the register number as seen by the called
function is `STATIC_CHAIN_INCOMING_REGNUM', while the register number as
seen by the calling function is `STATIC_CHAIN_REGNUM'. If these registers
are the same, `STATIC_CHAIN_INCOMING_REGNUM' need not be defined.
 
The static chain register need not be a fixed register.
 
If the static chain is passed in memory, these macros should not be defined;
instead, the next two macros should be defined. */
#define STATIC_CHAIN_REGNUM (GPR_FIRST + 7)
#define STATIC_CHAIN_INCOMING_REGNUM (GPR_FIRST + 7)
 
/* Eliminating the Frame Pointer and the Arg Pointer. */
 
/* A C expression which is nonzero if a function must have and use a frame
pointer. This expression is evaluated in the reload pass. If its value is
nonzero the function will have a frame pointer.
 
The expression can in principle examine the current function and decide
according to the facts, but on most machines the constant 0 or the constant
1 suffices. Use 0 when the machine allows code to be generated with no
frame pointer, and doing so saves some time or space. Use 1 when there is
no possible advantage to avoiding a frame pointer.
 
In certain cases, the compiler does not know how to produce valid code
without a frame pointer. The compiler recognizes those cases and
automatically gives the function a frame pointer regardless of what
`FRAME_POINTER_REQUIRED' says. You don't need to worry about them.
 
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. See `FIXED_REGISTERS' for more information. */
#define FRAME_POINTER_REQUIRED frv_frame_pointer_required ()
 
/* If defined, this macro specifies a table of register pairs used to eliminate
unneeded registers that point into the stack frame. If it is not defined,
the only elimination attempted by the compiler is to replace references to
the frame pointer with references to the stack pointer.
 
The definition of this macro is a list of structure initializations, each of
which specifies an original and replacement register.
 
On some machines, the position of the argument pointer is not known until
the compilation is completed. In such a case, a separate hard register must
be used for the argument pointer. This register can be eliminated by
replacing it with either the frame pointer or the argument pointer,
depending on whether or not the frame pointer has been eliminated.
 
In this case, you might specify:
#define ELIMINABLE_REGS \
{{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \
{FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}}
 
Note that the elimination of the argument pointer with the stack pointer is
specified first since that is the preferred elimination. */
 
#define ELIMINABLE_REGS \
{ \
{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \
{FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM} \
}
 
/* A C expression that returns nonzero if the compiler is allowed to try to
replace register number FROM with register number TO. This macro need only
be defined if `ELIMINABLE_REGS' is defined, and will usually be the constant
1, since most of the cases preventing register elimination are things that
the compiler already knows about. */
 
#define CAN_ELIMINATE(FROM, TO) \
((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM \
? ! frame_pointer_needed \
: 1)
 
/* This macro is similar to `INITIAL_FRAME_POINTER_OFFSET'. It specifies the
initial difference between the specified pair of registers. This macro must
be defined if `ELIMINABLE_REGS' is defined. */
 
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
(OFFSET) = frv_initial_elimination_offset (FROM, TO)
 
/* Passing Function Arguments on the Stack. */
 
/* If defined, the maximum amount of space required for outgoing arguments will
be computed and placed into the variable
`current_function_outgoing_args_size'. No space will be pushed onto the
stack for each call; instead, the function prologue should increase the
stack frame size by this amount.
 
Defining both `PUSH_ROUNDING' and `ACCUMULATE_OUTGOING_ARGS' is not
proper. */
#define ACCUMULATE_OUTGOING_ARGS 1
 
/* A C expression that should indicate the number of bytes of its own arguments
that a function pops on returning, or 0 if the function pops no arguments
and the caller must therefore pop them all after the function returns.
 
FUNDECL is a C variable whose value is a tree node that describes the
function in question. Normally it is a node of type `FUNCTION_DECL' that
describes the declaration of the function. From this it is possible to
obtain the DECL_ATTRIBUTES of the function.
 
FUNTYPE is a C variable whose value is a tree node that describes the
function in question. Normally it is a node of type `FUNCTION_TYPE' that
describes the data type of the function. From this it is possible to obtain
the data types of the value and arguments (if known).
 
When a call to a library function is being considered, FUNTYPE will contain
an identifier node for the library function. Thus, if you need to
distinguish among various library functions, you can do so by their names.
Note that "library function" in this context means a function used to
perform arithmetic, whose name is known specially in the compiler and was
not mentioned in the C code being compiled.
 
STACK-SIZE is the number of bytes of arguments passed on the stack. If a
variable number of bytes is passed, it is zero, and argument popping will
always be the responsibility of the calling function.
 
On the VAX, all functions always pop their arguments, so the definition of
this macro is STACK-SIZE. On the 68000, using the standard calling
convention, no functions pop their arguments, so the value of the macro is
always 0 in this case. But an alternative calling convention is available
in which functions that take a fixed number of arguments pop them but other
functions (such as `printf') pop nothing (the caller pops all). When this
convention is in use, FUNTYPE is examined to determine whether a function
takes a fixed number of arguments. */
#define RETURN_POPS_ARGS(FUNDECL, FUNTYPE, STACK_SIZE) 0
 
/* The number of register assigned to holding function arguments. */
 
#define FRV_NUM_ARG_REGS 6
 
#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
frv_function_arg (&CUM, MODE, TYPE, NAMED, FALSE)
 
/* Define this macro if the target machine has "register windows", so that the
register in which a function sees an arguments is not necessarily the same
as the one in which the caller passed the argument.
 
For such machines, `FUNCTION_ARG' computes the register in which the caller
passes the value, and `FUNCTION_INCOMING_ARG' should be defined in a similar
fashion to tell the function being called where the arguments will arrive.
 
If `FUNCTION_INCOMING_ARG' is not defined, `FUNCTION_ARG' serves both
purposes. */
 
#define FUNCTION_INCOMING_ARG(CUM, MODE, TYPE, NAMED) \
frv_function_arg (&CUM, MODE, TYPE, NAMED, TRUE)
 
/* A C type for declaring a variable that is used as the first argument of
`FUNCTION_ARG' and other related values. For some target machines, the type
`int' suffices and can hold the number of bytes of argument so far.
 
There is no need to record in `CUMULATIVE_ARGS' anything about the arguments
that have been passed on the stack. The compiler has other variables to
keep track of that. For target machines on which all arguments are passed
on the stack, there is no need to store anything in `CUMULATIVE_ARGS';
however, the data structure must exist and should not be empty, so use
`int'. */
#define CUMULATIVE_ARGS int
 
/* A C statement (sans semicolon) for initializing the variable CUM for the
state at the beginning of the argument list. The variable has type
`CUMULATIVE_ARGS'. The value of FNTYPE is the tree node for the data type
of the function which will receive the args, or 0 if the args are to a
compiler support library function. The value of INDIRECT is nonzero when
processing an indirect call, for example a call through a function pointer.
The value of INDIRECT is zero for a call to an explicitly named function, a
library function call, or when `INIT_CUMULATIVE_ARGS' is used to find
arguments for the function being compiled.
 
When processing a call to a compiler support library function, LIBNAME
identifies which one. It is a `symbol_ref' rtx which contains the name of
the function, as a string. LIBNAME is 0 when an ordinary C function call is
being processed. Thus, each time this macro is called, either LIBNAME or
FNTYPE is nonzero, but never both of them at once. */
 
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, FNDECL, N_NAMED_ARGS) \
frv_init_cumulative_args (&CUM, FNTYPE, LIBNAME, FNDECL, FALSE)
 
/* Like `INIT_CUMULATIVE_ARGS' but overrides it for the purposes of finding the
arguments for the function being compiled. If this macro is undefined,
`INIT_CUMULATIVE_ARGS' is used instead.
 
The value passed for LIBNAME is always 0, since library routines with
special calling conventions are never compiled with GCC. The argument
LIBNAME exists for symmetry with `INIT_CUMULATIVE_ARGS'. */
 
#define INIT_CUMULATIVE_INCOMING_ARGS(CUM, FNTYPE, LIBNAME) \
frv_init_cumulative_args (&CUM, FNTYPE, LIBNAME, NULL, TRUE)
 
/* A C statement (sans semicolon) to update the summarizer variable CUM to
advance past an argument in the argument list. The values MODE, TYPE and
NAMED describe that argument. Once this is done, the variable CUM is
suitable for analyzing the *following* argument with `FUNCTION_ARG', etc.
 
This macro need not do anything if the argument in question was passed on
the stack. The compiler knows how to track the amount of stack space used
for arguments without any special help. */
#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \
frv_function_arg_advance (&CUM, MODE, TYPE, NAMED)
 
/* If defined, a C expression that gives the alignment boundary, in bits, of an
argument with the specified mode and type. If it is not defined,
`PARM_BOUNDARY' is used for all arguments. */
 
#define FUNCTION_ARG_BOUNDARY(MODE, TYPE) \
frv_function_arg_boundary (MODE, TYPE)
 
/* A C expression that is nonzero if REGNO is the number of a hard register in
which function arguments are sometimes passed. This does *not* include
implicit arguments such as the static chain and the structure-value address.
On many machines, no registers can be used for this purpose since all
function arguments are pushed on the stack. */
#define FUNCTION_ARG_REGNO_P(REGNO) \
((REGNO) >= FIRST_ARG_REGNUM && ((REGNO) <= LAST_ARG_REGNUM))
 
/* How Scalar Function Values are Returned. */
 
/* The number of the hard register that is used to return a scalar value from a
function call. */
#define RETURN_VALUE_REGNUM (GPR_FIRST + 8)
 
/* A C expression to create an RTX representing the place where a function
returns a value of data type VALTYPE. VALTYPE is a tree node representing a
data type. Write `TYPE_MODE (VALTYPE)' to get the machine mode used to
represent that type. On many machines, only the mode is relevant.
(Actually, on most machines, scalar values are returned in the same place
regardless of mode).
 
If `TARGET_PROMOTE_FUNCTION_RETURN' is defined to return true, you
must apply the same 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
(`FUNCTION_DECL') for it; otherwise, FUNC is a null pointer. This makes it
possible to use a different value-returning convention for specific
functions when all their calls are known.
 
`FUNCTION_VALUE' is not used for return vales with aggregate data types,
because these are returned in another way. See
`TARGET_STRUCT_VALUE_RTX' and related macros, below. */
#define FUNCTION_VALUE(VALTYPE, FUNC) \
gen_rtx_REG (TYPE_MODE (VALTYPE), RETURN_VALUE_REGNUM)
 
/* A C expression to create an RTX representing the place where a library
function returns a value of mode MODE.
 
Note that "library function" in this context means a compiler support
routine, used to perform arithmetic, whose name is known specially by the
compiler and was not mentioned in the C code being compiled.
 
The definition of `LIBRARY_VALUE' need not be concerned aggregate data
types, because none of the library functions returns such types. */
#define LIBCALL_VALUE(MODE) gen_rtx_REG (MODE, RETURN_VALUE_REGNUM)
 
/* A C expression that is nonzero if REGNO is the number of a hard register in
which the values of called function may come back.
 
A register whose use for returning values is limited to serving as the
second of a pair (for a value of type `double', say) need not be recognized
by this macro. So for most machines, this definition suffices:
 
#define FUNCTION_VALUE_REGNO_P(N) ((N) == RETURN)
 
If the machine has register windows, so that the caller and the called
function use different registers for the return value, this macro should
recognize only the caller's register numbers. */
#define FUNCTION_VALUE_REGNO_P(REGNO) ((REGNO) == RETURN_VALUE_REGNUM)
 
/* How Large Values are Returned. */
 
/* The number of the register that is used to pass the structure
value address. */
#define FRV_STRUCT_VALUE_REGNUM (GPR_FIRST + 3)
 
/* Function Entry and Exit. */
 
/* Define this macro as a C expression that is nonzero if the return
instruction or the function epilogue ignores the value of the stack pointer;
in other words, if it is safe to delete an instruction to adjust the stack
pointer before a return from the function.
 
Note that this macro's value is relevant only for functions for which frame
pointers are maintained. It is never safe to delete a final stack
adjustment in a function that has no frame pointer, and the compiler knows
this regardless of `EXIT_IGNORE_STACK'. */
#define EXIT_IGNORE_STACK 1
/* Generating Code for Profiling. */
 
/* A C statement or compound statement to output to FILE some assembler code to
call the profiling subroutine `mcount'. Before calling, the assembler code
must load the address of a counter variable into a register where `mcount'
expects to find the address. The name of this variable is `LP' followed by
the number LABELNO, so you would generate the name using `LP%d' in a
`fprintf'.
 
The details of how the address should be passed to `mcount' are determined
by your operating system environment, not by GCC. To figure them out,
compile a small program for profiling using the system's installed C
compiler and look at the assembler code that results.
 
This declaration must be present, but it can be an abort if profiling is
not implemented. */
 
#define FUNCTION_PROFILER(FILE, LABELNO)
 
/* Implementing the Varargs Macros. */
 
/* Implement the stdarg/varargs va_start macro. STDARG_P is nonzero if this
is stdarg.h instead of varargs.h. VALIST is the tree of the va_list
variable to initialize. NEXTARG is the machine independent notion of the
'next' argument after the variable arguments. If not defined, a standard
implementation will be defined that works for arguments passed on the stack. */
 
#define EXPAND_BUILTIN_VA_START(VALIST, NEXTARG) \
(frv_expand_builtin_va_start(VALIST, NEXTARG))
 
/* Trampolines for Nested Functions. */
 
/* A C expression for the size in bytes of the trampoline, as an integer. */
#define TRAMPOLINE_SIZE frv_trampoline_size ()
 
/* Alignment required for trampolines, in bits.
 
If you don't define this macro, the value of `BIGGEST_ALIGNMENT' is used for
aligning trampolines. */
#define TRAMPOLINE_ALIGNMENT (TARGET_FDPIC ? 64 : 32)
 
/* A C statement to initialize the variable parts of a trampoline. ADDR is an
RTX for the address of the trampoline; FNADDR is an RTX for the address of
the nested function; STATIC_CHAIN is an RTX for the static chain value that
should be passed to the function when it is called. */
#define INITIALIZE_TRAMPOLINE(ADDR, FNADDR, STATIC_CHAIN) \
frv_initialize_trampoline (ADDR, FNADDR, STATIC_CHAIN)
 
/* Define this macro if trampolines need a special subroutine to do their work.
The macro should expand to a series of `asm' statements which will be
compiled with GCC. They go in a library function named
`__transfer_from_trampoline'.
 
If you need to avoid executing the ordinary prologue code of a compiled C
function when you jump to the subroutine, you can do so by placing a special
label of your own in the assembler code. Use one `asm' statement to
generate an assembler label, and another to make the label global. Then
trampolines can use that label to jump directly to your special assembler
code. */
 
#ifdef __FRV_UNDERSCORE__
#define TRAMPOLINE_TEMPLATE_NAME "___trampoline_template"
#else
#define TRAMPOLINE_TEMPLATE_NAME "__trampoline_template"
#endif
 
#define Twrite _write
 
#if ! __FRV_FDPIC__
#define TRANSFER_FROM_TRAMPOLINE \
extern int Twrite (int, const void *, unsigned); \
\
void \
__trampoline_setup (short * addr, int size, int fnaddr, int sc) \
{ \
extern short __trampoline_template[]; \
short * to = addr; \
short * from = &__trampoline_template[0]; \
int i; \
\
if (size < 20) \
{ \
Twrite (2, "__trampoline_setup bad size\n", \
sizeof ("__trampoline_setup bad size\n") - 1); \
exit (-1); \
} \
\
to[0] = from[0]; \
to[1] = (short)(fnaddr); \
to[2] = from[2]; \
to[3] = (short)(sc); \
to[4] = from[4]; \
to[5] = (short)(fnaddr >> 16); \
to[6] = from[6]; \
to[7] = (short)(sc >> 16); \
to[8] = from[8]; \
to[9] = from[9]; \
\
for (i = 0; i < 20; i++) \
__asm__ volatile ("dcf @(%0,%1)\n\tici @(%0,%1)" :: "r" (to), "r" (i)); \
} \
\
__asm__("\n" \
"\t.globl " TRAMPOLINE_TEMPLATE_NAME "\n" \
"\t.text\n" \
TRAMPOLINE_TEMPLATE_NAME ":\n" \
"\tsetlos #0, gr6\n" /* jump register */ \
"\tsetlos #0, gr7\n" /* static chain */ \
"\tsethi #0, gr6\n" \
"\tsethi #0, gr7\n" \
"\tjmpl @(gr0,gr6)\n");
#else
#define TRANSFER_FROM_TRAMPOLINE \
extern int Twrite (int, const void *, unsigned); \
\
void \
__trampoline_setup (addr, size, fnaddr, sc) \
short * addr; \
int size; \
int fnaddr; \
int sc; \
{ \
extern short __trampoline_template[]; \
short * from = &__trampoline_template[0]; \
int i; \
short **desc = (short **)addr; \
short * to = addr + 4; \
\
if (size != 32) \
{ \
Twrite (2, "__trampoline_setup bad size\n", \
sizeof ("__trampoline_setup bad size\n") - 1); \
exit (-1); \
} \
\
/* Create a function descriptor with the address of the code below
and NULL as the FDPIC value. We don't need the real GOT value
here, since we don't use it, so we use NULL, that is just as
good. */ \
desc[0] = to; \
desc[1] = NULL; \
size -= 8; \
\
to[0] = from[0]; \
to[1] = (short)(fnaddr); \
to[2] = from[2]; \
to[3] = (short)(sc); \
to[4] = from[4]; \
to[5] = (short)(fnaddr >> 16); \
to[6] = from[6]; \
to[7] = (short)(sc >> 16); \
to[8] = from[8]; \
to[9] = from[9]; \
to[10] = from[10]; \
to[11] = from[11]; \
\
for (i = 0; i < size; i++) \
__asm__ volatile ("dcf @(%0,%1)\n\tici @(%0,%1)" :: "r" (to), "r" (i)); \
} \
\
__asm__("\n" \
"\t.globl " TRAMPOLINE_TEMPLATE_NAME "\n" \
"\t.text\n" \
TRAMPOLINE_TEMPLATE_NAME ":\n" \
"\tsetlos #0, gr6\n" /* Jump register. */ \
"\tsetlos #0, gr7\n" /* Static chain. */ \
"\tsethi #0, gr6\n" \
"\tsethi #0, gr7\n" \
"\tldd @(gr6,gr0),gr14\n" \
"\tjmpl @(gr14,gr0)\n" \
);
#endif
 
/* Addressing Modes. */
 
/* A C expression that is 1 if the RTX X is a constant which is a valid
address. On most machines, this can be defined as `CONSTANT_P (X)', but a
few machines are more restrictive in which constant addresses are supported.
 
`CONSTANT_P' accepts integer-values expressions whose values are not
explicitly known, such as `symbol_ref', `label_ref', and `high' expressions
and `const' arithmetic expressions, in addition to `const_int' and
`const_double' expressions. */
#define CONSTANT_ADDRESS_P(X) CONSTANT_P (X)
 
/* A number, the maximum number of registers that can appear in a valid memory
address. Note that it is up to you to specify a value equal to the maximum
number that `GO_IF_LEGITIMATE_ADDRESS' would ever accept. */
#define MAX_REGS_PER_ADDRESS 2
 
/* A C compound statement with a conditional `goto LABEL;' executed if X (an
RTX) is a legitimate memory address on the target machine for a memory
operand of mode MODE.
 
It usually pays to define several simpler macros to serve as subroutines for
this one. Otherwise it may be too complicated to understand.
 
This macro must exist in two variants: a strict variant and a non-strict
one. 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
considered a memory reference. In contexts where some kind of register is
required, a pseudo-register with no hard register must be rejected.
 
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
required.
 
Compiler source files that want to use the strict variant of this macro
define the macro `REG_OK_STRICT'. You should use an `#ifdef REG_OK_STRICT'
conditional to define the strict variant in that case and the non-strict
variant otherwise.
 
Subroutines to check for acceptable registers for various purposes (one for
base registers, one for index registers, and so on) are typically among the
subroutines used to define `GO_IF_LEGITIMATE_ADDRESS'. Then only these
subroutine macros need have two variants; the higher levels of macros may be
the same whether strict or not.
 
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, there is no need to recognize such sums specifically as
legitimate addresses. Normally you would simply recognize any `const' as
legitimate.
 
Usually `PRINT_OPERAND_ADDRESS' is not prepared to handle constant sums that
are not marked with `const'. It assumes that a naked `plus' indicates
indexing. If so, then you *must* reject such naked constant sums as
illegitimate addresses, so that none of them will be given to
`PRINT_OPERAND_ADDRESS'.
 
On some machines, whether a symbolic address is legitimate depends on the
section that the address refers to. On these machines, define the macro
`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 to look
inside it to find the `symbol_ref' in order to determine the section.
 
The best way to modify the name string is by adding text to the beginning,
with suitable punctuation to prevent any ambiguity. Allocate the new name
in `saveable_obstack'. You will have to modify `ASM_OUTPUT_LABELREF' to
remove and decode the added text and output the name accordingly, and define
`(* targetm.strip_name_encoding)' to access the original name string.
 
You can check the information stored here into the `symbol_ref' in the
definitions of the macros `GO_IF_LEGITIMATE_ADDRESS' and
`PRINT_OPERAND_ADDRESS'. */
 
#ifdef REG_OK_STRICT
#define REG_OK_STRICT_P 1
#else
#define REG_OK_STRICT_P 0
#endif
 
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, LABEL) \
do \
{ \
if (frv_legitimate_address_p (MODE, X, REG_OK_STRICT_P, \
FALSE, FALSE)) \
goto LABEL; \
} \
while (0)
 
/* A C expression that is nonzero if X (assumed to be a `reg' RTX) is valid for
use as a base register. For hard registers, it should always accept those
which the hardware permits and reject the others. Whether the macro accepts
or rejects pseudo registers must be controlled by `REG_OK_STRICT' as
described above. This usually requires two variant definitions, of which
`REG_OK_STRICT' controls the one actually used. */
#ifdef REG_OK_STRICT
#define REG_OK_FOR_BASE_P(X) GPR_P (REGNO (X))
#else
#define REG_OK_FOR_BASE_P(X) GPR_AP_OR_PSEUDO_P (REGNO (X))
#endif
 
/* A C expression that is nonzero if X (assumed to be a `reg' RTX) is valid for
use as an index register.
 
The difference between an index register and a base register is that the
index register may be scaled. If an address involves the sum of two
registers, neither one of them scaled, then either one may be labeled the
"base" and the other the "index"; but whichever labeling is used must fit
the machine's constraints of which registers may serve in each capacity.
The compiler will try both labelings, looking for one that is valid, and
will reload one or both registers only if neither labeling works. */
#define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_BASE_P (X)
 
#define LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) \
do { \
rtx new_x = frv_legitimize_address (X, OLDX, MODE); \
if (new_x) \
{ \
(X) = new_x; \
goto WIN; \
} \
} while (0)
 
#define FIND_BASE_TERM frv_find_base_term
 
/* A C statement or compound statement with a conditional `goto LABEL;'
executed if memory address X (an RTX) can have different meanings depending
on the machine mode of the memory reference it is used for or if the address
is valid for some modes but not others.
 
Autoincrement and autodecrement addresses typically have mode-dependent
effects because the amount of the increment or decrement is the size of the
operand being addressed. Some machines have other mode-dependent addresses.
Many RISC machines have no mode-dependent addresses.
 
You may assume that ADDR is a valid address for the machine. */
#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL)
 
/* A C expression that is nonzero if X is a legitimate constant for an
immediate operand on the target machine. You can assume that X satisfies
`CONSTANT_P', so you need not check this. In fact, `1' is a suitable
definition for this macro on machines where anything `CONSTANT_P' is valid. */
#define LEGITIMATE_CONSTANT_P(X) frv_legitimate_constant_p (X)
 
/* The load-and-update commands allow pre-modification in addresses.
The index has to be in a register. */
#define HAVE_PRE_MODIFY_REG 1
 
/* We define extra CC modes in frv-modes.def so we need a selector. */
 
#define SELECT_CC_MODE frv_select_cc_mode
 
/* A C expression whose value is one if it is always safe to reverse a
comparison whose mode is MODE. If `SELECT_CC_MODE' can ever return MODE for
a floating-point inequality comparison, then `REVERSIBLE_CC_MODE (MODE)'
must be zero.
 
You need not define this macro if it would always returns zero or if the
floating-point format is anything other than `IEEE_FLOAT_FORMAT'. For
example, here is the definition used on the SPARC, where floating-point
inequality comparisons are always given `CCFPEmode':
 
#define REVERSIBLE_CC_MODE(MODE) ((MODE) != CCFPEmode) */
 
/* On frv, don't consider floating point comparisons to be reversible. In
theory, fp equality comparisons can be reversible. */
#define REVERSIBLE_CC_MODE(MODE) \
((MODE) == CCmode || (MODE) == CC_UNSmode || (MODE) == CC_NZmode)
 
/* Frv CCR_MODE's are not reversible. */
#define REVERSE_CONDEXEC_PREDICATES_P(x,y) 0
 
/* Describing Relative Costs of Operations. */
 
/* A C expression for the cost of moving data from a register in class FROM to
one in class TO. The classes are expressed using the enumeration values
such as `GENERAL_REGS'. A value of 4 is the default; other values are
interpreted relative to that.
 
It is not required that the cost always equal 2 when FROM is the same as TO;
on some machines it is expensive to move between registers if they are not
general registers.
 
If reload sees an insn consisting of a single `set' between two hard
registers, and if `REGISTER_MOVE_COST' applied to their classes returns a
value of 2, reload does not check to ensure that the constraints of the insn
are met. Setting a cost of other than 2 will allow reload to verify that
the constraints are met. You should do this if the `movM' pattern's
constraints do not allow such copying. */
#define REGISTER_MOVE_COST(MODE, FROM, TO) frv_register_move_cost (FROM, TO)
 
/* A C expression for the cost of moving data of mode M between a register and
memory. A value of 2 is the default; this cost is relative to those in
`REGISTER_MOVE_COST'.
 
If moving between registers and memory is more expensive than between two
registers, you should define this macro to express the relative cost. */
#define MEMORY_MOVE_COST(M,C,I) 4
 
/* A C expression for the cost of a branch instruction. A value of 1 is the
default; other values are interpreted relative to that. */
#define BRANCH_COST frv_branch_cost_int
 
/* Define this macro as a C expression which is nonzero if accessing less than
a word of memory (i.e. a `char' or a `short') is no faster than accessing a
word of memory, i.e., if such access require more than one instruction or if
there is no difference in cost between byte and (aligned) word loads.
 
When this macro is not defined, the compiler will access a field by finding
the smallest containing object; when it is defined, a fullword load will be
used if alignment permits. Unless bytes accesses are faster than word
accesses, using word accesses is preferable since it may eliminate
subsequent memory access if subsequent accesses occur to other fields in the
same word of the structure, but to different bytes. */
#define SLOW_BYTE_ACCESS 1
 
/* Define this macro if it is as good or better to call a constant function
address than to call an address kept in a register. */
#define NO_FUNCTION_CSE
 
/* Dividing the output into sections. */
 
/* A C expression whose value is a string containing the assembler operation
that should precede instructions and read-only data. Normally `".text"' is
right. */
#define TEXT_SECTION_ASM_OP "\t.text"
 
/* A C expression whose value is a string containing the assembler operation to
identify the following data as writable initialized data. Normally
`".data"' is right. */
#define DATA_SECTION_ASM_OP "\t.data"
 
/* If defined, a C expression whose value is a string containing the
assembler operation to identify the following data as
uninitialized global data. If not defined, and neither
`ASM_OUTPUT_BSS' nor `ASM_OUTPUT_ALIGNED_BSS' are defined,
uninitialized global data will be output in the data section if
`-fno-common' is passed, otherwise `ASM_OUTPUT_COMMON' will be
used. */
#define BSS_SECTION_ASM_OP "\t.section .bss,\"aw\""
 
/* Short Data Support */
#define SDATA_SECTION_ASM_OP "\t.section .sdata,\"aw\""
 
/* On svr4, we *do* have support for the .init and .fini sections, and we
can put stuff in there to be executed before and after `main'. We let
crtstuff.c and other files know this by defining the following symbols.
The definitions say how to change sections to the .init and .fini
sections. This is the same for all known svr4 assemblers.
 
The standard System V.4 macros will work, but they look ugly in the
assembly output, so redefine them. */
 
#undef INIT_SECTION_ASM_OP
#undef FINI_SECTION_ASM_OP
#define INIT_SECTION_ASM_OP "\t.section .init,\"ax\""
#define FINI_SECTION_ASM_OP "\t.section .fini,\"ax\""
 
#undef CTORS_SECTION_ASM_OP
#undef DTORS_SECTION_ASM_OP
#define CTORS_SECTION_ASM_OP "\t.section\t.ctors,\"a\""
#define DTORS_SECTION_ASM_OP "\t.section\t.dtors,\"a\""
 
/* A C expression whose value is a string containing the assembler operation to
switch to the fixup section that records all initialized pointers in a -fpic
program so they can be changed program startup time if the program is loaded
at a different address than linked for. */
#define FIXUP_SECTION_ASM_OP "\t.section .rofixup,\"a\""
/* Position Independent Code. */
 
/* A C expression that is nonzero if X is a legitimate immediate operand on the
target machine when generating position independent code. You can assume
that X satisfies `CONSTANT_P', so you need not check this. You can also
assume FLAG_PIC is true, so you need not check it either. You need not
define this macro if all constants (including `SYMBOL_REF') can be immediate
operands when generating position independent code. */
#define LEGITIMATE_PIC_OPERAND_P(X) \
( GET_CODE (X) == CONST_INT \
|| GET_CODE (X) == CONST_DOUBLE \
|| (GET_CODE (X) == HIGH && GET_CODE (XEXP (X, 0)) == CONST_INT) \
|| got12_operand (X, VOIDmode)) \
 
/* The Overall Framework of an Assembler File. */
 
/* A C string constant describing how to begin a comment in the target
assembler language. The compiler assumes that the comment will end at the
end of the line. */
#define ASM_COMMENT_START ";"
 
/* A C string constant for text to be output before each `asm' statement or
group of consecutive ones. Normally this is `"#APP"', which is a comment
that has no effect on most assemblers but tells the GNU assembler that it
must check the lines that follow for all valid assembler constructs. */
#define ASM_APP_ON "#APP\n"
 
/* A C string constant for text to be output after each `asm' statement or
group of consecutive ones. Normally this is `"#NO_APP"', which tells the
GNU assembler to resume making the time-saving assumptions that are valid
for ordinary compiler output. */
#define ASM_APP_OFF "#NO_APP\n"
 
/* Output of Data. */
 
/* This is how to output a label to dwarf/dwarf2. */
#define ASM_OUTPUT_DWARF_ADDR(STREAM, LABEL) \
do { \
fprintf (STREAM, "\t.picptr\t"); \
assemble_name (STREAM, LABEL); \
} while (0)
 
/* Whether to emit the gas specific dwarf2 line number support. */
#define DWARF2_ASM_LINE_DEBUG_INFO (TARGET_DEBUG_LOC)
/* Output of Uninitialized Variables. */
 
/* A C statement (sans semicolon) to output to the stdio stream STREAM the
assembler definition of a local-common-label named NAME whose size is SIZE
bytes. The variable ROUNDED is the size rounded up to whatever alignment
the caller wants.
 
Use the expression `assemble_name (STREAM, NAME)' to output the name itself;
before and after that, output the additional assembler syntax for defining
the name, and a newline.
 
This macro controls how the assembler definitions of uninitialized static
variables are output. */
#undef ASM_OUTPUT_LOCAL
 
/* Like `ASM_OUTPUT_LOCAL' except takes the required alignment as a separate,
explicit argument. If you define this macro, it is used in place of
`ASM_OUTPUT_LOCAL', and gives you more flexibility in handling the required
alignment of the variable. The alignment is specified as the number of
bits.
 
Defined in svr4.h. */
#undef ASM_OUTPUT_ALIGNED_LOCAL
 
/* This is for final.c, because it is used by ASM_DECLARE_OBJECT_NAME. */
extern int size_directive_output;
 
/* Like `ASM_OUTPUT_ALIGNED_LOCAL' except that it takes an additional
parameter - the DECL of variable to be output, if there is one.
This macro can be called with DECL == NULL_TREE. If you define
this macro, it is used in place of `ASM_OUTPUT_LOCAL' and
`ASM_OUTPUT_ALIGNED_LOCAL', and gives you more flexibility in
handling the destination of the variable. */
#undef ASM_OUTPUT_ALIGNED_DECL_LOCAL
#define ASM_OUTPUT_ALIGNED_DECL_LOCAL(STREAM, DECL, NAME, SIZE, ALIGN) \
do { \
if ((SIZE) > 0 && (SIZE) <= g_switch_value) \
switch_to_section (get_named_section (NULL, ".sbss", 0)); \
else \
switch_to_section (bss_section); \
ASM_OUTPUT_ALIGN (STREAM, floor_log2 ((ALIGN) / BITS_PER_UNIT)); \
ASM_DECLARE_OBJECT_NAME (STREAM, NAME, DECL); \
ASM_OUTPUT_SKIP (STREAM, (SIZE) ? (SIZE) : 1); \
} while (0)
 
/* Output and Generation of Labels. */
 
/* A C statement (sans semicolon) to output to the stdio stream STREAM the
assembler definition of a label named NAME. Use the expression
`assemble_name (STREAM, NAME)' to output the name itself; before and after
that, output the additional assembler syntax for defining the name, and a
newline. */
#define ASM_OUTPUT_LABEL(STREAM, NAME) \
do { \
assemble_name (STREAM, NAME); \
fputs (":\n", STREAM); \
} while (0)
 
/* Globalizing directive for a label. */
#define GLOBAL_ASM_OP "\t.globl "
 
/* A C statement to store into the string STRING a label whose name is made
from the string PREFIX and the number NUM.
 
This string, when output subsequently by `assemble_name', should produce the
output that `(*targetm.asm_out.internal_label)' would produce with the same PREFIX
and NUM.
 
If the string begins with `*', then `assemble_name' will output the rest of
the string unchanged. It is often convenient for
`ASM_GENERATE_INTERNAL_LABEL' to use `*' in this way. If the string doesn't
start with `*', then `ASM_OUTPUT_LABELREF' gets to output the string, and
may change it. (Of course, `ASM_OUTPUT_LABELREF' is also part of your
machine description, so you should know what it does on your machine.)
 
Defined in svr4.h. */
#undef ASM_GENERATE_INTERNAL_LABEL
#define ASM_GENERATE_INTERNAL_LABEL(LABEL, PREFIX, NUM) \
do { \
sprintf (LABEL, "*.%s%ld", PREFIX, (long)NUM); \
} while (0)
 
/* Macros Controlling Initialization Routines. */
 
/* If defined, a C string constant for the assembler operation to identify the
following data as initialization code. If not defined, GCC will assume
such a section does not exist. When you are using special sections for
initialization and termination functions, this macro also controls how
`crtstuff.c' and `libgcc2.c' arrange to run the initialization functions.
 
Defined in svr4.h. */
#undef INIT_SECTION_ASM_OP
 
/* If defined, `main' will call `__main' despite the presence of
`INIT_SECTION_ASM_OP'. This macro should be defined for systems where the
init section is not actually run automatically, but is still useful for
collecting the lists of constructors and destructors. */
#define INVOKE__main
/* Output of Assembler Instructions. */
 
/* A C initializer containing the assembler's names for the machine registers,
each one as a C string constant. This is what translates register numbers
in the compiler into assembler language. */
#define REGISTER_NAMES \
{ \
"gr0", "sp", "fp", "gr3", "gr4", "gr5", "gr6", "gr7", \
"gr8", "gr9", "gr10", "gr11", "gr12", "gr13", "gr14", "gr15", \
"gr16", "gr17", "gr18", "gr19", "gr20", "gr21", "gr22", "gr23", \
"gr24", "gr25", "gr26", "gr27", "gr28", "gr29", "gr30", "gr31", \
"gr32", "gr33", "gr34", "gr35", "gr36", "gr37", "gr38", "gr39", \
"gr40", "gr41", "gr42", "gr43", "gr44", "gr45", "gr46", "gr47", \
"gr48", "gr49", "gr50", "gr51", "gr52", "gr53", "gr54", "gr55", \
"gr56", "gr57", "gr58", "gr59", "gr60", "gr61", "gr62", "gr63", \
\
"fr0", "fr1", "fr2", "fr3", "fr4", "fr5", "fr6", "fr7", \
"fr8", "fr9", "fr10", "fr11", "fr12", "fr13", "fr14", "fr15", \
"fr16", "fr17", "fr18", "fr19", "fr20", "fr21", "fr22", "fr23", \
"fr24", "fr25", "fr26", "fr27", "fr28", "fr29", "fr30", "fr31", \
"fr32", "fr33", "fr34", "fr35", "fr36", "fr37", "fr38", "fr39", \
"fr40", "fr41", "fr42", "fr43", "fr44", "fr45", "fr46", "fr47", \
"fr48", "fr49", "fr50", "fr51", "fr52", "fr53", "fr54", "fr55", \
"fr56", "fr57", "fr58", "fr59", "fr60", "fr61", "fr62", "fr63", \
\
"fcc0", "fcc1", "fcc2", "fcc3", "icc0", "icc1", "icc2", "icc3", \
"cc0", "cc1", "cc2", "cc3", "cc4", "cc5", "cc6", "cc7", \
"acc0", "acc1", "acc2", "acc3", "acc4", "acc5", "acc6", "acc7", \
"acc8", "acc9", "acc10", "acc11", \
"accg0","accg1","accg2","accg3","accg4","accg5","accg6","accg7", \
"accg8", "accg9", "accg10", "accg11", \
"ap", "lr", "lcr", "iacc0h", "iacc0l" \
}
 
/* Define this macro if you are using an unusual assembler that
requires different names for the machine instructions.
 
The definition is a C statement or statements which output an
assembler instruction opcode to the stdio stream STREAM. The
macro-operand PTR is a variable of type `char *' which points to
the opcode name in its "internal" form--the form that is written
in the machine description. The definition should output the
opcode name to STREAM, performing any translation you desire, and
increment the variable PTR to point at the end of the opcode so
that it will not be output twice.
 
In fact, your macro definition may process less than the entire
opcode name, or more than the opcode name; but if you want to
process text that includes `%'-sequences to substitute operands,
you must take care of the substitution yourself. Just be sure to
increment PTR over whatever text should not be output normally.
 
If you need to look at the operand values, they can be found as the
elements of `recog_operand'.
 
If the macro definition does nothing, the instruction is output in
the usual way. */
 
#define ASM_OUTPUT_OPCODE(STREAM, PTR)\
(PTR) = frv_asm_output_opcode (STREAM, PTR)
 
/* If defined, a C statement to be executed just prior to the output
of assembler code for INSN, to modify the extracted operands so
they will be output differently.
 
Here the argument OPVEC is the vector containing the operands
extracted from INSN, and NOPERANDS is the number of elements of
the vector which contain meaningful data for this insn. The
contents of this vector are what will be used to convert the insn
template into assembler code, so you can change the assembler
output by changing the contents of the vector.
 
This macro is useful when various assembler syntaxes share a single
file of instruction patterns; by defining this macro differently,
you can cause a large class of instructions to be output
differently (such as with rearranged operands). Naturally,
variations in assembler syntax affecting individual insn patterns
ought to be handled by writing conditional output routines in
those patterns.
 
If this macro is not defined, it is equivalent to a null statement. */
 
#define FINAL_PRESCAN_INSN(INSN, OPVEC, NOPERANDS)\
frv_final_prescan_insn (INSN, OPVEC, NOPERANDS)
 
 
/* A C compound statement to output to stdio stream STREAM the assembler syntax
for an instruction operand X. X is an RTL expression.
 
CODE is a value that can be used to specify one of several ways of printing
the operand. It is used when identical operands must be printed differently
depending on the context. CODE comes from the `%' specification that was
used to request printing of the operand. If the specification was just
`%DIGIT' then CODE is 0; if the specification was `%LTR DIGIT' then CODE is
the ASCII code for LTR.
 
If X is a register, this macro should print the register's name. The names
can be found in an array `reg_names' whose type is `char *[]'. `reg_names'
is initialized from `REGISTER_NAMES'.
 
When the machine description has a specification `%PUNCT' (a `%' followed by
a punctuation character), this macro is called with a null pointer for X and
the punctuation character for CODE. */
#define PRINT_OPERAND(STREAM, X, CODE) frv_print_operand (STREAM, X, CODE)
 
/* A C expression which evaluates to true if CODE is a valid punctuation
character for use in the `PRINT_OPERAND' macro. If
`PRINT_OPERAND_PUNCT_VALID_P' is not defined, it means that no punctuation
characters (except for the standard one, `%') are used in this way. */
/* . == gr0
# == hint operand -- always zero for now
@ == small data base register (gr16)
~ == pic register (gr17)
* == temporary integer CCR register (cr3)
& == temporary integer ICC register (icc3) */
#define PRINT_OPERAND_PUNCT_VALID_P(CODE) \
((CODE) == '.' || (CODE) == '#' || (CODE) == '@' || (CODE) == '~' \
|| (CODE) == '*' || (CODE) == '&')
 
/* A C compound statement to output to stdio stream STREAM the assembler syntax
for an instruction operand that is a memory reference whose address is X. X
is an RTL expression.
 
On some machines, the syntax for a symbolic address depends on the section
that the address refers to. On these machines, define the macro
`ENCODE_SECTION_INFO' to store the information into the `symbol_ref', and
then check for it here.
 
This declaration must be present. */
#define PRINT_OPERAND_ADDRESS(STREAM, X) frv_print_operand_address (STREAM, X)
 
/* If defined, C string expressions to be used for the `%R', `%L', `%U', and
`%I' options of `asm_fprintf' (see `final.c'). These are useful when a
single `md' file must support multiple assembler formats. In that case, the
various `tm.h' files can define these macros differently.
 
USER_LABEL_PREFIX is defined in svr4.h. */
#undef USER_LABEL_PREFIX
#define USER_LABEL_PREFIX ""
#define REGISTER_PREFIX ""
#define LOCAL_LABEL_PREFIX "."
#define IMMEDIATE_PREFIX "#"
 
/* Output of dispatch tables. */
 
/* This macro should be provided on machines where the addresses in a dispatch
table are relative to the table's own address.
 
The definition should be a C statement to output to the stdio stream STREAM
an assembler pseudo-instruction to generate a difference between two labels.
VALUE and REL are the numbers of two internal labels. The definitions of
these labels are output using `(*targetm.asm_out.internal_label)', and they must be
printed in the same way here. For example,
 
fprintf (STREAM, "\t.word L%d-L%d\n", VALUE, REL) */
#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM, BODY, VALUE, REL) \
fprintf (STREAM, "\t.word .L%d-.L%d\n", VALUE, REL)
 
/* This macro should be provided on machines where the addresses in a dispatch
table are absolute.
 
The definition should be a C statement to output to the stdio stream STREAM
an assembler pseudo-instruction to generate a reference to a label. VALUE
is the number of an internal label whose definition is output using
`(*targetm.asm_out.internal_label)'. For example,
 
fprintf (STREAM, "\t.word L%d\n", VALUE) */
#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM, VALUE) \
fprintf (STREAM, "\t.word .L%d\n", VALUE)
 
/* Define this if the label before a jump-table needs to be output specially.
The first three arguments are the same as for `(*targetm.asm_out.internal_label)';
the fourth argument is the jump-table which follows (a `jump_insn'
containing an `addr_vec' or `addr_diff_vec').
 
This feature is used on system V to output a `swbeg' statement for the
table.
 
If this macro is not defined, these labels are output with
`(*targetm.asm_out.internal_label)'.
 
Defined in svr4.h. */
/* When generating embedded PIC or mips16 code we want to put the jump
table in the .text section. In all other cases, we want to put the
jump table in the .rdata section. Unfortunately, we can't use
JUMP_TABLES_IN_TEXT_SECTION, because it is not conditional.
Instead, we use ASM_OUTPUT_CASE_LABEL to switch back to the .text
section if appropriate. */
 
#undef ASM_OUTPUT_CASE_LABEL
#define ASM_OUTPUT_CASE_LABEL(STREAM, PREFIX, NUM, TABLE) \
do { \
if (flag_pic) \
switch_to_section (function_section (current_function_decl)); \
(*targetm.asm_out.internal_label) (STREAM, PREFIX, NUM); \
} while (0)
 
/* Assembler Commands for Exception Regions. */
 
/* Define this macro to 0 if your target supports DWARF 2 frame unwind
information, but it does not yet work with exception handling. Otherwise,
if your target supports this information (if it defines
`INCOMING_RETURN_ADDR_RTX' and either `UNALIGNED_INT_ASM_OP' or
`OBJECT_FORMAT_ELF'), GCC will provide a default definition of 1.
 
If this macro is defined to 1, the DWARF 2 unwinder will be the default
exception handling mechanism; otherwise, setjmp/longjmp will be used by
default.
 
If this macro is defined to anything, the DWARF 2 unwinder will be used
instead of inline unwinders and __unwind_function in the non-setjmp case. */
#define DWARF2_UNWIND_INFO 1
 
#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (LR_REGNO)
/* Assembler Commands for Alignment. */
 
/* A C statement to output to the stdio stream STREAM an assembler instruction
to advance the location counter by NBYTES bytes. Those bytes should be zero
when loaded. NBYTES will be a C expression of type `int'.
 
Defined in svr4.h. */
#undef ASM_OUTPUT_SKIP
#define ASM_OUTPUT_SKIP(STREAM, NBYTES) \
fprintf (STREAM, "\t.zero\t%u\n", (int)(NBYTES))
 
/* A C statement to output to the stdio stream STREAM an assembler command to
advance the location counter to a multiple of 2 to the POWER bytes. POWER
will be a C expression of type `int'. */
#define ASM_OUTPUT_ALIGN(STREAM, POWER) \
fprintf ((STREAM), "\t.p2align %d\n", (POWER))
 
/* Inside the text section, align with unpacked nops rather than zeros. */
#define ASM_OUTPUT_ALIGN_WITH_NOP(STREAM, POWER) \
fprintf ((STREAM), "\t.p2alignl %d,0x80880000\n", (POWER))
/* Macros Affecting all Debug Formats. */
 
/* A C expression that returns the DBX register number for the compiler
register number REGNO. In simple cases, the value of this expression may be
REGNO itself. But sometimes there are some registers that the compiler
knows about and DBX does not, or vice versa. In such cases, some register
may need to have one number in the compiler and another for DBX.
 
If two registers have consecutive numbers inside GCC, and they can be
used as a pair to hold a multiword value, then they *must* have consecutive
numbers after renumbering with `DBX_REGISTER_NUMBER'. Otherwise, debuggers
will be unable to access such a pair, because they expect register pairs to
be consecutive in their own numbering scheme.
 
If you find yourself defining `DBX_REGISTER_NUMBER' in way that does not
preserve register pairs, then what you must do instead is redefine the
actual register numbering scheme.
 
This declaration is required. */
#define DBX_REGISTER_NUMBER(REGNO) (REGNO)
 
/* A C expression that returns the type of debugging output GCC produces
when the user specifies `-g' or `-ggdb'. Define this if you have arranged
for GCC to support more than one format of debugging output. Currently,
the allowable values are `DBX_DEBUG', `SDB_DEBUG', `DWARF_DEBUG',
`DWARF2_DEBUG', and `XCOFF_DEBUG'.
 
The value of this macro only affects the default debugging output; the user
can always get a specific type of output by using `-gstabs', `-gcoff',
`-gdwarf-1', `-gdwarf-2', or `-gxcoff'.
 
Defined in svr4.h. */
#undef PREFERRED_DEBUGGING_TYPE
#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
/* Miscellaneous Parameters. */
 
/* An alias for a machine mode name. This is the machine mode that elements of
a jump-table should have. */
#define CASE_VECTOR_MODE SImode
 
/* Define this macro if operations between registers with integral mode smaller
than a word are always performed on the entire register. Most RISC machines
have this property and most CISC machines do not. */
#define WORD_REGISTER_OPERATIONS
 
/* Define this macro to be a C expression indicating when insns that read
memory in MODE, an integral mode narrower than a word, set the bits outside
of MODE to be either the sign-extension or the zero-extension of the data
read. Return `SIGN_EXTEND' for values of MODE for which the insn
sign-extends, `ZERO_EXTEND' for which it zero-extends, and `UNKNOWN' for other
modes.
 
This macro is not called with MODE non-integral or with a width greater than
or equal to `BITS_PER_WORD', so you may return any value in this case. Do
not define this macro if it would always return `UNKNOWN'. On machines where
this macro is defined, you will normally define it as the constant
`SIGN_EXTEND' or `ZERO_EXTEND'. */
#define LOAD_EXTEND_OP(MODE) SIGN_EXTEND
 
/* Define if loading short immediate values into registers sign extends. */
#define SHORT_IMMEDIATES_SIGN_EXTEND
 
/* The maximum number of bytes that a single instruction can move quickly from
memory to memory. */
#define MOVE_MAX 8
 
/* A C expression which is nonzero if on this machine it is safe to "convert"
an integer of INPREC bits to one of OUTPREC bits (where OUTPREC is smaller
than INPREC) by merely operating on it as if it had only OUTPREC bits.
 
On many machines, this expression can be 1.
 
When `TRULY_NOOP_TRUNCATION' returns 1 for a pair of sizes for modes for
which `MODES_TIEABLE_P' is 0, suboptimal code can result. If this is the
case, making `TRULY_NOOP_TRUNCATION' return 0 in such cases may improve
things. */
#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1
 
/* An alias for the machine mode for pointers. On most machines, define this
to be the integer mode corresponding to the width of a hardware pointer;
`SImode' on 32-bit machine or `DImode' on 64-bit machines. On some machines
you must define this to be one of the partial integer modes, such as
`PSImode'.
 
The width of `Pmode' must be at least as large as the value of
`POINTER_SIZE'. If it is not equal, you must define the macro
`POINTERS_EXTEND_UNSIGNED' to specify how pointers are extended to `Pmode'. */
#define Pmode SImode
 
/* An alias for the machine mode used for memory references to functions being
called, in `call' RTL expressions. On most machines this should be
`QImode'. */
#define FUNCTION_MODE QImode
 
/* Define this macro to handle System V style pragmas: #pragma pack and
#pragma weak. Note, #pragma weak will only be supported if SUPPORT_WEAK is
defined.
 
Defined in svr4.h. */
#define HANDLE_SYSV_PRAGMA 1
 
/* A C expression for the maximum number of instructions to execute via
conditional execution instructions instead of a branch. A value of
BRANCH_COST+1 is the default if the machine does not use
cc0, and 1 if it does use cc0. */
#define MAX_CONDITIONAL_EXECUTE frv_condexec_insns
 
/* A C expression to modify the code described by the conditional if
information CE_INFO, possibly updating the tests in TRUE_EXPR, and
FALSE_EXPR for converting if-then and if-then-else code to conditional
instructions. Set either TRUE_EXPR or FALSE_EXPR to a null pointer if the
tests cannot be converted. */
#define IFCVT_MODIFY_TESTS(CE_INFO, TRUE_EXPR, FALSE_EXPR) \
frv_ifcvt_modify_tests (CE_INFO, &TRUE_EXPR, &FALSE_EXPR)
 
/* A C expression to modify the code described by the conditional if
information CE_INFO, for the basic block BB, possibly updating the tests in
TRUE_EXPR, and FALSE_EXPR for converting the && and || parts of if-then or
if-then-else code to conditional instructions. OLD_TRUE and OLD_FALSE are
the previous tests. Set either TRUE_EXPR or FALSE_EXPR to a null pointer if
the tests cannot be converted. */
#define IFCVT_MODIFY_MULTIPLE_TESTS(CE_INFO, BB, TRUE_EXPR, FALSE_EXPR) \
frv_ifcvt_modify_multiple_tests (CE_INFO, BB, &TRUE_EXPR, &FALSE_EXPR)
 
/* A C expression to modify the code described by the conditional if
information CE_INFO with the new PATTERN in INSN. If PATTERN is a null
pointer after the IFCVT_MODIFY_INSN macro executes, it is assumed that that
insn cannot be converted to be executed conditionally. */
#define IFCVT_MODIFY_INSN(CE_INFO, PATTERN, INSN) \
(PATTERN) = frv_ifcvt_modify_insn (CE_INFO, PATTERN, INSN)
 
/* A C expression to perform any final machine dependent modifications in
converting code to conditional execution in the code described by the
conditional if information CE_INFO. */
#define IFCVT_MODIFY_FINAL(CE_INFO) frv_ifcvt_modify_final (CE_INFO)
 
/* A C expression to cancel any machine dependent modifications in converting
code to conditional execution in the code described by the conditional if
information CE_INFO. */
#define IFCVT_MODIFY_CANCEL(CE_INFO) frv_ifcvt_modify_cancel (CE_INFO)
 
/* Initialize the extra fields provided by IFCVT_EXTRA_FIELDS. */
#define IFCVT_INIT_EXTRA_FIELDS(CE_INFO) frv_ifcvt_init_extra_fields (CE_INFO)
 
/* The definition of the following macro results in that the 2nd jump
optimization (after the 2nd insn scheduling) is minimal. It is
necessary to define when start cycle marks of insns (TImode is used
for this) is used for VLIW insn packing. Some jump optimizations
make such marks invalid. These marks are corrected for some
(minimal) optimizations. ??? Probably the macro is temporary.
Final solution could making the 2nd jump optimizations before the
2nd instruction scheduling or corrections of the marks for all jump
optimizations. Although some jump optimizations are actually
deoptimizations for VLIW (super-scalar) processors. */
 
#define MINIMAL_SECOND_JUMP_OPTIMIZATION
 
 
/* If the following macro is defined and nonzero and deterministic
finite state automata are used for pipeline hazard recognition, the
code making resource-constrained software pipelining is on. */
#define RCSP_SOFTWARE_PIPELINING 1
 
/* If the following macro is defined and nonzero and deterministic
finite state automata are used for pipeline hazard recognition, we
will try to exchange insns in queue ready to improve the schedule.
The more macro value, the more tries will be made. */
#define FIRST_CYCLE_MULTIPASS_SCHEDULING 1
 
/* The following macro is used only when value of
FIRST_CYCLE_MULTIPASS_SCHEDULING is nonzero. The more macro value,
the more tries will be made to choose better schedule. If the
macro value is zero or negative there will be no multi-pass
scheduling. */
#define FIRST_CYCLE_MULTIPASS_SCHEDULING_LOOKAHEAD frv_sched_lookahead
 
enum frv_builtins
{
FRV_BUILTIN_MAND,
FRV_BUILTIN_MOR,
FRV_BUILTIN_MXOR,
FRV_BUILTIN_MNOT,
FRV_BUILTIN_MAVEH,
FRV_BUILTIN_MSATHS,
FRV_BUILTIN_MSATHU,
FRV_BUILTIN_MADDHSS,
FRV_BUILTIN_MADDHUS,
FRV_BUILTIN_MSUBHSS,
FRV_BUILTIN_MSUBHUS,
FRV_BUILTIN_MPACKH,
FRV_BUILTIN_MQADDHSS,
FRV_BUILTIN_MQADDHUS,
FRV_BUILTIN_MQSUBHSS,
FRV_BUILTIN_MQSUBHUS,
FRV_BUILTIN_MUNPACKH,
FRV_BUILTIN_MDPACKH,
FRV_BUILTIN_MBTOH,
FRV_BUILTIN_MHTOB,
FRV_BUILTIN_MCOP1,
FRV_BUILTIN_MCOP2,
FRV_BUILTIN_MROTLI,
FRV_BUILTIN_MROTRI,
FRV_BUILTIN_MWCUT,
FRV_BUILTIN_MSLLHI,
FRV_BUILTIN_MSRLHI,
FRV_BUILTIN_MSRAHI,
FRV_BUILTIN_MEXPDHW,
FRV_BUILTIN_MEXPDHD,
FRV_BUILTIN_MMULHS,
FRV_BUILTIN_MMULHU,
FRV_BUILTIN_MMULXHS,
FRV_BUILTIN_MMULXHU,
FRV_BUILTIN_MMACHS,
FRV_BUILTIN_MMACHU,
FRV_BUILTIN_MMRDHS,
FRV_BUILTIN_MMRDHU,
FRV_BUILTIN_MQMULHS,
FRV_BUILTIN_MQMULHU,
FRV_BUILTIN_MQMULXHU,
FRV_BUILTIN_MQMULXHS,
FRV_BUILTIN_MQMACHS,
FRV_BUILTIN_MQMACHU,
FRV_BUILTIN_MCPXRS,
FRV_BUILTIN_MCPXRU,
FRV_BUILTIN_MCPXIS,
FRV_BUILTIN_MCPXIU,
FRV_BUILTIN_MQCPXRS,
FRV_BUILTIN_MQCPXRU,
FRV_BUILTIN_MQCPXIS,
FRV_BUILTIN_MQCPXIU,
FRV_BUILTIN_MCUT,
FRV_BUILTIN_MCUTSS,
FRV_BUILTIN_MWTACC,
FRV_BUILTIN_MWTACCG,
FRV_BUILTIN_MRDACC,
FRV_BUILTIN_MRDACCG,
FRV_BUILTIN_MTRAP,
FRV_BUILTIN_MCLRACC,
FRV_BUILTIN_MCLRACCA,
FRV_BUILTIN_MDUNPACKH,
FRV_BUILTIN_MBTOHE,
FRV_BUILTIN_MQXMACHS,
FRV_BUILTIN_MQXMACXHS,
FRV_BUILTIN_MQMACXHS,
FRV_BUILTIN_MADDACCS,
FRV_BUILTIN_MSUBACCS,
FRV_BUILTIN_MASACCS,
FRV_BUILTIN_MDADDACCS,
FRV_BUILTIN_MDSUBACCS,
FRV_BUILTIN_MDASACCS,
FRV_BUILTIN_MABSHS,
FRV_BUILTIN_MDROTLI,
FRV_BUILTIN_MCPLHI,
FRV_BUILTIN_MCPLI,
FRV_BUILTIN_MDCUTSSI,
FRV_BUILTIN_MQSATHS,
FRV_BUILTIN_MQLCLRHS,
FRV_BUILTIN_MQLMTHS,
FRV_BUILTIN_MQSLLHI,
FRV_BUILTIN_MQSRAHI,
FRV_BUILTIN_MHSETLOS,
FRV_BUILTIN_MHSETLOH,
FRV_BUILTIN_MHSETHIS,
FRV_BUILTIN_MHSETHIH,
FRV_BUILTIN_MHDSETS,
FRV_BUILTIN_MHDSETH,
FRV_BUILTIN_SMUL,
FRV_BUILTIN_UMUL,
FRV_BUILTIN_PREFETCH0,
FRV_BUILTIN_PREFETCH,
FRV_BUILTIN_SMASS,
FRV_BUILTIN_SMSSS,
FRV_BUILTIN_SMU,
FRV_BUILTIN_SCUTSS,
FRV_BUILTIN_ADDSS,
FRV_BUILTIN_SUBSS,
FRV_BUILTIN_SLASS,
FRV_BUILTIN_IACCreadll,
FRV_BUILTIN_IACCreadl,
FRV_BUILTIN_IACCsetll,
FRV_BUILTIN_IACCsetl,
FRV_BUILTIN_SCAN,
FRV_BUILTIN_READ8,
FRV_BUILTIN_READ16,
FRV_BUILTIN_READ32,
FRV_BUILTIN_READ64,
FRV_BUILTIN_WRITE8,
FRV_BUILTIN_WRITE16,
FRV_BUILTIN_WRITE32,
FRV_BUILTIN_WRITE64
};
#define FRV_BUILTIN_FIRST_NONMEDIA FRV_BUILTIN_SMUL
 
/* Enable prototypes on the call rtl functions. */
#define MD_CALL_PROTOTYPES 1
 
extern GTY(()) rtx frv_compare_op0; /* operand save for */
extern GTY(()) rtx frv_compare_op1; /* comparison generation */
 
#define CPU_UNITS_QUERY 1
 
#ifdef __FRV_FDPIC__
#define CRT_GET_RFIB_DATA(dbase) \
({ extern void *_GLOBAL_OFFSET_TABLE_; (dbase) = &_GLOBAL_OFFSET_TABLE_; })
#endif
 
#endif /* __FRV_H__ */
/frvbegin.c
0,0 → 1,160
/* Frv initialization file linked before all user modules
Copyright (C) 1999, 2000, 2003, 2004 Free Software Foundation, Inc.
Contributed by Red Hat, Inc.
This file is part of GCC.
GCC 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 Software Foundation * either version 2, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
 
This file was originally taken from the file crtstuff.c in the
main compiler directory, and simplified. */
 
/* As a special exception, if you link this library with other files,
some of which are compiled with GCC, to produce an executable,
this library does not by itself cause the resulting executable
to be covered by the GNU General Public License.
This exception does not however invalidate any other reasons why
the executable file might be covered by the GNU General Public License. */
 
#include "defaults.h"
#include <stddef.h>
#include "unwind-dw2-fde.h"
#include "gbl-ctors.h"
 
/* Declare a pointer to void function type. */
#define STATIC static
 
#ifdef __FRV_UNDERSCORE__
#define UNDERSCORE "_"
#else
#define UNDERSCORE ""
#endif
 
#define INIT_SECTION_NEG_ONE(SECTION, FLAGS, NAME) \
__asm__ (".section " SECTION "," FLAGS "\n\t" \
".globl " UNDERSCORE NAME "\n\t" \
".type " UNDERSCORE NAME ",@object\n\t" \
".p2align 2\n" \
UNDERSCORE NAME ":\n\t" \
".word -1\n\t" \
".previous")
 
#define INIT_SECTION(SECTION, FLAGS, NAME) \
__asm__ (".section " SECTION "," FLAGS "\n\t" \
".globl " UNDERSCORE NAME "\n\t" \
".type " UNDERSCORE NAME ",@object\n\t" \
".p2align 2\n" \
UNDERSCORE NAME ":\n\t" \
".previous")
 
/* Beginning of .ctor/.dtor sections that provides a list of constructors and
destructors to run. */
 
INIT_SECTION_NEG_ONE (".ctors", "\"aw\"", "__CTOR_LIST__");
INIT_SECTION_NEG_ONE (".dtors", "\"aw\"", "__DTOR_LIST__");
 
/* Beginning of .eh_frame section that provides all of the exception handling
tables. */
 
INIT_SECTION (".eh_frame", "\"aw\"", "__EH_FRAME_BEGIN__");
 
#if ! __FRV_FDPIC__
/* In FDPIC, the linker itself generates this. */
/* Beginning of .rofixup section that provides a list of pointers that we
need to adjust. */
 
INIT_SECTION (".rofixup", "\"a\"", "__ROFIXUP_LIST__");
#endif /* __FRV_FDPIC__ */
 
extern void __frv_register_eh(void) __attribute__((__constructor__));
extern void __frv_deregister_eh(void) __attribute__((__destructor__));
 
extern func_ptr __EH_FRAME_BEGIN__[];
 
/* Register the exception handling table as the first constructor. */
void
__frv_register_eh (void)
{
static struct object object;
if (__register_frame_info)
__register_frame_info (__EH_FRAME_BEGIN__, &object);
}
 
/* Note, do not declare __{,de}register_frame_info weak as it seems
to interfere with the pic support. */
 
/* Unregister the exception handling table as a deconstructor. */
void
__frv_deregister_eh (void)
{
static int completed = 0;
 
if (completed)
return;
 
if (__deregister_frame_info)
__deregister_frame_info (__EH_FRAME_BEGIN__);
 
completed = 1;
}
 
/* Run the global destructors. */
void
__do_global_dtors (void)
{
static func_ptr *p = __DTOR_LIST__ + 1;
while (*p)
{
p++;
(*(p-1)) ();
}
}
 
/* Run the global constructors. */
void
__do_global_ctors (void)
{
unsigned long nptrs = (unsigned long) __CTOR_LIST__[0];
unsigned i;
 
if (nptrs == (unsigned long)-1)
for (nptrs = 0; __CTOR_LIST__[nptrs + 1] != 0; nptrs++);
 
for (i = nptrs; i >= 1; i--)
__CTOR_LIST__[i] ();
 
atexit (__do_global_dtors);
}
 
/* Subroutine called automatically by `main'.
Compiling a global function named `main'
produces an automatic call to this function at the beginning.
 
For many systems, this routine calls __do_global_ctors.
For systems which support a .init section we use the .init section
to run __do_global_ctors, so we need not do anything here. */
 
void
__main (void)
{
/* Support recursive calls to `main': run initializers just once. */
static int initialized;
if (! initialized)
{
initialized = 1;
__do_global_ctors ();
}
}
/ulltod.c
0,0 → 1,4
double __ulltod (unsigned long long a)
{
return a;
}
/linux.h
0,0 → 1,75
/* Target macros for the FRV Linux port of GCC.
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2006, 2007
Free Software Foundation, Inc.
Contributed by Red Hat Inc.
 
This file is part of GCC.
 
GCC 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 Software Foundation; either version 3, or (at your
option) any later version.
 
GCC is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
#ifndef __FRV_LINUX_H__
#define __FRV_LINUX_H__
 
#undef SUBTARGET_DRIVER_SELF_SPECS
#define SUBTARGET_DRIVER_SELF_SPECS \
"%{!mno-fdpic:-mfdpic}",
 
#undef STARTFILE_SPEC
#define STARTFILE_SPEC \
"%{!shared: %{pg|p|profile:gcrt1.o%s;pie:Scrt1.o%s;:crt1.o%s}} \
crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s}"
 
#undef ENDFILE_SPEC
#define ENDFILE_SPEC \
"%{shared|pie:crtendS.o%s;:crtend.o%s} crtn.o%s"
 
#define GLIBC_DYNAMIC_LINKER "/lib/ld.so.1"
 
#undef LINK_SPEC
#define LINK_SPEC "\
%{mfdpic: -m elf32frvfd -z text} %{shared} %{pie} \
%{!shared: %{!static: \
%{rdynamic:-export-dynamic} \
%{!dynamic-linker:-dynamic-linker " LINUX_DYNAMIC_LINKER "}} \
%{static}}"
 
/* Support for compile-time default CPU. */
#define OPTION_DEFAULT_SPECS \
{"cpu", "%{!mcpu=*:-mcpu=%(VALUE)}" }
 
/* Define OS-specific predefined preprocessor macros. */
#define TARGET_OS_CPP_BUILTINS() \
do { \
builtin_define ("__gnu_linux__"); \
builtin_define_std ("linux"); \
builtin_define_std ("unix"); \
builtin_assert ("system=linux"); \
} while (0)
 
#define HAS_INIT_SECTION 1
#define INIT_SECTION_ASM_OP "\t.section .init,\"ax\""
#define FINI_SECTION_ASM_OP "\t.section .fini,\"ax\""
 
#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \
asm (SECTION_OP); \
asm ("ldi.p @(fp,4), gr15 ! call " #FUNC); \
asm (TEXT_SECTION_ASM_OP);
 
#undef INVOKE__main
 
#undef Twrite
#define Twrite __write
 
#endif /* __FRV_LINUX_H__ */
/ulltof.c
0,0 → 1,4
float __ulltof (unsigned long long a)
{
return a;
}
/cmovd.c
0,0 → 1,54
/* Move double-word library function.
Copyright (C) 2000, 2003 Free Software Foundation, Inc.
Contributed by Red Hat, Inc.
This file is part of GCC.
GCC 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 Software Foundation * either version 2, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
 
/* As a special exception, if you link this library with other files,
some of which are compiled with GCC, to produce an executable,
this library does not by itself cause the resulting executable
to be covered by the GNU General Public License.
This exception does not however invalidate any other reasons why
the executable file might be covered by the GNU General Public License. */
 
void
__cmovd (long long *dest, const long long *src, unsigned len)
{
unsigned i;
unsigned num = len >> 3;
unsigned xlen = len & ~7;
char *dest_byte = (char *)dest;
const char *src_byte = (const char *)src;
 
if (dest_byte < src_byte || dest_byte > src_byte+len)
{
for (i = 0; i < num; i++)
dest[i] = src[i];
 
while (len > xlen)
{
dest_byte[xlen] = src_byte[xlen];
xlen++;
}
}
else
{
while (len-- > 0)
dest_byte[len] = src_byte[len];
}
}
/frv.md
0,0 → 1,8319
;; Frv Machine Description
;; Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2007
;; Free Software Foundation, Inc.
;; Contributed by Red Hat, Inc.
 
;; This file is part of GCC.
 
;; GCC 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 Software Foundation; either version 3, or (at your option)
;; any later version.
 
;; GCC is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
 
;; You should have received a copy of the GNU General Public License
;; along with GCC; see the file COPYING3. If not see
;; <http://www.gnu.org/licenses/>.
 
;;- See file "rtl.def" for documentation on define_insn, match_*, et. al.
 
;; ::::::::::::::::::::
;; ::
;; :: Unspec's used
;; ::
;; ::::::::::::::::::::
 
;; GOT constants must go 12/HI/LO for the splitter to work
 
(define_constants
[(UNSPEC_BLOCKAGE 0)
(UNSPEC_CC_TO_GPR 1)
(UNSPEC_GPR_TO_CC 2)
(UNSPEC_PIC_PROLOGUE 3)
(UNSPEC_CR_LOGIC 4)
(UNSPEC_STACK_ADJUST 5)
(UNSPEC_EH_RETURN_EPILOGUE 6)
(UNSPEC_GOT 7)
(UNSPEC_LDD 8)
(UNSPEC_OPTIONAL_MEMBAR 9)
 
(UNSPEC_GETTLSOFF 200)
(UNSPEC_TLS_LOAD_GOTTLSOFF12 201)
(UNSPEC_TLS_INDIRECT_CALL 202)
(UNSPEC_TLS_TLSDESC_LDD 203)
(UNSPEC_TLS_TLSDESC_LDD_AUX 204)
(UNSPEC_TLS_TLSOFF_LD 205)
(UNSPEC_TLS_LDDI 206)
(UNSPEC_TLSOFF_HILO 207)
 
(R_FRV_GOT12 11)
(R_FRV_GOTHI 12)
(R_FRV_GOTLO 13)
(R_FRV_FUNCDESC 14)
(R_FRV_FUNCDESC_GOT12 15)
(R_FRV_FUNCDESC_GOTHI 16)
(R_FRV_FUNCDESC_GOTLO 17)
(R_FRV_FUNCDESC_VALUE 18)
(R_FRV_FUNCDESC_GOTOFF12 19)
(R_FRV_FUNCDESC_GOTOFFHI 20)
(R_FRV_FUNCDESC_GOTOFFLO 21)
(R_FRV_GOTOFF12 22)
(R_FRV_GOTOFFHI 23)
(R_FRV_GOTOFFLO 24)
(R_FRV_GPREL12 25)
(R_FRV_GPRELHI 26)
(R_FRV_GPRELLO 27)
(R_FRV_GOTTLSOFF_HI 28)
(R_FRV_GOTTLSOFF_LO 29)
(R_FRV_TLSMOFFHI 30)
(R_FRV_TLSMOFFLO 31)
(R_FRV_TLSMOFF12 32)
(R_FRV_TLSDESCHI 33)
(R_FRV_TLSDESCLO 34)
(R_FRV_GOTTLSDESCHI 35)
(R_FRV_GOTTLSDESCLO 36)
 
(GR8_REG 8)
(GR9_REG 9)
(GR14_REG 14)
;; LR_REG conflicts with definition in frv.h
(LRREG 169)
(FDPIC_REG 15)
])
 
(define_mode_macro IMODE [QI HI SI DI])
(define_mode_attr IMODEsuffix [(QI "b") (HI "h") (SI "") (DI "d")])
(define_mode_attr BREADsuffix [(QI "ub") (HI "uh") (SI "") (DI "d")])
;; ::::::::::::::::::::
;; ::
;; :: Constraints
;; ::
;; ::::::::::::::::::::
 
;; Standard Constraints
;;
;; `m' A memory operand is allowed, with any kind of address that the
;; machine supports in general.
;;
;; `o' A memory operand is allowed, but only if the address is
;; "offsettable". This means that adding a small integer (actually, the
;; width in bytes of the operand, as determined by its machine mode) may be
;; added to the address and the result is also a valid memory address.
;;
;; `V' A memory operand that is not offsettable. In other words,
;; anything that would fit the `m' constraint but not the `o' constraint.
;;
;; `<' A memory operand with autodecrement addressing (either
;; predecrement or postdecrement) is allowed.
;;
;; `>' A memory operand with autoincrement addressing (either
;; preincrement or postincrement) is allowed.
;;
;; `r' A register operand is allowed provided that it is in a general
;; register.
;;
;; `d', `a', `f', ...
;; Other letters can be defined in machine-dependent fashion to stand for
;; particular classes of registers. `d', `a' and `f' are defined on the
;; 68000/68020 to stand for data, address and floating point registers.
;;
;; `i' An immediate integer operand (one with constant value) is allowed.
;; This includes symbolic constants whose values will be known only at
;; assembly time.
;;
;; `n' An immediate integer operand with a known numeric value is allowed.
;; Many systems cannot support assembly-time constants for operands less
;; than a word wide. Constraints for these operands should use `n' rather
;; than `i'.
;;
;; 'I' First machine-dependent integer constant (6 bit signed ints).
;; 'J' Second machine-dependent integer constant (10 bit signed ints).
;; 'K' Third machine-dependent integer constant (-2048).
;; 'L' Fourth machine-dependent integer constant (16 bit signed ints).
;; 'M' Fifth machine-dependent integer constant (16 bit unsigned ints).
;; 'N' Sixth machine-dependent integer constant (-2047..-1).
;; 'O' Seventh machine-dependent integer constant (zero).
;; 'P' Eighth machine-dependent integer constant (1..2047).
;;
;; Other letters in the range `I' through `P' may be defined in a
;; machine-dependent fashion to permit immediate integer operands with
;; explicit integer values in specified ranges. For example, on the 68000,
;; `I' is defined to stand for the range of values 1 to 8. This is the
;; range permitted as a shift count in the shift instructions.
;;
;; `E' An immediate floating operand (expression code `const_double') is
;; allowed, but only if the target floating point format is the same as
;; that of the host machine (on which the compiler is running).
;;
;; `F' An immediate floating operand (expression code `const_double') is
;; allowed.
;;
;; 'G' First machine-dependent const_double.
;; 'H' Second machine-dependent const_double.
;;
;; `s' An immediate integer operand whose value is not an explicit
;; integer is allowed.
;;
;; This might appear strange; if an insn allows a constant operand with a
;; value not known at compile time, it certainly must allow any known
;; value. So why use `s' instead of `i'? Sometimes it allows better code
;; to be generated.
;;
;; For example, on the 68000 in a fullword instruction it is possible to
;; use an immediate operand; but if the immediate value is between -128 and
;; 127, better code results from loading the value into a register and
;; using the register. This is because the load into the register can be
;; done with a `moveq' instruction. We arrange for this to happen by
;; defining the letter `K' to mean "any integer outside the range -128 to
;; 127", and then specifying `Ks' in the operand constraints.
;;
;; `g' Any register, memory or immediate integer operand is allowed,
;; except for registers that are not general registers.
;;
;; `X' Any operand whatsoever is allowed, even if it does not satisfy
;; `general_operand'. This is normally used in the constraint of a
;; `match_scratch' when certain alternatives will not actually require a
;; scratch register.
;;
;; `0' Match operand 0.
;; `1' Match operand 1.
;; `2' Match operand 2.
;; `3' Match operand 3.
;; `4' Match operand 4.
;; `5' Match operand 5.
;; `6' Match operand 6.
;; `7' Match operand 7.
;; `8' Match operand 8.
;; `9' Match operand 9.
;;
;; An operand that matches the specified operand number is allowed. If a
;; digit is used together with letters within the same alternative, the
;; digit should come last.
;;
;; This is called a "matching constraint" and what it really means is that
;; the assembler has only a single operand that fills two roles considered
;; separate in the RTL insn. For example, an add insn has two input
;; operands and one output operand in the RTL, but on most CISC machines an
;; add instruction really has only two operands, one of them an
;; input-output operand:
;;
;; addl #35,r12
;;
;; Matching constraints are used in these circumstances. More precisely,
;; the two operands that match must include one input-only operand and one
;; output-only operand. Moreover, the digit must be a smaller number than
;; the number of the operand that uses it in the constraint.
;;
;; For operands to match in a particular case usually means that they are
;; identical-looking RTL expressions. But in a few special cases specific
;; kinds of dissimilarity are allowed. For example, `*x' as an input
;; operand will match `*x++' as an output operand. For proper results in
;; such cases, the output template should always use the output-operand's
;; number when printing the operand.
;;
;; `p' An operand that is a valid memory address is allowed. This is for
;; "load address" and "push address" instructions.
;;
;; `p' in the constraint must be accompanied by `address_operand' as the
;; predicate in the `match_operand'. This predicate interprets the mode
;; specified in the `match_operand' as the mode of the memory reference for
;; which the address would be valid.
;;
;; `Q` First non constant, non register machine-dependent insns
;; `R` Second non constant, non register machine-dependent insns
;; `S` Third non constant, non register machine-dependent insns
;; `T` Fourth non constant, non register machine-dependent insns
;; `U` Fifth non constant, non register machine-dependent insns
;;
;; Letters in the range `Q' through `U' may be defined in a
;; machine-dependent fashion to stand for arbitrary operand types. The
;; machine description macro `EXTRA_CONSTRAINT' is passed the operand as
;; its first argument and the constraint letter as its second operand.
;;
;; A typical use for this would be to distinguish certain types of memory
;; references that affect other insn operands.
;;
;; Do not define these constraint letters to accept register references
;; (`reg'); the reload pass does not expect this and would not handle it
;; properly.
 
;; Multiple Alternative Constraints
;; `?' Disparage slightly the alternative that the `?' appears in, as a
;; choice when no alternative applies exactly. The compiler regards this
;; alternative as one unit more costly for each `?' that appears in it.
;;
;; `!' Disparage severely the alternative that the `!' appears in. This
;; alternative can still be used if it fits without reloading, but if
;; reloading is needed, some other alternative will be used.
 
;; Constraint modifiers
;; `=' Means that this operand is write-only for this instruction: the
;; previous value is discarded and replaced by output data.
;;
;; `+' Means that this operand is both read and written by the
;; instruction.
;;
;; When the compiler fixes up the operands to satisfy the constraints, it
;; needs to know which operands are inputs to the instruction and which are
;; outputs from it. `=' identifies an output; `+' identifies an operand
;; that is both input and output; all other operands are assumed to be
;; input only.
;;
;; `&' Means (in a particular alternative) that this operand is written
;; before the instruction is finished using the input operands. Therefore,
;; this operand may not lie in a register that is used as an input operand
;; or as part of any memory address.
;;
;; `&' applies only to the alternative in which it is written. In
;; constraints with multiple alternatives, sometimes one alternative
;; requires `&' while others do not.
;;
;; `&' does not obviate the need to write `='.
;;
;; `%' Declares the instruction to be commutative for this operand and the
;; following operand. This means that the compiler may interchange the two
;; operands if that is the cheapest way to make all operands fit the
;; constraints. This is often used in patterns for addition instructions
;; that really have only two operands: the result must go in one of the
;; arguments.
;;
;; `#' Says that all following characters, up to the next comma, are to be
;; ignored as a constraint. They are significant only for choosing
;; register preferences.
;;
;; `*' Says that the following character should be ignored when choosing
;; register preferences. `*' has no effect on the meaning of the
;; constraint as a constraint, and no effect on reloading.
 
;; ::::::::::::::::::::
;; ::
;; :: Attributes
;; ::
;; ::::::::::::::::::::
 
;; The `define_attr' expression is used to define each attribute required by
;; the target machine. It looks like:
;;
;; (define_attr NAME LIST-OF-VALUES DEFAULT)
 
;; NAME is a string specifying the name of the attribute being defined.
 
;; LIST-OF-VALUES is either a string that specifies a comma-separated list of
;; values that can be assigned to the attribute, or a null string to indicate
;; that the attribute takes numeric values.
 
;; DEFAULT is an attribute expression that gives the value of this attribute
;; for insns that match patterns whose definition does not include an explicit
;; value for this attribute.
 
;; For each defined attribute, a number of definitions are written to the
;; `insn-attr.h' file. For cases where an explicit set of values is specified
;; for an attribute, the following are defined:
 
;; * A `#define' is written for the symbol `HAVE_ATTR_NAME'.
;;
;; * An enumeral class is defined for `attr_NAME' with elements of the
;; form `UPPER-NAME_UPPER-VALUE' where the attribute name and value are first
;; converted to upper case.
;;
;; * A function `get_attr_NAME' is defined that is passed an insn and
;; returns the attribute value for that insn.
 
;; For example, if the following is present in the `md' file:
;;
;; (define_attr "type" "branch,fp,load,store,arith" ...)
;;
;; the following lines will be written to the file `insn-attr.h'.
;;
;; #define HAVE_ATTR_type
;; enum attr_type {TYPE_BRANCH, TYPE_FP, TYPE_LOAD, TYPE_STORE, TYPE_ARITH};
;; extern enum attr_type get_attr_type ();
 
;; If the attribute takes numeric values, no `enum' type will be defined and
;; the function to obtain the attribute's value will return `int'.
 
(define_attr "length" "" (const_int 4))
 
;; Processor type -- this attribute must exactly match the processor_type
;; enumeration in frv-protos.h.
 
(define_attr "cpu" "generic,fr550,fr500,fr450,fr405,fr400,fr300,simple,tomcat"
(const (symbol_ref "frv_cpu_type")))
 
;; Attribute is "yes" for branches and jumps that span too great a distance
;; to be implemented in the most natural way. Such instructions will use
;; a call instruction in some way.
 
(define_attr "far_jump" "yes,no" (const_string "no"))
 
;; Instruction type
;; "unknown" must come last.
(define_attr "type"
"int,sethi,setlo,mul,div,gload,gstore,fload,fstore,movfg,movgf,macc,scan,cut,branch,jump,jumpl,call,spr,trap,fnop,fsconv,fsadd,fscmp,fsmul,fsmadd,fsdiv,sqrt_single,fdconv,fdadd,fdcmp,fdmul,fdmadd,fddiv,sqrt_double,mnop,mlogic,maveh,msath,maddh,mqaddh,mpackh,munpackh,mdpackh,mbhconv,mrot,mshift,mexpdhw,mexpdhd,mwcut,mmulh,mmulxh,mmach,mmrdh,mqmulh,mqmulxh,mqmach,mcpx,mqcpx,mcut,mclracc,mclracca,mdunpackh,mbhconve,mrdacc,mwtacc,maddacc,mdaddacc,mabsh,mdrot,mcpl,mdcut,mqsath,mqlimh,mqshift,mset,ccr,multi,load_or_call,unknown"
(const_string "unknown"))
 
(define_attr "acc_group" "none,even,odd"
(symbol_ref "frv_acc_group (insn)"))
;; Scheduling and Packing Overview
;; -------------------------------
;;
;; FR-V instructions are divided into five groups: integer, floating-point,
;; media, branch and control. Each group is associated with a separate set
;; of processing units, the number and behavior of which depend on the target
;; target processor. Integer units have names like I0 and I1, floating-point
;; units have names like F0 and F1, and so on.
;;
;; Each member of the FR-V family has its own restrictions on which
;; instructions can issue to which units. For example, some processors
;; allow loads to issue to I0 or I1 while others only allow them to issue
;; to I0. As well as these processor-specific restrictions, there is a
;; general rule that an instruction can only issue to unit X + 1 if an
;; instruction in the same packet issued to unit X.
;;
;; Sometimes the only way to honor these restrictions is by adding nops
;; to a packet. For example, on the fr550, media instructions that access
;; ACC4-7 can only issue to M1 or M3. It is therefore only possible to
;; execute these instructions by packing them with something that issues
;; to M0. When no useful M0 instruction exists, an "mnop" can be used
;; instead.
;;
;; Having decided which instructions should issue to which units, the packet
;; should be ordered according to the following template:
;;
;; I0 F0/M0 I1 F1/M1 .... B0 B1 ...
;;
;; Note that VLIW packets execute strictly in parallel. Every instruction
;; in the packet will stall until all input operands are ready. These
;; operands are then read simultaneously before any registers are modified.
;; This means that it's OK to have write-after-read hazards between
;; instructions in the same packet, even if the write is listed earlier
;; than the read.
;;
;; Three gcc passes are involved in generating VLIW packets:
;;
;; (1) The scheduler. This pass uses the standard scheduling code and
;; behaves in much the same way as it would for a superscalar RISC
;; architecture.
;;
;; (2) frv_reorg. This pass inserts nops into packets in order to meet
;; the processor's issue requirements. It also has code to optimize
;; the type of padding used to align labels.
;;
;; (3) frv_pack_insns. The final packing phase, which puts the
;; instructions into assembly language order according to the
;; "I0 F0/M0 ..." template above.
;;
;; In the ideal case, these three passes will agree on which instructions
;; should be packed together, but this won't always happen. In particular:
;;
;; (a) (2) might not pack predicated instructions in the same way as (1).
;; The scheduler tries to schedule predicated instructions for the
;; worst case, assuming the predicate is true. However, if we have
;; something like a predicated load, it isn't always possible to
;; fill the load delay with useful instructions. (2) should then
;; pack the user of the loaded value as aggressively as possible,
;; in order to optimize the case when the predicate is false.
;; See frv_pack_insn_p for more details.
;;
;; (b) The final shorten_branches pass runs between (2) and (3).
;; Since (2) inserts nops, it is possible that some branches
;; that were thought to be in range during (2) turned out to
;; out-of-range in (3).
;;
;; All three passes use DFAs to model issue restrictions. The main
;; question that the DFAs are supposed to answer is simply: can these
;; instructions be packed together? The DFAs are not responsible for
;; assigning instructions to execution units; that's the job of
;; frv_sort_insn_group, see below for details.
;;
;; To get the best results, the DFAs should try to allow packets to
;; be built in every possible order. This gives the scheduler more
;; flexibility, removing the need for things like multipass lookahead.
;; It also means we can take more advantage of inter-packet dependencies.
;;
;; For example, suppose we're compiling for the fr400 and we have:
;;
;; addi gr4,#1,gr5
;; ldi @(gr6,gr0),gr4
;;
;; We can pack these instructions together by assigning the load to I0 and
;; the addition to I1. However, because of the anti dependence between the
;; two instructions, the scheduler must schedule the addition first.
;; We should generally get better schedules if the DFA allows both
;; (ldi, addi) and (addi, ldi), leaving the final packing pass to
;; reorder the packet where appropriate.
;;
;; Almost all integer instructions can issue to any unit in the range I0
;; to Ix, where the value of "x" depends on the type of instruction and
;; on the target processor. The rules for other instruction groups are
;; usually similar.
;;
;; When the restrictions are as regular as this, we can get the desired
;; behavior by claiming the DFA unit associated with the highest unused
;; execution unit. For example, if an instruction can issue to I0 or I1,
;; the DFA first tries to take the DFA unit associated with I1, and will
;; only take I0's unit if I1 isn't free. (Note that, as mentioned above,
;; the DFA does not assign instructions to units. An instruction that
;; claims DFA unit I1 will not necessarily issue to I1 in the final packet.)
;;
;; There are some cases, such as the fr550 media restriction mentioned
;; above, where the rule is not as simple as "any unit between 0 and X".
;; Even so, allocating higher units first brings us close to the ideal.
;;
;; Having divided instructions into packets, passes (2) and (3) must
;; assign instructions to specific execution units. They do this using
;; the following algorithm:
;;
;; 1. Partition the instructions into groups (integer, float/media, etc.)
;;
;; 2. For each group of instructions:
;;
;; (a) Issue each instruction in the reset DFA state and use the
;; DFA cpu_unit_query interface to find out which unit it picks
;; first.
;;
;; (b) Sort the instructions into ascending order of picked units.
;; Instructions that pick I1 first come after those that pick
;; I0 first, and so on. Let S be the sorted sequence and S[i]
;; be the ith element of it (counting from zero).
;;
;; (c) If this is the control or branch group, goto (i)
;;
;; (d) Find the largest L such that S[0]...S[L-1] can be issued
;; consecutively from the reset state and such that the DFA
;; claims unit X when S[X] is added. Let D be the DFA state
;; after instructions S[0]...S[L-1] have been issued.
;;
;; (e) If L is the length of S, goto (i)
;;
;; (f) Let U be the number of units belonging to this group and #S be
;; the length of S. Create a new sequence S' by concatenating
;; S[L]...S[#S-1] and (U - #S) nops.
;;
;; (g) For each permutation S'' of S', try issuing S'' from last to
;; first, starting with state D. See if the DFA claims unit
;; X + L when each S''[X] is added. If so, set S to the
;; concatenation of S[0]...S[L-1] and S'', then goto (i).
;;
;; (h) If (g) found no permutation, abort.
;;
;; (i) S is now the sorted sequence for this group, meaning that S[X]
;; issues to unit X. Trim any unwanted nops from the end of S.
;;
;; The sequence calculated by (b) is trivially correct for control
;; instructions since they can't be packed. It is also correct for branch
;; instructions due to their simple issue requirements. For integer and
;; floating-point/media instructions, the sequence calculated by (b) is
;; often the correct answer; the rest of the algorithm is optimized for
;; the case in which it is correct.
;;
;; If there were no irregularities in the issue restrictions then step
;; (d) would not be needed. It is mainly there to cope with the fr550
;; integer restrictions, where a store can issue to I1, but only if a store
;; also issues to I0. (Note that if a packet has two stores, they will be
;; at the beginning of the sequence calculated by (b).) It also copes
;; with fr400 M-2 instructions, which must issue to M0, and which cannot
;; be issued together with an mnop in M1.
;;
;; Step (g) is the main one for integer and float/media instructions.
;; The first permutation it tries is S' itself (because, as noted above,
;; the sequence calculated by (b) is often correct). If S' doesn't work,
;; the implementation tries varying the beginning of the sequence first.
;; Thus the nops towards the end of the sequence will only move to lower
;; positions if absolutely necessary.
;;
;; The algorithm is theoretically exponential in the number of instructions
;; in a group, although it's only O(n log(n)) if the sequence calculated by
;; (b) is acceptable. In practice, the algorithm completes quickly even
;; in the rare cases where (g) needs to try other permutations.
(define_automaton "integer, float_media, branch, control, idiv, div")
 
;; The main issue units. Note that not all units are available on
;; all processors.
(define_query_cpu_unit "i0,i1,i2,i3" "integer")
(define_query_cpu_unit "f0,f1,f2,f3" "float_media")
(define_query_cpu_unit "b0,b1" "branch")
(define_query_cpu_unit "c" "control")
 
;; Division units.
(define_cpu_unit "idiv1,idiv2" "idiv")
(define_cpu_unit "div1,div2,root" "div")
 
;; Control instructions cannot be packed with others.
(define_reservation "control" "i0+i1+i2+i3+f0+f1+f2+f3+b0+b1")
 
;; Generic reservation for control insns
(define_insn_reservation "control" 1
(eq_attr "type" "trap,spr,unknown,multi")
"c + control")
 
;; Reservation for relaxable calls to gettlsoff.
(define_insn_reservation "load_or_call" 3
(eq_attr "type" "load_or_call")
"c + control")
 
;; ::::::::::::::::::::
;; ::
;; :: Generic/FR500 scheduler description
;; ::
;; ::::::::::::::::::::
 
;; Integer insns
;; Synthetic units used to describe issue restrictions.
(define_automaton "fr500_integer")
(define_cpu_unit "fr500_load0,fr500_load1,fr500_store0" "fr500_integer")
(exclusion_set "fr500_load0,fr500_load1" "fr500_store0")
 
(define_bypass 0 "fr500_i1_sethi" "fr500_i1_setlo")
(define_insn_reservation "fr500_i1_sethi" 1
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "sethi"))
"i1|i0")
 
(define_insn_reservation "fr500_i1_setlo" 1
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "setlo"))
"i1|i0")
 
(define_insn_reservation "fr500_i1_int" 1
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "int"))
"i1|i0")
 
(define_insn_reservation "fr500_i1_mul" 3
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "mul"))
"i1|i0")
 
(define_insn_reservation "fr500_i1_div" 19
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "div"))
"(i1|i0),(idiv1*18|idiv2*18)")
 
(define_insn_reservation "fr500_i2" 4
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "gload,fload"))
"(i1|i0) + (fr500_load0|fr500_load1)")
 
(define_insn_reservation "fr500_i3" 0
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "gstore,fstore"))
"i0 + fr500_store0")
 
(define_insn_reservation "fr500_i4" 3
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "movgf,movfg"))
"i0")
 
(define_insn_reservation "fr500_i5" 0
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "jumpl"))
"i0")
 
;;
;; Branch-instructions
;;
(define_insn_reservation "fr500_branch" 0
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "jump,branch,ccr"))
"b1|b0")
 
(define_insn_reservation "fr500_call" 0
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "call"))
"b0")
 
;; Floating point insns. The default latencies are for non-media
;; instructions; media instructions incur an extra cycle.
 
(define_bypass 4 "fr500_farith" "fr500_m1,fr500_m2,fr500_m3,
fr500_m4,fr500_m5,fr500_m6")
(define_insn_reservation "fr500_farith" 3
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "fnop,fsconv,fsadd,fsmul,fsmadd,fdconv,fdadd,fdmul,fdmadd"))
"(f1|f0)")
 
(define_insn_reservation "fr500_fcmp" 4
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "fscmp,fdcmp"))
"(f1|f0)")
 
(define_bypass 11 "fr500_fdiv" "fr500_m1,fr500_m2,fr500_m3,
fr500_m4,fr500_m5,fr500_m6")
(define_insn_reservation "fr500_fdiv" 10
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "fsdiv,fddiv"))
"(f1|f0),(div1*9 | div2*9)")
 
(define_bypass 16 "fr500_froot" "fr500_m1,fr500_m2,fr500_m3,
fr500_m4,fr500_m5,fr500_m6")
(define_insn_reservation "fr500_froot" 15
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "sqrt_single,sqrt_double"))
"(f1|f0) + root*15")
 
;; Media insns. Conflict table is as follows:
;;
;; M1 M2 M3 M4 M5 M6
;; M1 - - - - - -
;; M2 - - - - X X
;; M3 - - - - X X
;; M4 - - - - - X
;; M5 - X X - X X
;; M6 - X X X X X
;;
;; where X indicates an invalid combination.
;;
;; Target registers are as follows:
;;
;; M1 : FPRs
;; M2 : FPRs
;; M3 : ACCs
;; M4 : ACCs
;; M5 : FPRs
;; M6 : ACCs
;;
;; The default FPR latencies are for integer instructions.
;; Floating-point instructions need one cycle more and media
;; instructions need one cycle less.
(define_automaton "fr500_media")
(define_cpu_unit "fr500_m2_0,fr500_m2_1" "fr500_media")
(define_cpu_unit "fr500_m3_0,fr500_m3_1" "fr500_media")
(define_cpu_unit "fr500_m4_0,fr500_m4_1" "fr500_media")
(define_cpu_unit "fr500_m5" "fr500_media")
(define_cpu_unit "fr500_m6" "fr500_media")
 
(exclusion_set "fr500_m5,fr500_m6" "fr500_m2_0,fr500_m2_1,
fr500_m3_0,fr500_m3_1")
(exclusion_set "fr500_m6" "fr500_m4_0,fr500_m4_1,fr500_m5")
 
(define_bypass 2 "fr500_m1" "fr500_m1,fr500_m2,fr500_m3,
fr500_m4,fr500_m5,fr500_m6")
(define_bypass 4 "fr500_m1" "fr500_farith,fr500_fcmp,fr500_fdiv,fr500_froot")
(define_insn_reservation "fr500_m1" 3
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "mnop,mlogic,maveh,msath,maddh,mqaddh"))
"(f1|f0)")
 
(define_bypass 2 "fr500_m2" "fr500_m1,fr500_m2,fr500_m3,
fr500_m4,fr500_m5,fr500_m6")
(define_bypass 4 "fr500_m2" "fr500_farith,fr500_fcmp,fr500_fdiv,fr500_froot")
(define_insn_reservation "fr500_m2" 3
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "mrdacc,mpackh,munpackh,mbhconv,mrot,mshift,mexpdhw,mexpdhd,mwcut,mcut,mdunpackh,mbhconve"))
"(f1|f0) + (fr500_m2_0|fr500_m2_1)")
 
(define_bypass 1 "fr500_m3" "fr500_m4")
(define_insn_reservation "fr500_m3" 2
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "mclracc,mwtacc"))
"(f1|f0) + (fr500_m3_0|fr500_m3_1)")
 
(define_bypass 1 "fr500_m4" "fr500_m4")
(define_insn_reservation "fr500_m4" 2
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "mmulh,mmulxh,mmach,mmrdh,mqmulh,mqmulxh,mqmach,mcpx,mqcpx"))
"(f1|f0) + (fr500_m4_0|fr500_m4_1)")
 
(define_bypass 2 "fr500_m5" "fr500_m1,fr500_m2,fr500_m3,
fr500_m4,fr500_m5,fr500_m6")
(define_bypass 4 "fr500_m5" "fr500_farith,fr500_fcmp,fr500_fdiv,fr500_froot")
(define_insn_reservation "fr500_m5" 3
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "mdpackh"))
"(f1|f0) + fr500_m5")
 
(define_bypass 1 "fr500_m6" "fr500_m4")
(define_insn_reservation "fr500_m6" 2
(and (eq_attr "cpu" "generic,fr500,tomcat")
(eq_attr "type" "mclracca"))
"(f1|f0) + fr500_m6")
 
;; ::::::::::::::::::::
;; ::
;; :: FR400 scheduler description
;; ::
;; ::::::::::::::::::::
 
;; Category 2 media instructions use both media units, but can be packed
;; with non-media instructions. Use fr400_m1unit to claim the M1 unit
;; without claiming a slot.
 
;; Name Class Units Latency
;; ==== ===== ===== =======
;; int I1 I0/I1 1
;; sethi I1 I0/I1 0 -- does not interfere with setlo
;; setlo I1 I0/I1 1
;; mul I1 I0 3 (*)
;; div I1 I0 20 (*)
;; gload I2 I0 4 (*)
;; fload I2 I0 4 -- only 3 if read by a media insn
;; gstore I3 I0 0 -- provides no result
;; fstore I3 I0 0 -- provides no result
;; movfg I4 I0 3 (*)
;; movgf I4 I0 3 (*)
;; jumpl I5 I0 0 -- provides no result
;;
;; (*) The results of these instructions can be read one cycle earlier
;; than indicated. The penalty given is for instructions with write-after-
;; write dependencies.
 
;; The FR400 can only do loads and stores in I0, so we there's no danger
;; of memory unit collision in the same packet. There's only one divide
;; unit too.
 
(define_automaton "fr400_integer")
(define_cpu_unit "fr400_mul" "fr400_integer")
 
(define_insn_reservation "fr400_i1_int" 1
(and (eq_attr "cpu" "fr400,fr405,fr450")
(eq_attr "type" "int"))
"i1|i0")
 
(define_bypass 0 "fr400_i1_sethi" "fr400_i1_setlo")
(define_insn_reservation "fr400_i1_sethi" 1
(and (eq_attr "cpu" "fr400,fr405,fr450")
(eq_attr "type" "sethi"))
"i1|i0")
 
(define_insn_reservation "fr400_i1_setlo" 1
(and (eq_attr "cpu" "fr400,fr405,fr450")
(eq_attr "type" "setlo"))
"i1|i0")
 
;; 3 is the worst case (write-after-write hazard).
(define_insn_reservation "fr400_i1_mul" 3
(and (eq_attr "cpu" "fr400,fr405")
(eq_attr "type" "mul"))
"i0 + fr400_mul")
 
(define_insn_reservation "fr450_i1_mul" 2
(and (eq_attr "cpu" "fr450")
(eq_attr "type" "mul"))
"i0 + fr400_mul")
 
(define_bypass 1 "fr400_i1_macc" "fr400_i1_macc")
(define_insn_reservation "fr400_i1_macc" 2
(and (eq_attr "cpu" "fr405,fr450")
(eq_attr "type" "macc"))
"(i0|i1) + fr400_mul")
 
(define_insn_reservation "fr400_i1_scan" 1
(and (eq_attr "cpu" "fr400,fr405,fr450")
(eq_attr "type" "scan"))
"i0")
 
(define_insn_reservation "fr400_i1_cut" 2
(and (eq_attr "cpu" "fr405,fr450")
(eq_attr "type" "cut"))
"i0 + fr400_mul")
 
;; 20 is for a write-after-write hazard.
(define_insn_reservation "fr400_i1_div" 20
(and (eq_attr "cpu" "fr400,fr405")
(eq_attr "type" "div"))
"i0 + idiv1*19")
 
(define_insn_reservation "fr450_i1_div" 19
(and (eq_attr "cpu" "fr450")
(eq_attr "type" "div"))
"i0 + idiv1*19")
 
;; 4 is for a write-after-write hazard.
(define_insn_reservation "fr400_i2" 4
(and (eq_attr "cpu" "fr400,fr405")
(eq_attr "type" "gload,fload"))
"i0")
 
(define_insn_reservation "fr450_i2_gload" 3
(and (eq_attr "cpu" "fr450")
(eq_attr "type" "gload"))
"i0")
 
;; 4 is for a write-after-write hazard.
(define_insn_reservation "fr450_i2_fload" 4
(and (eq_attr "cpu" "fr450")
(eq_attr "type" "fload"))
"i0")
 
(define_insn_reservation "fr400_i3" 0
(and (eq_attr "cpu" "fr400,fr405,fr450")
(eq_attr "type" "gstore,fstore"))
"i0")
 
;; 3 is for a write-after-write hazard.
(define_insn_reservation "fr400_i4" 3
(and (eq_attr "cpu" "fr400,fr405")
(eq_attr "type" "movfg,movgf"))
"i0")
 
(define_insn_reservation "fr450_i4_movfg" 2
(and (eq_attr "cpu" "fr450")
(eq_attr "type" "movfg"))
"i0")
 
;; 3 is for a write-after-write hazard.
(define_insn_reservation "fr450_i4_movgf" 3
(and (eq_attr "cpu" "fr450")
(eq_attr "type" "movgf"))
"i0")
 
(define_insn_reservation "fr400_i5" 0
(and (eq_attr "cpu" "fr400,fr405,fr450")
(eq_attr "type" "jumpl"))
"i0")
 
;; The bypass between FPR loads and media instructions, described above.
 
(define_bypass 3
"fr400_i2"
"fr400_m1_1,fr400_m1_2,\
fr400_m2_1,fr400_m2_2,\
fr400_m3_1,fr400_m3_2,\
fr400_m4_1,fr400_m4_2,\
fr400_m5")
 
;; The branch instructions all use the B unit and produce no result.
 
(define_insn_reservation "fr400_b" 0
(and (eq_attr "cpu" "fr400,fr405,fr450")
(eq_attr "type" "jump,branch,ccr,call"))
"b0")
 
;; FP->FP moves are marked as "fsconv" instructions in the define_insns
;; below, but are implemented on the FR400 using "mlogic" instructions.
;; It's easier to class "fsconv" as a "m1:1" instruction than provide
;; separate define_insns for the FR400.
 
;; M1 instructions store their results in FPRs. Any instruction can read
;; the result in the following cycle, so no penalty occurs.
 
(define_automaton "fr400_media")
(define_cpu_unit "fr400_m1a,fr400_m1b,fr400_m2a" "fr400_media")
(exclusion_set "fr400_m1a,fr400_m1b" "fr400_m2a")
 
(define_reservation "fr400_m1" "(f1|f0) + (fr400_m1a|fr400_m1b)")
(define_reservation "fr400_m2" "f0 + fr400_m2a")
 
(define_insn_reservation "fr400_m1_1" 1
(and (eq_attr "cpu" "fr400,fr405")
(eq_attr "type" "fsconv,mnop,mlogic,maveh,msath,maddh,mabsh,mset"))
"fr400_m1")
 
(define_insn_reservation "fr400_m1_2" 1
(and (eq_attr "cpu" "fr400,fr405")
(eq_attr "type" "mqaddh,mqsath,mqlimh,mqshift"))
"fr400_m2")
 
;; M2 instructions store their results in accumulators, which are read
;; by M2 or M4 media commands. M2 instructions can read the results in
;; the following cycle, but M4 instructions must wait a cycle more.
 
(define_bypass 1
"fr400_m2_1,fr400_m2_2"
"fr400_m2_1,fr400_m2_2")
 
(define_insn_reservation "fr400_m2_1" 2
(and (eq_attr "cpu" "fr400,fr405")
(eq_attr "type" "mmulh,mmulxh,mmach,mmrdh,mcpx,maddacc"))
"fr400_m1")
 
(define_insn_reservation "fr400_m2_2" 2
(and (eq_attr "cpu" "fr400,fr405")
(eq_attr "type" "mqmulh,mqmulxh,mqmach,mqcpx,mdaddacc"))
"fr400_m2")
 
;; For our purposes, there seems to be little real difference between
;; M1 and M3 instructions. Keep them separate anyway in case the distinction
;; is needed later.
 
(define_insn_reservation "fr400_m3_1" 1
(and (eq_attr "cpu" "fr400,fr405")
(eq_attr "type" "mpackh,mrot,mshift,mexpdhw"))
"fr400_m1")
 
(define_insn_reservation "fr400_m3_2" 1
(and (eq_attr "cpu" "fr400,fr405")
(eq_attr "type" "munpackh,mdpackh,mbhconv,mexpdhd,mwcut,mdrot,mcpl"))
"fr400_m2")
 
;; M4 instructions write to accumulators or FPRs. MOVFG and STF
;; instructions can read an FPR result in the following cycle, but
;; M-unit instructions must wait a cycle more for either kind of result.
 
(define_bypass 1 "fr400_m4_1,fr400_m4_2" "fr400_i3,fr400_i4")
 
(define_insn_reservation "fr400_m4_1" 2
(and (eq_attr "cpu" "fr400,fr405")
(eq_attr "type" "mrdacc,mcut,mclracc"))
"fr400_m1")
 
(define_insn_reservation "fr400_m4_2" 2
(and (eq_attr "cpu" "fr400,fr405")
(eq_attr "type" "mclracca,mdcut"))
"fr400_m2")
 
;; M5 instructions always incur a 1-cycle penalty.
 
(define_insn_reservation "fr400_m5" 2
(and (eq_attr "cpu" "fr400,fr405")
(eq_attr "type" "mwtacc"))
"fr400_m2")
 
;; ::::::::::::::::::::
;; ::
;; :: FR450 media scheduler description
;; ::
;; ::::::::::::::::::::
 
;; The FR451 media restrictions are similar to the FR400's, but not as
;; strict and not as regular. There are 6 categories with the following
;; restrictions:
;;
;; M1
;; M-1 M-2 M-3 M-4 M-5 M-6
;; M-1: x x x
;; M-2: x x x x x x
;; M0 M-3: x x x
;; M-4: x x x x
;; M-5: x x x
;; M-6: x x x x x x
;;
;; where "x" indicates a conflict.
;;
;; There is no difference between M-1 and M-3 as far as issue
;; restrictions are concerned, so they are combined as "m13".
 
;; Units for odd-numbered categories. There can be two of these
;; in a packet.
(define_cpu_unit "fr450_m13a,fr450_m13b" "float_media")
(define_cpu_unit "fr450_m5a,fr450_m5b" "float_media")
 
;; Units for even-numbered categories. There can only be one per packet.
(define_cpu_unit "fr450_m2a,fr450_m4a,fr450_m6a" "float_media")
 
;; Enforce the restriction matrix above.
(exclusion_set "fr450_m2a,fr450_m4a,fr450_m6a" "fr450_m13a,fr450_m13b")
(exclusion_set "fr450_m2a,fr450_m6a" "fr450_m5a,fr450_m5b")
(exclusion_set "fr450_m4a,fr450_m6a" "fr450_m2a")
 
(define_reservation "fr450_m13" "(f1|f0) + (fr450_m13a|fr450_m13b)")
(define_reservation "fr450_m2" "f0 + fr450_m2a")
(define_reservation "fr450_m4" "f0 + fr450_m4a")
(define_reservation "fr450_m5" "(f1|f0) + (fr450_m5a|fr450_m5b)")
(define_reservation "fr450_m6" "(f0|f1) + fr450_m6a")
 
;; MD-1, MD-3 and MD-8 instructions, which are the same as far
;; as scheduling is concerned. The inputs and outputs are FPRs.
;; Instructions that have 32-bit inputs and outputs belong to M-1 while
;; the rest belong to M-2.
;;
;; ??? Arithmetic shifts (MD-6) have an extra cycle latency, but we don't
;; make the distinction between them and logical shifts.
(define_insn_reservation "fr450_md138_1" 1
(and (eq_attr "cpu" "fr450")
(eq_attr "type" "fsconv,mnop,mlogic,maveh,msath,maddh,mabsh,mset,
mrot,mshift,mexpdhw,mpackh"))
"fr450_m13")
 
(define_insn_reservation "fr450_md138_2" 1
(and (eq_attr "cpu" "fr450")
(eq_attr "type" "mqaddh,mqsath,mqlimh,
mdrot,mwcut,mqshift,mexpdhd,
munpackh,mdpackh,mbhconv,mcpl"))
"fr450_m2")
 
;; MD-2 instructions. These take FPR or ACC inputs and produce an ACC output.
;; Instructions that write to double ACCs belong to M-3 while those that write
;; to quad ACCs belong to M-4.
(define_insn_reservation "fr450_md2_3" 2
(and (eq_attr "cpu" "fr450")
(eq_attr "type" "mmulh,mmach,mcpx,mmulxh,mmrdh,maddacc"))
"fr450_m13")
 
(define_insn_reservation "fr450_md2_4" 2
(and (eq_attr "cpu" "fr450")
(eq_attr "type" "mqmulh,mqmach,mqcpx,mqmulxh,mdaddacc"))
"fr450_m4")
 
;; Another MD-2 instruction can use the result on the following cycle.
(define_bypass 1 "fr450_md2_3,fr450_md2_4" "fr450_md2_3,fr450_md2_4")
 
;; MD-4 instructions that write to ACCs.
(define_insn_reservation "fr450_md4_3" 2
(and (eq_attr "cpu" "fr450")
(eq_attr "type" "mclracc"))
"fr450_m13")
 
(define_insn_reservation "fr450_md4_4" 3
(and (eq_attr "cpu" "fr450")
(eq_attr "type" "mclracca"))
"fr450_m4")
 
;; MD-4 instructions that write to FPRs.
(define_insn_reservation "fr450_md4_1" 2
(and (eq_attr "cpu" "fr450")
(eq_attr "type" "mcut"))
"fr450_m13")
 
(define_insn_reservation "fr450_md4_5" 2
(and (eq_attr "cpu" "fr450")
(eq_attr "type" "mrdacc"))
"fr450_m5")
 
(define_insn_reservation "fr450_md4_6" 2
(and (eq_attr "cpu" "fr450")
(eq_attr "type" "mdcut"))
"fr450_m6")
 
;; Integer instructions can read the FPR result of an MD-4 instruction on
;; the following cycle.
(define_bypass 1 "fr450_md4_1,fr450_md4_5,fr450_md4_6"
"fr400_i3,fr450_i4_movfg")
 
;; MD-5 instructions, which belong to M-3. They take FPR inputs and
;; write to ACCs.
(define_insn_reservation "fr450_md5_3" 2
(and (eq_attr "cpu" "fr450")
(eq_attr "type" "mwtacc"))
"fr450_m13")
 
;; ::::::::::::::::::::
;; ::
;; :: FR550 scheduler description
;; ::
;; ::::::::::::::::::::
 
;; Prevent loads and stores from being issued in the same packet.
;; These units must go into the generic "integer" reservation because
;; of the constraints on fr550_store0 and fr550_store1.
(define_cpu_unit "fr550_load0,fr550_load1" "integer")
(define_cpu_unit "fr550_store0,fr550_store1" "integer")
(exclusion_set "fr550_load0,fr550_load1" "fr550_store0,fr550_store1")
 
;; A store can only issue to I1 if one has also been issued to I0.
(presence_set "fr550_store1" "fr550_store0")
 
(define_bypass 0 "fr550_sethi" "fr550_setlo")
(define_insn_reservation "fr550_sethi" 1
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "sethi"))
"i3|i2|i1|i0")
 
(define_insn_reservation "fr550_setlo" 1
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "setlo"))
"i3|i2|i1|i0")
 
(define_insn_reservation "fr550_int" 1
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "int"))
"i3|i2|i1|i0")
 
(define_insn_reservation "fr550_mul" 2
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "mul"))
"i1|i0")
 
(define_insn_reservation "fr550_div" 19
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "div"))
"(i1|i0),(idiv1*18 | idiv2*18)")
 
(define_insn_reservation "fr550_load" 3
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "gload,fload"))
"(i1|i0)+(fr550_load0|fr550_load1)")
 
;; We can only issue a store to I1 if one was also issued to I0.
;; This means that, as far as frv_reorder_packet is concerned,
;; the instruction has the same priority as an I0-only instruction.
(define_insn_reservation "fr550_store" 1
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "gstore,fstore"))
"(i0+fr550_store0)|(i1+fr550_store1)")
 
(define_insn_reservation "fr550_transfer" 2
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "movgf,movfg"))
"i0")
 
(define_insn_reservation "fr550_jumpl" 0
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "jumpl"))
"i0")
 
(define_cpu_unit "fr550_ccr0,fr550_ccr1" "float_media")
 
(define_insn_reservation "fr550_branch" 0
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "jump,branch"))
"b1|b0")
 
(define_insn_reservation "fr550_ccr" 0
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "ccr"))
"(b1|b0) + (fr550_ccr1|fr550_ccr0)")
 
(define_insn_reservation "fr550_call" 0
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "call"))
"b0")
 
(define_automaton "fr550_float_media")
(define_cpu_unit "fr550_add0,fr550_add1" "fr550_float_media")
 
;; There are three possible combinations of floating-point/media instructions:
;;
;; - one media and one float
;; - up to four float, no media
;; - up to four media, no float
(define_cpu_unit "fr550_f0,fr550_f1,fr550_f2,fr550_f3" "fr550_float_media")
(define_cpu_unit "fr550_m0,fr550_m1,fr550_m2,fr550_m3" "fr550_float_media")
(exclusion_set "fr550_f1,fr550_f2,fr550_f3" "fr550_m1,fr550_m2,fr550_m3")
 
(define_reservation "fr550_float" "fr550_f0|fr550_f1|fr550_f2|fr550_f3")
(define_reservation "fr550_media" "fr550_m0|fr550_m1|fr550_m2|fr550_m3")
 
(define_insn_reservation "fr550_f1" 0
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "fnop"))
"(f3|f2|f1|f0) + fr550_float")
 
(define_insn_reservation "fr550_f2" 3
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "fsconv,fsadd,fscmp"))
"(f3|f2|f1|f0) + (fr550_add0|fr550_add1) + fr550_float")
 
(define_insn_reservation "fr550_f3_mul" 3
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "fsmul"))
"(f1|f0) + fr550_float")
 
(define_insn_reservation "fr550_f3_div" 10
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "fsdiv"))
"(f1|f0) + fr550_float")
 
(define_insn_reservation "fr550_f3_sqrt" 15
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "sqrt_single"))
"(f1|f0) + fr550_float")
 
;; Synthetic units for enforcing media issue restrictions. Certain types
;; of insn in M2 conflict with certain types in M0:
;;
;; M2
;; MNOP MALU MSFT MMAC MSET
;; MNOP - - x - -
;; MALU - x x - -
;; M0 MSFT - - x - x
;; MMAC - - x x -
;; MSET - - x - -
;;
;; where "x" indicates a conflict. The same restrictions apply to
;; M3 and M1.
;;
;; In addition -- and this is the awkward bit! -- instructions that
;; access ACC0-3 can only issue to M0 or M2. Those that access ACC4-7
;; can only issue to M1 or M3. We refer to such instructions as "even"
;; and "odd" respectively.
(define_cpu_unit "fr550_malu0,fr550_malu1" "float_media")
(define_cpu_unit "fr550_malu2,fr550_malu3" "float_media")
(define_cpu_unit "fr550_msft0,fr550_msft1" "float_media")
(define_cpu_unit "fr550_mmac0,fr550_mmac1" "float_media")
(define_cpu_unit "fr550_mmac2,fr550_mmac3" "float_media")
(define_cpu_unit "fr550_mset0,fr550_mset1" "float_media")
(define_cpu_unit "fr550_mset2,fr550_mset3" "float_media")
 
(exclusion_set "fr550_malu0" "fr550_malu2")
(exclusion_set "fr550_malu1" "fr550_malu3")
 
(exclusion_set "fr550_msft0" "fr550_mset2")
(exclusion_set "fr550_msft1" "fr550_mset3")
 
(exclusion_set "fr550_mmac0" "fr550_mmac2")
(exclusion_set "fr550_mmac1" "fr550_mmac3")
 
;; If an MSFT or MMAC instruction issues to a unit other than M0, we may
;; need to insert some nops. In the worst case, the packet will end up
;; having 4 integer instructions and 4 media instructions, leaving no
;; room for any branch instructions that the DFA might have accepted.
;;
;; This doesn't matter for JUMP_INSNs and CALL_INSNs because they are
;; always the last instructions to be passed to the DFA, and could be
;; pushed out to a separate packet once the nops have been added.
;; However, it does cause problems for ccr instructions since they
;; can occur anywhere in the unordered packet.
(exclusion_set "fr550_msft1,fr550_mmac1,fr550_mmac2,fr550_mmac3"
"fr550_ccr0,fr550_ccr1")
 
(define_reservation "fr550_malu"
"(f3 + fr550_malu3) | (f2 + fr550_malu2)
| (f1 + fr550_malu1) | (f0 + fr550_malu0)")
 
(define_reservation "fr550_msft_even"
"f0 + fr550_msft0")
 
(define_reservation "fr550_msft_odd"
"f1 + fr550_msft1")
 
(define_reservation "fr550_msft_either"
"(f1 + fr550_msft1) | (f0 + fr550_msft0)")
 
(define_reservation "fr550_mmac_even"
"(f2 + fr550_mmac2) | (f0 + fr550_mmac0)")
 
(define_reservation "fr550_mmac_odd"
"(f3 + fr550_mmac3) | (f1 + fr550_mmac1)")
 
(define_reservation "fr550_mset"
"(f3 + fr550_mset3) | (f2 + fr550_mset2)
| (f1 + fr550_mset1) | (f0 + fr550_mset0)")
 
(define_insn_reservation "fr550_mnop" 0
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "mnop"))
"fr550_media + (f3|f2|f1|f0)")
 
(define_insn_reservation "fr550_malu" 2
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "mlogic,maveh,msath,mabsh,maddh,mqaddh,mqsath"))
"fr550_media + fr550_malu")
 
;; These insns only operate on FPRs and so don't need to be classified
;; as even/odd.
(define_insn_reservation "fr550_msft_1_either" 2
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "mrot,mwcut,mshift,mexpdhw,mexpdhd,mpackh,
munpackh,mdpackh,mbhconv,mdrot,mcpl"))
"fr550_media + fr550_msft_either")
 
;; These insns read from ACC0-3.
(define_insn_reservation "fr550_msft_1_even" 2
(and (eq_attr "cpu" "fr550")
(and (eq_attr "type" "mcut,mrdacc,mdcut")
(eq_attr "acc_group" "even")))
"fr550_media + fr550_msft_even")
 
;; These insns read from ACC4-7.
(define_insn_reservation "fr550_msft_1_odd" 2
(and (eq_attr "cpu" "fr550")
(and (eq_attr "type" "mcut,mrdacc,mdcut")
(eq_attr "acc_group" "odd")))
"fr550_media + fr550_msft_odd")
 
;; MCLRACC with A=1 can issue to either M0 or M1.
(define_insn_reservation "fr550_msft_2_either" 2
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "mclracca"))
"fr550_media + fr550_msft_either")
 
;; These insns write to ACC0-3.
(define_insn_reservation "fr550_msft_2_even" 2
(and (eq_attr "cpu" "fr550")
(and (eq_attr "type" "mclracc,mwtacc")
(eq_attr "acc_group" "even")))
"fr550_media + fr550_msft_even")
 
;; These insns write to ACC4-7.
(define_insn_reservation "fr550_msft_2_odd" 2
(and (eq_attr "cpu" "fr550")
(and (eq_attr "type" "mclracc,mwtacc")
(eq_attr "acc_group" "odd")))
"fr550_media + fr550_msft_odd")
 
;; These insns read from and write to ACC0-3.
(define_insn_reservation "fr550_mmac_even" 2
(and (eq_attr "cpu" "fr550")
(and (eq_attr "type" "mmulh,mmulxh,mmach,mmrdh,mqmulh,mqmulxh,mqmach,
maddacc,mdaddacc,mcpx,mqcpx")
(eq_attr "acc_group" "even")))
"fr550_media + fr550_mmac_even")
 
;; These insns read from and write to ACC4-7.
(define_insn_reservation "fr550_mmac_odd" 2
(and (eq_attr "cpu" "fr550")
(and (eq_attr "type" "mmulh,mmulxh,mmach,mmrdh,mqmulh,mqmulxh,mqmach,
maddacc,mdaddacc,mcpx,mqcpx")
(eq_attr "acc_group" "odd")))
"fr550_media + fr550_mmac_odd")
 
(define_insn_reservation "fr550_mset" 1
(and (eq_attr "cpu" "fr550")
(eq_attr "type" "mset"))
"fr550_media + fr550_mset")
 
;; ::::::::::::::::::::
;; ::
;; :: Simple/FR300 scheduler description
;; ::
;; ::::::::::::::::::::
 
;; Fr300 or simple processor. To describe it as 1 insn issue
;; processor, we use control unit.
 
(define_insn_reservation "fr300_lat1" 1
(and (eq_attr "cpu" "fr300,simple")
(eq_attr "type" "!gload,fload,movfg,movgf"))
"c + control")
 
(define_insn_reservation "fr300_lat2" 2
(and (eq_attr "cpu" "fr300,simple")
(eq_attr "type" "gload,fload,movfg,movgf"))
"c + control")
 
;; ::::::::::::::::::::
;; ::
;; :: Delay Slots
;; ::
;; ::::::::::::::::::::
 
;; The insn attribute mechanism can be used to specify the requirements for
;; delay slots, if any, on a target machine. An instruction is said to require
;; a "delay slot" if some instructions that are physically after the
;; instruction are executed as if they were located before it. Classic
;; examples are branch and call instructions, which often execute the following
;; instruction before the branch or call is performed.
 
;; On some machines, conditional branch instructions can optionally "annul"
;; instructions in the delay slot. This means that the instruction will not be
;; executed for certain branch outcomes. Both instructions that annul if the
;; branch is true and instructions that annul if the branch is false are
;; supported.
 
;; Delay slot scheduling differs from instruction scheduling in that
;; determining whether an instruction needs a delay slot is dependent only
;; on the type of instruction being generated, not on data flow between the
;; instructions. See the next section for a discussion of data-dependent
;; instruction scheduling.
 
;; The requirement of an insn needing one or more delay slots is indicated via
;; the `define_delay' expression. It has the following form:
;;
;; (define_delay TEST
;; [DELAY-1 ANNUL-TRUE-1 ANNUL-FALSE-1
;; DELAY-2 ANNUL-TRUE-2 ANNUL-FALSE-2
;; ...])
 
;; TEST is an attribute test that indicates whether this `define_delay' applies
;; to a particular insn. If so, the number of required delay slots is
;; determined by the length of the vector specified as the second argument. An
;; insn placed in delay slot N must satisfy attribute test DELAY-N.
;; ANNUL-TRUE-N is an attribute test that specifies which insns may be annulled
;; if the branch is true. Similarly, ANNUL-FALSE-N specifies which insns in
;; the delay slot may be annulled if the branch is false. If annulling is not
;; supported for that delay slot, `(nil)' should be coded.
 
;; For example, in the common case where branch and call insns require a single
;; delay slot, which may contain any insn other than a branch or call, the
;; following would be placed in the `md' file:
 
;; (define_delay (eq_attr "type" "branch,call")
;; [(eq_attr "type" "!branch,call") (nil) (nil)])
 
;; Multiple `define_delay' expressions may be specified. In this case, each
;; such expression specifies different delay slot requirements and there must
;; be no insn for which tests in two `define_delay' expressions are both true.
 
;; For example, if we have a machine that requires one delay slot for branches
;; but two for calls, no delay slot can contain a branch or call insn, and any
;; valid insn in the delay slot for the branch can be annulled if the branch is
;; true, we might represent this as follows:
 
;; (define_delay (eq_attr "type" "branch")
;; [(eq_attr "type" "!branch,call")
;; (eq_attr "type" "!branch,call")
;; (nil)])
;;
;; (define_delay (eq_attr "type" "call")
;; [(eq_attr "type" "!branch,call") (nil) (nil)
;; (eq_attr "type" "!branch,call") (nil) (nil)])
 
;; Note - it is the backend's responsibility to fill any unfilled delay slots
;; at assembler generation time. This is usually done by adding a special print
;; operand to the delayed instruction, and then in the PRINT_OPERAND function
;; calling dbr_sequence_length() to determine how many delay slots were filled.
;; For example:
;;
;; --------------<machine>.md-----------------
;; (define_insn "call"
;; [(call (match_operand 0 "memory_operand" "m")
;; (match_operand 1 "" ""))]
;; ""
;; "call_delayed %0,%1,%2%#"
;; [(set_attr "length" "4")
;; (set_attr "type" "call")])
;;
;; -------------<machine>.h-------------------
;; #define PRINT_OPERAND_PUNCT_VALID_P(CODE) (CODE == '#')
;;
;; ------------<machine>.c------------------
;; void
;; machine_print_operand (file, x, code)
;; FILE * file;
;; rtx x;
;; int code;
;; {
;; switch (code)
;; {
;; case '#':
;; if (dbr_sequence_length () == 0)
;; fputs ("\n\tnop", file);
;; return;
;; ::::::::::::::::::::
;; ::
;; :: Notes on Patterns
;; ::
;; ::::::::::::::::::::
 
;; If you need to construct a sequence of assembler instructions in order
;; to implement a pattern be sure to escape any backslashes and double quotes
;; that you use, e.g.:
;;
;; (define_insn "an example"
;; [(some rtl)]
;; ""
;; "*
;; { static char buffer [100];
;; sprintf (buffer, \"insn \\t %d\", REGNO (operands[1]));
;; return buffer;
;; }"
;; )
;;
;; Also if there is more than one instruction, they can be separated by \\;
;; which is a space saving synonym for \\n\\t:
;;
;; (define_insn "another example"
;; [(some rtl)]
;; ""
;; "*
;; { static char buffer [100];
;; sprintf (buffer, \"insn1 \\t %d\\;insn2 \\t %%1\",
;; REGNO (operands[1]));
;; return buffer;
;; }"
;; )
;;
 
(include "predicates.md")
;; ::::::::::::::::::::
;; ::
;; :: Moves
;; ::
;; ::::::::::::::::::::
 
;; Wrap moves in define_expand to prevent memory->memory moves from being
;; generated at the RTL level, which generates better code for most machines
;; which can't do mem->mem moves.
 
;; If operand 0 is a `subreg' with mode M of a register whose own mode is wider
;; than M, the effect of this instruction is to store the specified value in
;; the part of the register that corresponds to mode M. The effect on the rest
;; of the register is undefined.
 
;; This class of patterns is special in several ways. First of all, each of
;; these names *must* be defined, because there is no other way to copy a datum
;; from one place to another.
 
;; Second, these patterns are not used solely in the RTL generation pass. Even
;; the reload pass can generate move insns to copy values from stack slots into
;; temporary registers. When it does so, one of the operands is a hard
;; register and the other is an operand that can need to be reloaded into a
;; register.
 
;; Therefore, when given such a pair of operands, the pattern must
;; generate RTL which needs no reloading and needs no temporary
;; registers--no registers other than the operands. For example, if
;; you support the pattern with a `define_expand', then in such a
;; case the `define_expand' mustn't call `force_reg' or any other such
;; function which might generate new pseudo registers.
 
;; This requirement exists even for subword modes on a RISC machine
;; where fetching those modes from memory normally requires several
;; insns and some temporary registers. Look in `spur.md' to see how
;; the requirement can be satisfied.
 
;; During reload a memory reference with an invalid address may be passed as an
;; operand. Such an address will be replaced with a valid address later in the
;; reload pass. In this case, nothing may be done with the address except to
;; use it as it stands. If it is copied, it will not be replaced with a valid
;; address. No attempt should be made to make such an address into a valid
;; address and no routine (such as `change_address') that will do so may be
;; called. Note that `general_operand' will fail when applied to such an
;; address.
;;
;; The global variable `reload_in_progress' (which must be explicitly declared
;; if required) can be used to determine whether such special handling is
;; required.
;;
;; The variety of operands that have reloads depends on the rest of
;; the machine description, but typically on a RISC machine these can
;; only be pseudo registers that did not get hard registers, while on
;; other machines explicit memory references will get optional
;; reloads.
;;
;; If a scratch register is required to move an object to or from memory, it
;; can be allocated using `gen_reg_rtx' prior to reload. But this is
;; impossible during and after reload. If there are cases needing scratch
;; registers after reload, you must define `SECONDARY_INPUT_RELOAD_CLASS' and
;; perhaps also `SECONDARY_OUTPUT_RELOAD_CLASS' to detect them, and provide
;; patterns `reload_inM' or `reload_outM' to handle them.
 
;; The constraints on a `moveM' must permit moving any hard register to any
;; other hard register provided that `HARD_REGNO_MODE_OK' permits mode M in
;; both registers and `REGISTER_MOVE_COST' applied to their classes returns a
;; value of 2.
 
;; It is obligatory to support floating point `moveM' instructions
;; into and out of any registers that can hold fixed point values,
;; because unions and structures (which have modes `SImode' or
;; `DImode') can be in those registers and they may have floating
;; point members.
 
;; There may also be a need to support fixed point `moveM' instructions in and
;; out of floating point registers. Unfortunately, I have forgotten why this
;; was so, and I don't know whether it is still true. If `HARD_REGNO_MODE_OK'
;; rejects fixed point values in floating point registers, then the constraints
;; of the fixed point `moveM' instructions must be designed to avoid ever
;; trying to reload into a floating point register.
 
(define_expand "movqi"
[(set (match_operand:QI 0 "general_operand" "")
(match_operand:QI 1 "general_operand" ""))]
""
"{ frv_emit_move (QImode, operands[0], operands[1]); DONE; }")
 
(define_insn "*movqi_load"
[(set (match_operand:QI 0 "register_operand" "=d,f")
(match_operand:QI 1 "frv_load_operand" "m,m"))]
""
"* return output_move_single (operands, insn);"
[(set_attr "length" "4")
(set_attr "type" "gload,fload")])
 
(define_insn "*movqi_internal"
[(set (match_operand:QI 0 "move_destination_operand" "=d,d,m,m,?f,?f,?d,?m,f,d,f")
(match_operand:QI 1 "move_source_operand" "L,d,d,O, d, f, f, f,GO,!m,!m"))]
"register_operand(operands[0], QImode) || reg_or_0_operand (operands[1], QImode)"
"* return output_move_single (operands, insn);"
[(set_attr "length" "4")
(set_attr "type" "int,int,gstore,gstore,movgf,fsconv,movfg,fstore,movgf,gload,fload")])
 
(define_expand "movhi"
[(set (match_operand:HI 0 "general_operand" "")
(match_operand:HI 1 "general_operand" ""))]
""
"{ frv_emit_move (HImode, operands[0], operands[1]); DONE; }")
 
(define_insn "*movhi_load"
[(set (match_operand:HI 0 "register_operand" "=d,f")
(match_operand:HI 1 "frv_load_operand" "m,m"))]
""
"* return output_move_single (operands, insn);"
[(set_attr "length" "4")
(set_attr "type" "gload,fload")])
 
(define_insn "*movhi_internal"
[(set (match_operand:HI 0 "move_destination_operand" "=d,d,d,m,m,?f,?f,?d,?m,f,d,f")
(match_operand:HI 1 "move_source_operand" "L,n,d,d,O, d, f, f, f,GO,!m,!m"))]
"register_operand(operands[0], HImode) || reg_or_0_operand (operands[1], HImode)"
"* return output_move_single (operands, insn);"
[(set_attr "length" "4,8,4,4,4,4,4,4,4,4,4,4")
(set_attr "type" "int,multi,int,gstore,gstore,movgf,fsconv,movfg,fstore,movgf,gload,fload")])
 
;; Split 2 word load of constants into sethi/setlo instructions
(define_split
[(set (match_operand:HI 0 "integer_register_operand" "")
(match_operand:HI 1 "int_2word_operand" ""))]
"reload_completed"
[(set (match_dup 0)
(high:HI (match_dup 1)))
(set (match_dup 0)
(lo_sum:HI (match_dup 0)
(match_dup 1)))]
"")
 
(define_insn "movhi_high"
[(set (match_operand:HI 0 "integer_register_operand" "=d")
(high:HI (match_operand:HI 1 "int_2word_operand" "i")))]
""
"sethi #hi(%1), %0"
[(set_attr "type" "sethi")
(set_attr "length" "4")])
 
(define_insn "movhi_lo_sum"
[(set (match_operand:HI 0 "integer_register_operand" "+d")
(lo_sum:HI (match_dup 0)
(match_operand:HI 1 "int_2word_operand" "i")))]
""
"setlo #lo(%1), %0"
[(set_attr "type" "setlo")
(set_attr "length" "4")])
 
(define_expand "movsi"
[(set (match_operand:SI 0 "move_destination_operand" "")
(match_operand:SI 1 "move_source_operand" ""))]
""
"{ frv_emit_move (SImode, operands[0], operands[1]); DONE; }")
 
;; Note - it is best to only have one movsi pattern and to handle
;; all the various contingencies by the use of alternatives. This
;; allows reload the greatest amount of flexibility (since reload will
;; only choose amongst alternatives for a selected insn, it will not
;; replace the insn with another one).
 
;; Unfortunately, we do have to separate out load-type moves from the rest,
;; and only allow memory source operands in the former. If we do memory and
;; constant loads in a single pattern, reload will be tempted to force
;; constants into memory when the destination is a floating-point register.
;; That may make a function use a PIC pointer when it didn't before, and we
;; cannot change PIC usage (and hence stack layout) so late in the game.
;; The resulting sequences for loading constants into FPRs are preferable
;; even when we're not generating PIC code.
 
;; However, if we don't accept input from memory at all in the generic
;; movsi pattern, reloads for asm instructions that reference pseudos
;; that end up assigned to memory will fail to match, because we
;; recognize them right after they're emitted, and we don't
;; re-recognize them again after the substitution for memory. So keep
;; a memory constraint available, just make sure reload won't be
;; tempted to use it.
;;
(define_insn "*movsi_load"
[(set (match_operand:SI 0 "register_operand" "=d,f")
(match_operand:SI 1 "frv_load_operand" "m,m"))]
""
"* return output_move_single (operands, insn);"
[(set_attr "length" "4")
(set_attr "type" "gload,fload")])
 
(define_insn "*movsi_got"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(match_operand:SI 1 "got12_operand" ""))]
""
"addi gr0, %1, %0"
[(set_attr "type" "int")
(set_attr "length" "4")])
 
(define_insn "*movsi_high_got"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(high:SI (match_operand:SI 1 "const_unspec_operand" "")))]
""
"sethi %1, %0"
[(set_attr "type" "sethi")
(set_attr "length" "4")])
 
(define_insn "*movsi_lo_sum_got"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(lo_sum:SI (match_operand:SI 1 "integer_register_operand" "0")
(match_operand:SI 2 "const_unspec_operand" "")))]
""
"setlo %2, %0"
[(set_attr "type" "setlo")
(set_attr "length" "4")])
 
(define_insn "*movsi_internal"
[(set (match_operand:SI 0 "move_destination_operand" "=d,d,d,m,m,z,d,d,f,f,m,?f,?z,d,f")
(match_operand:SI 1 "move_source_operand" "L,n,d,d,O,d,z,f,d,f,f,GO,GO,!m,!m"))]
"register_operand (operands[0], SImode) || reg_or_0_operand (operands[1], SImode)"
"* return output_move_single (operands, insn);"
[(set_attr "length" "4,8,4,4,4,4,4,4,4,4,4,4,4,4,4")
(set_attr "type" "int,multi,int,gstore,gstore,spr,spr,movfg,movgf,fsconv,fstore,movgf,spr,gload,fload")])
 
;; Split 2 word load of constants into sethi/setlo instructions
(define_insn_and_split "*movsi_2word"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(match_operand:SI 1 "int_2word_operand" "i"))]
""
"#"
"reload_completed"
[(set (match_dup 0)
(high:SI (match_dup 1)))
(set (match_dup 0)
(lo_sum:SI (match_dup 0)
(match_dup 1)))]
""
[(set_attr "length" "8")
(set_attr "type" "multi")])
 
(define_insn "movsi_high"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(high:SI (match_operand:SI 1 "int_2word_operand" "i")))]
""
"sethi #hi(%1), %0"
[(set_attr "type" "sethi")
(set_attr "length" "4")])
 
(define_insn "movsi_lo_sum"
[(set (match_operand:SI 0 "integer_register_operand" "+d")
(lo_sum:SI (match_dup 0)
(match_operand:SI 1 "int_2word_operand" "i")))]
""
"setlo #lo(%1), %0"
[(set_attr "type" "setlo")
(set_attr "length" "4")])
 
(define_expand "movdi"
[(set (match_operand:DI 0 "nonimmediate_operand" "")
(match_operand:DI 1 "general_operand" ""))]
""
"{ frv_emit_move (DImode, operands[0], operands[1]); DONE; }")
 
(define_insn "*movdi_double"
[(set (match_operand:DI 0 "move_destination_operand" "=e,?h,??d,??f,R,?R,??m,??m,e,?h,??d,??f,?e,??d,?h,??f,R,m,e,??d,e,??d,?h,??f")
(match_operand:DI 1 "move_source_operand" " e,h,d,f,e,h,d,f,R,R,m,m,h,f,e,d,GO,GO,GO,GO,nF,nF,GO,GO"))]
"TARGET_DOUBLE
&& (register_operand (operands[0], DImode)
|| reg_or_0_operand (operands[1], DImode))"
"* return output_move_double (operands, insn);"
[(set_attr "length" "8,4,8,8,4,4,8,8,4,4,8,8,4,8,4,8,4,8,8,8,16,16,8,8")
(set_attr "type" "multi,fdconv,multi,multi,gstore,fstore,gstore,fstore,gload,fload,gload,fload,movfg,movfg,movgf,movgf,gstore,gstore,multi,multi,multi,multi,movgf,movgf")])
 
(define_insn "*movdi_nodouble"
[(set (match_operand:DI 0 "move_destination_operand" "=e,?h,??d,??f,R,?R,??m,??m,e,?h,??d,??f,?e,??d,?h,??f,R,m,e,??d,e,??d,?h,??f")
(match_operand:DI 1 "move_source_operand" " e,h,d,f,e,h,d,f,R,R,m,m,h,f,e,d,GO,GO,GO,GO,nF,nF,GO,GO"))]
"!TARGET_DOUBLE
&& (register_operand (operands[0], DImode)
|| reg_or_0_operand (operands[1], DImode))"
"* return output_move_double (operands, insn);"
[(set_attr "length" "8,8,8,8,4,4,8,8,4,4,8,8,8,8,8,8,4,8,8,8,16,16,8,8")
(set_attr "type" "multi,multi,multi,multi,gstore,fstore,gstore,fstore,gload,fload,gload,fload,movfg,movfg,movgf,movgf,gstore,gstore,multi,multi,multi,multi,movgf,movgf")])
 
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(match_operand:DI 1 "dbl_memory_two_insn_operand" ""))]
"reload_completed"
[(const_int 0)]
"frv_split_double_load (operands[0], operands[1]);")
 
(define_split
[(set (match_operand:DI 0 "odd_reg_operand" "")
(match_operand:DI 1 "memory_operand" ""))]
"reload_completed"
[(const_int 0)]
"frv_split_double_load (operands[0], operands[1]);")
 
(define_split
[(set (match_operand:DI 0 "dbl_memory_two_insn_operand" "")
(match_operand:DI 1 "reg_or_0_operand" ""))]
"reload_completed"
[(const_int 0)]
"frv_split_double_store (operands[0], operands[1]);")
 
(define_split
[(set (match_operand:DI 0 "memory_operand" "")
(match_operand:DI 1 "odd_reg_operand" ""))]
"reload_completed"
[(const_int 0)]
"frv_split_double_store (operands[0], operands[1]);")
 
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(match_operand:DI 1 "register_operand" ""))]
"reload_completed
&& (odd_reg_operand (operands[0], DImode)
|| odd_reg_operand (operands[1], DImode)
|| (integer_register_operand (operands[0], DImode)
&& integer_register_operand (operands[1], DImode))
|| (!TARGET_DOUBLE
&& fpr_operand (operands[0], DImode)
&& fpr_operand (operands[1], DImode)))"
[(set (match_dup 2) (match_dup 4))
(set (match_dup 3) (match_dup 5))]
"
{
rtx op0 = operands[0];
rtx op0_low = gen_lowpart (SImode, op0);
rtx op0_high = gen_highpart (SImode, op0);
rtx op1 = operands[1];
rtx op1_low = gen_lowpart (SImode, op1);
rtx op1_high = gen_highpart (SImode, op1);
 
/* We normally copy the low-numbered register first. However, if the first
register operand 0 is the same as the second register of operand 1, we
must copy in the opposite order. */
 
if (REGNO (op0_high) == REGNO (op1_low))
{
operands[2] = op0_low;
operands[3] = op0_high;
operands[4] = op1_low;
operands[5] = op1_high;
}
else
{
operands[2] = op0_high;
operands[3] = op0_low;
operands[4] = op1_high;
operands[5] = op1_low;
}
}")
 
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(match_operand:DI 1 "const_int_operand" ""))]
"reload_completed"
[(set (match_dup 2) (match_dup 4))
(set (match_dup 3) (match_dup 5))]
"
{
rtx op0 = operands[0];
rtx op1 = operands[1];
 
operands[2] = gen_highpart (SImode, op0);
operands[3] = gen_lowpart (SImode, op0);
if (HOST_BITS_PER_WIDE_INT <= 32)
{
operands[4] = GEN_INT ((INTVAL (op1) < 0) ? -1 : 0);
operands[5] = op1;
}
else
{
operands[4] = GEN_INT ((((unsigned HOST_WIDE_INT)INTVAL (op1) >> 16)
>> 16) ^ ((unsigned HOST_WIDE_INT)1 << 31)
- ((unsigned HOST_WIDE_INT)1 << 31));
operands[5] = GEN_INT (trunc_int_for_mode (INTVAL (op1), SImode));
}
}")
 
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(match_operand:DI 1 "const_double_operand" ""))]
"reload_completed"
[(set (match_dup 2) (match_dup 4))
(set (match_dup 3) (match_dup 5))]
"
{
rtx op0 = operands[0];
rtx op1 = operands[1];
 
operands[2] = gen_highpart (SImode, op0);
operands[3] = gen_lowpart (SImode, op0);
operands[4] = GEN_INT (CONST_DOUBLE_HIGH (op1));
operands[5] = GEN_INT (CONST_DOUBLE_LOW (op1));
}")
 
;; Floating Point Moves
;;
;; Note - Patterns for SF mode moves are compulsory, but
;; patterns for DF are optional, as GCC can synthesize them.
 
(define_expand "movsf"
[(set (match_operand:SF 0 "general_operand" "")
(match_operand:SF 1 "general_operand" ""))]
""
"{ frv_emit_move (SFmode, operands[0], operands[1]); DONE; }")
 
(define_split
[(set (match_operand:SF 0 "integer_register_operand" "")
(match_operand:SF 1 "int_2word_operand" ""))]
"reload_completed"
[(set (match_dup 0)
(high:SF (match_dup 1)))
(set (match_dup 0)
(lo_sum:SF (match_dup 0)
(match_dup 1)))]
"")
 
(define_insn "*movsf_load_has_fprs"
[(set (match_operand:SF 0 "register_operand" "=f,d")
(match_operand:SF 1 "frv_load_operand" "m,m"))]
"TARGET_HAS_FPRS"
"* return output_move_single (operands, insn);"
[(set_attr "length" "4")
(set_attr "type" "fload,gload")])
 
(define_insn "*movsf_internal_has_fprs"
[(set (match_operand:SF 0 "move_destination_operand" "=f,f,m,m,?f,?d,?d,m,?d")
(match_operand:SF 1 "move_source_operand" "f,OG,f,OG,d,f,d,d,F"))]
"TARGET_HAS_FPRS
&& (register_operand (operands[0], SFmode) || reg_or_0_operand (operands[1], SFmode))"
"* return output_move_single (operands, insn);"
[(set_attr "length" "4,4,4,4,4,4,4,4,8")
(set_attr "type" "fsconv,movgf,fstore,gstore,movgf,movfg,int,gstore,multi")])
 
;; If we don't support the double instructions, prefer gprs over fprs, since it
;; will all be emulated
(define_insn "*movsf_internal_no_fprs"
[(set (match_operand:SF 0 "move_destination_operand" "=d,d,m,d,d")
(match_operand:SF 1 "move_source_operand" " d,OG,dOG,m,F"))]
"!TARGET_HAS_FPRS
&& (register_operand (operands[0], SFmode) || reg_or_0_operand (operands[1], SFmode))"
"* return output_move_single (operands, insn);"
[(set_attr "length" "4,4,4,4,8")
(set_attr "type" "int,int,gstore,gload,multi")])
 
(define_insn "movsf_high"
[(set (match_operand:SF 0 "integer_register_operand" "=d")
(high:SF (match_operand:SF 1 "int_2word_operand" "i")))]
""
"sethi #hi(%1), %0"
[(set_attr "type" "sethi")
(set_attr "length" "4")])
 
(define_insn "movsf_lo_sum"
[(set (match_operand:SF 0 "integer_register_operand" "+d")
(lo_sum:SF (match_dup 0)
(match_operand:SF 1 "int_2word_operand" "i")))]
""
"setlo #lo(%1), %0"
[(set_attr "type" "setlo")
(set_attr "length" "4")])
 
(define_expand "movdf"
[(set (match_operand:DF 0 "nonimmediate_operand" "")
(match_operand:DF 1 "general_operand" ""))]
""
"{ frv_emit_move (DFmode, operands[0], operands[1]); DONE; }")
 
(define_insn "*movdf_double"
[(set (match_operand:DF 0 "move_destination_operand" "=h,?e,??f,??d,R,?R,??m,??m,h,?e,??f,??d,?h,??f,?e,??d,R,m,h,??f,e,??d,e,??d")
(match_operand:DF 1 "move_source_operand" " h,e,f,d,h,e,f,d,R,R,m,m,e,d,h,f,GO,GO,GO,GO,GO,GO,F,F"))]
"TARGET_DOUBLE
&& (register_operand (operands[0], DFmode)
|| reg_or_0_operand (operands[1], DFmode))"
"* return output_move_double (operands, insn);"
[(set_attr "length" "4,8,8,8,4,4,8,8,4,4,8,8,4,8,4,8,4,8,8,8,8,8,16,16")
(set_attr "type" "fdconv,multi,multi,multi,fstore,gstore,fstore,gstore,fload,gload,fload,gload,movgf,movgf,movfg,movfg,gstore,gstore,movgf,movgf,multi,multi,multi,multi")])
 
;; If we don't support the double instructions, prefer gprs over fprs, since it
;; will all be emulated
(define_insn "*movdf_nodouble"
[(set (match_operand:DF 0 "move_destination_operand" "=e,?h,??d,??f,R,?R,??m,??m,e,?h,??d,??f,?e,??d,?h,??f,R,m,e,??d,e,??d,?h,??f")
(match_operand:DF 1 "move_source_operand" " e,h,d,f,e,h,d,f,R,R,m,m,h,f,e,d,GO,GO,GO,GO,nF,nF,GO,GO"))]
"!TARGET_DOUBLE
&& (register_operand (operands[0], DFmode)
|| reg_or_0_operand (operands[1], DFmode))"
"* return output_move_double (operands, insn);"
[(set_attr "length" "8,8,8,8,4,4,8,8,4,4,8,8,8,8,8,8,4,8,8,8,16,16,8,8")
(set_attr "type" "multi,multi,multi,multi,gstore,fstore,gstore,fstore,gload,fload,gload,fload,movfg,movfg,movgf,movgf,gstore,gstore,multi,multi,multi,multi,movgf,movgf")])
 
(define_split
[(set (match_operand:DF 0 "register_operand" "")
(match_operand:DF 1 "dbl_memory_two_insn_operand" ""))]
"reload_completed"
[(const_int 0)]
"frv_split_double_load (operands[0], operands[1]);")
 
(define_split
[(set (match_operand:DF 0 "odd_reg_operand" "")
(match_operand:DF 1 "memory_operand" ""))]
"reload_completed"
[(const_int 0)]
"frv_split_double_load (operands[0], operands[1]);")
 
(define_split
[(set (match_operand:DF 0 "dbl_memory_two_insn_operand" "")
(match_operand:DF 1 "reg_or_0_operand" ""))]
"reload_completed"
[(const_int 0)]
"frv_split_double_store (operands[0], operands[1]);")
 
(define_split
[(set (match_operand:DF 0 "memory_operand" "")
(match_operand:DF 1 "odd_reg_operand" ""))]
"reload_completed"
[(const_int 0)]
"frv_split_double_store (operands[0], operands[1]);")
 
(define_split
[(set (match_operand:DF 0 "register_operand" "")
(match_operand:DF 1 "register_operand" ""))]
"reload_completed
&& (odd_reg_operand (operands[0], DFmode)
|| odd_reg_operand (operands[1], DFmode)
|| (integer_register_operand (operands[0], DFmode)
&& integer_register_operand (operands[1], DFmode))
|| (!TARGET_DOUBLE
&& fpr_operand (operands[0], DFmode)
&& fpr_operand (operands[1], DFmode)))"
[(set (match_dup 2) (match_dup 4))
(set (match_dup 3) (match_dup 5))]
"
{
rtx op0 = operands[0];
rtx op0_low = gen_lowpart (SImode, op0);
rtx op0_high = gen_highpart (SImode, op0);
rtx op1 = operands[1];
rtx op1_low = gen_lowpart (SImode, op1);
rtx op1_high = gen_highpart (SImode, op1);
 
/* We normally copy the low-numbered register first. However, if the first
register operand 0 is the same as the second register of operand 1, we
must copy in the opposite order. */
 
if (REGNO (op0_high) == REGNO (op1_low))
{
operands[2] = op0_low;
operands[3] = op0_high;
operands[4] = op1_low;
operands[5] = op1_high;
}
else
{
operands[2] = op0_high;
operands[3] = op0_low;
operands[4] = op1_high;
operands[5] = op1_low;
}
}")
 
(define_split
[(set (match_operand:DF 0 "register_operand" "")
(match_operand:DF 1 "const_int_operand" ""))]
"reload_completed"
[(set (match_dup 2) (match_dup 4))
(set (match_dup 3) (match_dup 5))]
"
{
rtx op0 = operands[0];
rtx op1 = operands[1];
 
operands[2] = gen_highpart (SImode, op0);
operands[3] = gen_lowpart (SImode, op0);
if (HOST_BITS_PER_WIDE_INT <= 32)
{
operands[4] = GEN_INT ((INTVAL (op1) < 0) ? -1 : 0);
operands[5] = op1;
}
else
{
operands[4] = GEN_INT ((((unsigned HOST_WIDE_INT)INTVAL (op1) >> 16)
>> 16) ^ ((unsigned HOST_WIDE_INT)1 << 31)
- ((unsigned HOST_WIDE_INT)1 << 31));
operands[5] = GEN_INT (trunc_int_for_mode (INTVAL (op1), SImode));
}
}")
 
(define_split
[(set (match_operand:DF 0 "register_operand" "")
(match_operand:DF 1 "const_double_operand" ""))]
"reload_completed"
[(set (match_dup 2) (match_dup 4))
(set (match_dup 3) (match_dup 5))]
"
{
rtx op0 = operands[0];
rtx op1 = operands[1];
REAL_VALUE_TYPE rv;
long l[2];
 
REAL_VALUE_FROM_CONST_DOUBLE (rv, op1);
REAL_VALUE_TO_TARGET_DOUBLE (rv, l);
 
operands[2] = gen_highpart (SImode, op0);
operands[3] = gen_lowpart (SImode, op0);
operands[4] = GEN_INT (l[0]);
operands[5] = GEN_INT (l[1]);
}")
 
;; String/block move insn.
;; Argument 0 is the destination
;; Argument 1 is the source
;; Argument 2 is the length
;; Argument 3 is the alignment
 
(define_expand "movmemsi"
[(parallel [(set (match_operand:BLK 0 "" "")
(match_operand:BLK 1 "" ""))
(use (match_operand:SI 2 "" ""))
(use (match_operand:SI 3 "" ""))])]
""
"
{
if (frv_expand_block_move (operands))
DONE;
else
FAIL;
}")
 
;; String/block set insn.
;; Argument 0 is the destination
;; Argument 1 is the length
;; Argument 2 is the byte value -- ignore any value but zero
;; Argument 3 is the alignment
 
(define_expand "setmemsi"
[(parallel [(set (match_operand:BLK 0 "" "")
(match_operand 2 "" ""))
(use (match_operand:SI 1 "" ""))
(use (match_operand:SI 3 "" ""))])]
""
"
{
/* If value to set is not zero, use the library routine. */
if (operands[2] != const0_rtx)
FAIL;
 
if (frv_expand_block_clear (operands))
DONE;
else
FAIL;
}")
 
;; The "membar" part of a __builtin_read* or __builtin_write* function.
;; Operand 0 is a volatile reference to the memory that the function reads
;; or writes. Operand 1 is the address being accessed, or zero if the
;; address isn't a known constant. Operand 2 describes the __builtin
;; function (either FRV_IO_READ or FRV_IO_WRITE).
(define_insn "optional_membar_<mode>"
[(set (match_operand:IMODE 0 "memory_operand" "=m")
(unspec:IMODE [(match_operand 1 "const_int_operand" "")
(match_operand 2 "const_int_operand" "")]
UNSPEC_OPTIONAL_MEMBAR))]
""
"membar"
[(set_attr "length" "4")])
;; ::::::::::::::::::::
;; ::
;; :: Reload CC registers
;; ::
;; ::::::::::::::::::::
 
;; Use as a define_expand so that cse/gcse/combine can't accidentally
;; create movcc insns.
 
(define_expand "movcc"
[(parallel [(set (match_operand:CC 0 "move_destination_operand" "")
(match_operand:CC 1 "move_source_operand" ""))
(clobber (match_dup 2))])]
""
"
{
if (! reload_in_progress && ! reload_completed)
FAIL;
 
operands[2] = gen_rtx_REG (CC_CCRmode, ICR_TEMP);
}")
 
(define_insn "*internal_movcc"
[(set (match_operand:CC 0 "move_destination_operand" "=t,d,d,m,d")
(match_operand:CC 1 "move_source_operand" "d,d,m,d,t"))
(clobber (match_scratch:CC_CCR 2 "=X,X,X,X,&v"))]
"reload_in_progress || reload_completed"
"@
cmpi %1, #0, %0
mov %1, %0
ld%I1%U1 %M1, %0
st%I0%U0 %1, %M0
#"
[(set_attr "length" "4,4,4,4,20")
(set_attr "type" "int,int,gload,gstore,multi")])
 
;; To move an ICC value to a GPR for a signed comparison, we create a value
;; that when compared to 0, sets the N and Z flags appropriately (we don't care
;; about the V and C flags, since these comparisons are signed).
 
(define_split
[(set (match_operand:CC 0 "integer_register_operand" "")
(match_operand:CC 1 "icc_operand" ""))
(clobber (match_operand:CC_CCR 2 "icr_operand" ""))]
"reload_in_progress || reload_completed"
[(match_dup 3)]
"
{
rtx dest = simplify_gen_subreg (SImode, operands[0], CCmode, 0);
rtx icc = operands[1];
rtx icr = operands[2];
 
start_sequence ();
 
emit_insn (gen_rtx_SET (VOIDmode, icr,
gen_rtx_LT (CC_CCRmode, icc, const0_rtx)));
 
emit_insn (gen_movsi (dest, const1_rtx));
 
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_NE (CC_CCRmode, icr, const0_rtx),
gen_rtx_SET (VOIDmode, dest,
gen_rtx_NEG (SImode, dest))));
 
emit_insn (gen_rtx_SET (VOIDmode, icr,
gen_rtx_EQ (CC_CCRmode, icc, const0_rtx)));
 
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_NE (CC_CCRmode, icr, const0_rtx),
gen_rtx_SET (VOIDmode, dest, const0_rtx)));
 
operands[3] = get_insns ();
end_sequence ();
}")
 
(define_expand "reload_incc"
[(parallel [(set (match_operand:CC 2 "integer_register_operand" "=&d")
(match_operand:CC 1 "memory_operand" "m"))
(clobber (match_scratch:CC_CCR 3 ""))])
(parallel [(set (match_operand:CC 0 "icc_operand" "=t")
(match_dup 2))
(clobber (match_scratch:CC_CCR 4 ""))])]
""
"")
 
(define_expand "reload_outcc"
[(parallel [(set (match_operand:CC 2 "integer_register_operand" "=&d")
(match_operand:CC 1 "icc_operand" "t"))
(clobber (match_dup 3))])
(parallel [(set (match_operand:CC 0 "memory_operand" "=m")
(match_dup 2))
(clobber (match_scratch:CC_CCR 4 ""))])]
""
"operands[3] = gen_rtx_REG (CC_CCRmode, ICR_TEMP);")
 
;; Reload CC_UNSmode for unsigned integer comparisons
;; Use define_expand so that cse/gcse/combine can't create movcc_uns insns
 
(define_expand "movcc_uns"
[(parallel [(set (match_operand:CC_UNS 0 "move_destination_operand" "")
(match_operand:CC_UNS 1 "move_source_operand" ""))
(clobber (match_dup 2))])]
""
"
{
if (! reload_in_progress && ! reload_completed)
FAIL;
operands[2] = gen_rtx_REG (CC_CCRmode, ICR_TEMP);
}")
 
(define_insn "*internal_movcc_uns"
[(set (match_operand:CC_UNS 0 "move_destination_operand" "=t,d,d,m,d")
(match_operand:CC_UNS 1 "move_source_operand" "d,d,m,d,t"))
(clobber (match_scratch:CC_CCR 2 "=X,X,X,X,&v"))]
"reload_in_progress || reload_completed"
"@
cmpi %1, #1, %0
mov %1, %0
ld%I1%U1 %M1, %0
st%I0%U0 %1, %M0
#"
[(set_attr "length" "4,4,4,4,20")
(set_attr "type" "int,int,gload,gstore,multi")])
 
;; To move an ICC value to a GPR for an unsigned comparison, we create a value
;; that when compared to 1, sets the Z, V, and C flags appropriately (we don't
;; care about the N flag, since these comparisons are unsigned).
 
(define_split
[(set (match_operand:CC_UNS 0 "integer_register_operand" "")
(match_operand:CC_UNS 1 "icc_operand" ""))
(clobber (match_operand:CC_CCR 2 "icr_operand" ""))]
"reload_in_progress || reload_completed"
[(match_dup 3)]
"
{
rtx dest = simplify_gen_subreg (SImode, operands[0], CC_UNSmode, 0);
rtx icc = operands[1];
rtx icr = operands[2];
 
start_sequence ();
 
emit_insn (gen_rtx_SET (VOIDmode, icr,
gen_rtx_GTU (CC_CCRmode, icc, const0_rtx)));
 
emit_insn (gen_movsi (dest, const1_rtx));
 
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_NE (CC_CCRmode, icr, const0_rtx),
gen_addsi3 (dest, dest, dest)));
 
emit_insn (gen_rtx_SET (VOIDmode, icr,
gen_rtx_LTU (CC_CCRmode, icc, const0_rtx)));
 
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_NE (CC_CCRmode, icr, const0_rtx),
gen_rtx_SET (VOIDmode, dest, const0_rtx)));
 
operands[3] = get_insns ();
end_sequence ();
}")
 
(define_expand "reload_incc_uns"
[(parallel [(set (match_operand:CC_UNS 2 "integer_register_operand" "=&d")
(match_operand:CC_UNS 1 "memory_operand" "m"))
(clobber (match_scratch:CC_CCR 3 ""))])
(parallel [(set (match_operand:CC_UNS 0 "icc_operand" "=t")
(match_dup 2))
(clobber (match_scratch:CC_CCR 4 ""))])]
""
"")
 
(define_expand "reload_outcc_uns"
[(parallel [(set (match_operand:CC_UNS 2 "integer_register_operand" "=&d")
(match_operand:CC_UNS 1 "icc_operand" "t"))
(clobber (match_dup 3))])
(parallel [(set (match_operand:CC_UNS 0 "memory_operand" "=m")
(match_dup 2))
(clobber (match_scratch:CC_CCR 4 ""))])]
""
"operands[3] = gen_rtx_REG (CC_CCRmode, ICR_TEMP);")
 
;; Reload CC_NZmode. This is mostly the same as the CCmode and CC_UNSmode
;; handling, but it uses different sequences for moving between GPRs and ICCs.
 
(define_expand "movcc_nz"
[(parallel [(set (match_operand:CC_NZ 0 "move_destination_operand" "")
(match_operand:CC_NZ 1 "move_source_operand" ""))
(clobber (match_dup 2))])]
""
"
{
if (!reload_in_progress && !reload_completed)
FAIL;
operands[2] = gen_rtx_REG (CC_CCRmode, ICR_TEMP);
}")
 
(define_insn "*internal_movcc_nz"
[(set (match_operand:CC_NZ 0 "move_destination_operand" "=t,d,d,m,d")
(match_operand:CC_NZ 1 "move_source_operand" "d,d,m,d,t"))
(clobber (match_scratch:CC_CCR 2 "=X,X,X,X,&v"))]
"reload_in_progress || reload_completed"
"@
cmpi %1, #0, %0
mov %1, %0
ld%I1%U1 %M1, %0
st%I0%U0 %1, %M0
#"
[(set_attr "length" "4,4,4,4,20")
(set_attr "type" "int,int,gload,gstore,multi")])
 
;; Set the destination to a value that, when compared with zero, will
;; restore the value of the Z and N flags. The values of the other
;; flags don't matter. The sequence is:
;;
;; setlos op0,#-1
;; ckp op1,op2
;; csub gr0,op0,op0,op2
;; ckeq op1,op2
;; cmov gr0,op0,op2
(define_split
[(set (match_operand:CC_NZ 0 "integer_register_operand" "")
(match_operand:CC_NZ 1 "icc_operand" ""))
(clobber (match_operand:CC_CCR 2 "icr_operand" ""))]
"reload_in_progress || reload_completed"
[(set (match_dup 3)
(const_int -1))
(set (match_dup 2)
(ge:CC_CCR (match_dup 1)
(const_int 0)))
(cond_exec (ne:CC_CCR (match_dup 2)
(const_int 0))
(set (match_dup 3)
(neg:SI (match_dup 3))))
(set (match_dup 2)
(eq:CC_CCR (match_dup 1)
(const_int 0)))
(cond_exec (ne:CC_CCR (match_dup 2)
(const_int 0))
(set (match_dup 3) (const_int 0)))]
"operands[3] = simplify_gen_subreg (SImode, operands[0], CC_NZmode, 0);")
 
(define_expand "reload_incc_nz"
[(parallel [(set (match_operand:CC_NZ 2 "integer_register_operand" "=&d")
(match_operand:CC_NZ 1 "memory_operand" "m"))
(clobber (match_scratch:CC_CCR 3 ""))])
(parallel [(set (match_operand:CC_NZ 0 "icc_operand" "=t")
(match_dup 2))
(clobber (match_scratch:CC_CCR 4 ""))])]
""
"")
 
(define_expand "reload_outcc_nz"
[(parallel [(set (match_operand:CC_NZ 2 "integer_register_operand" "=&d")
(match_operand:CC_NZ 1 "icc_operand" "t"))
(clobber (match_dup 3))])
(parallel [(set (match_operand:CC_NZ 0 "memory_operand" "=m")
(match_dup 2))
(clobber (match_scratch:CC_CCR 4 ""))])]
""
"operands[3] = gen_rtx_REG (CC_CCRmode, ICR_TEMP);")
 
;; Reload CC_FPmode for floating point comparisons
;; We use a define_expand here so that cse/gcse/combine can't accidentally
;; create movcc insns. If this was a named define_insn, we would not be able
;; to make it conditional on reload.
 
(define_expand "movcc_fp"
[(set (match_operand:CC_FP 0 "movcc_fp_destination_operand" "")
(match_operand:CC_FP 1 "move_source_operand" ""))]
"TARGET_HAS_FPRS"
"
{
if (! reload_in_progress && ! reload_completed)
FAIL;
}")
 
(define_insn "*movcc_fp_internal"
[(set (match_operand:CC_FP 0 "movcc_fp_destination_operand" "=d,d,d,m")
(match_operand:CC_FP 1 "move_source_operand" "u,d,m,d"))]
"TARGET_HAS_FPRS && (reload_in_progress || reload_completed)"
"@
#
mov %1, %0
ld%I1%U1 %M1, %0
st%I0%U0 %1, %M0"
[(set_attr "length" "12,4,4,4")
(set_attr "type" "multi,int,gload,gstore")])
 
 
(define_expand "reload_incc_fp"
[(match_operand:CC_FP 0 "fcc_operand" "=u")
(match_operand:CC_FP 1 "gpr_or_memory_operand_with_scratch" "m")
(match_operand:TI 2 "integer_register_operand" "=&d")]
"TARGET_HAS_FPRS"
"
{
rtx cc_op2 = simplify_gen_subreg (CC_FPmode, operands[2], TImode, 0);
rtx int_op2 = simplify_gen_subreg (SImode, operands[2], TImode, 0);
rtx temp1 = simplify_gen_subreg (SImode, operands[2], TImode, 4);
rtx temp2 = simplify_gen_subreg (SImode, operands[2], TImode, 8);
int shift = CC_SHIFT_RIGHT (REGNO (operands[0]));
HOST_WIDE_INT mask;
 
if (!gpr_or_memory_operand (operands[1], CC_FPmode))
{
rtx addr;
rtx temp3 = simplify_gen_subreg (SImode, operands[2], TImode, 12);
 
gcc_assert (GET_CODE (operands[1]) == MEM);
 
addr = XEXP (operands[1], 0);
 
gcc_assert (GET_CODE (addr) == PLUS);
 
emit_move_insn (temp3, XEXP (addr, 1));
 
operands[1] = replace_equiv_address (operands[1],
gen_rtx_PLUS (GET_MODE (addr),
XEXP (addr, 0),
temp3));
}
 
emit_insn (gen_movcc_fp (cc_op2, operands[1]));
if (shift)
emit_insn (gen_ashlsi3 (int_op2, int_op2, GEN_INT (shift)));
 
mask = ~ ((HOST_WIDE_INT)CC_MASK << shift);
emit_insn (gen_movsi (temp1, GEN_INT (mask)));
emit_insn (gen_update_fcc (operands[0], int_op2, temp1, temp2));
DONE;
}")
 
(define_expand "reload_outcc_fp"
[(set (match_operand:CC_FP 2 "integer_register_operand" "=&d")
(match_operand:CC_FP 1 "fcc_operand" "u"))
(set (match_operand:CC_FP 0 "memory_operand" "=m")
(match_dup 2))]
"TARGET_HAS_FPRS"
"")
 
;; Convert a FCC value to gpr
(define_insn "read_fcc"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(unspec:SI [(match_operand:CC_FP 1 "fcc_operand" "u")]
UNSPEC_CC_TO_GPR))]
"TARGET_HAS_FPRS"
"movsg ccr, %0"
[(set_attr "type" "spr")
(set_attr "length" "4")])
 
(define_split
[(set (match_operand:CC_FP 0 "integer_register_operand" "")
(match_operand:CC_FP 1 "fcc_operand" ""))]
"reload_completed && TARGET_HAS_FPRS"
[(match_dup 2)]
"
{
rtx int_op0 = simplify_gen_subreg (SImode, operands[0], CC_FPmode, 0);
int shift = CC_SHIFT_RIGHT (REGNO (operands[1]));
 
start_sequence ();
 
emit_insn (gen_read_fcc (int_op0, operands[1]));
if (shift)
emit_insn (gen_lshrsi3 (int_op0, int_op0, GEN_INT (shift)));
 
emit_insn (gen_andsi3 (int_op0, int_op0, GEN_INT (CC_MASK)));
 
operands[2] = get_insns ();
end_sequence ();
}")
 
;; Move a gpr value to FCC.
;; Operand0 = FCC
;; Operand1 = reloaded value shifted appropriately
;; Operand2 = mask to eliminate current register
;; Operand3 = temporary to load/store ccr
(define_insn "update_fcc"
[(set (match_operand:CC_FP 0 "fcc_operand" "=u")
(unspec:CC_FP [(match_operand:SI 1 "integer_register_operand" "d")
(match_operand:SI 2 "integer_register_operand" "d")]
UNSPEC_GPR_TO_CC))
(clobber (match_operand:SI 3 "integer_register_operand" "=&d"))]
"TARGET_HAS_FPRS"
"movsg ccr, %3\;and %2, %3, %3\;or %1, %3, %3\;movgs %3, ccr"
[(set_attr "type" "multi")
(set_attr "length" "16")])
 
;; Reload CC_CCRmode for conditional execution registers
(define_insn "movcc_ccr"
[(set (match_operand:CC_CCR 0 "move_destination_operand" "=d,d,d,m,v,?w,C,d")
(match_operand:CC_CCR 1 "move_source_operand" "C,d,m,d,n,n,C,L"))]
""
"@
#
mov %1, %0
ld%I1%U1 %M1, %0
st%I0%U0 %1, %M0
#
#
orcr %1, %1, %0
setlos #%1, %0"
[(set_attr "length" "8,4,4,4,8,12,4,4")
(set_attr "type" "multi,int,gload,gstore,multi,multi,ccr,int")])
 
(define_expand "reload_incc_ccr"
[(match_operand:CC_CCR 0 "cr_operand" "=C")
(match_operand:CC_CCR 1 "memory_operand" "m")
(match_operand:CC_CCR 2 "integer_register_operand" "=&d")]
""
"
{
rtx icc = gen_rtx_REG (CCmode, ICC_TEMP);
rtx int_op2 = simplify_gen_subreg (SImode, operands[2], CC_CCRmode, 0);
rtx icr = (ICR_P (REGNO (operands[0]))
? operands[0] : gen_rtx_REG (CC_CCRmode, ICR_TEMP));
 
emit_insn (gen_movcc_ccr (operands[2], operands[1]));
emit_insn (gen_cmpsi_cc (icc, int_op2, const0_rtx));
emit_insn (gen_movcc_ccr (icr, gen_rtx_NE (CC_CCRmode, icc, const0_rtx)));
 
if (! ICR_P (REGNO (operands[0])))
emit_insn (gen_movcc_ccr (operands[0], icr));
 
DONE;
}")
 
(define_expand "reload_outcc_ccr"
[(set (match_operand:CC_CCR 2 "integer_register_operand" "=&d")
(match_operand:CC_CCR 1 "cr_operand" "C"))
(set (match_operand:CC_CCR 0 "memory_operand" "=m")
(match_dup 2))]
""
"")
 
(define_split
[(set (match_operand:CC_CCR 0 "integer_register_operand" "")
(match_operand:CC_CCR 1 "cr_operand" ""))]
"reload_completed"
[(match_dup 2)]
"
{
rtx int_op0 = simplify_gen_subreg (SImode, operands[0], CC_CCRmode, 0);
 
start_sequence ();
emit_move_insn (operands[0], const1_rtx);
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_EQ (CC_CCRmode,
operands[1],
const0_rtx),
gen_rtx_SET (VOIDmode, int_op0,
const0_rtx)));
 
operands[2] = get_insns ();
end_sequence ();
}")
 
(define_split
[(set (match_operand:CC_CCR 0 "cr_operand" "")
(match_operand:CC_CCR 1 "const_int_operand" ""))]
"reload_completed"
[(match_dup 2)]
"
{
rtx icc = gen_rtx_REG (CCmode, ICC_TEMP);
rtx r0 = gen_rtx_REG (SImode, GPR_FIRST);
rtx icr = (ICR_P (REGNO (operands[0]))
? operands[0] : gen_rtx_REG (CC_CCRmode, ICR_TEMP));
 
start_sequence ();
 
emit_insn (gen_cmpsi_cc (icc, r0, const0_rtx));
 
emit_insn (gen_movcc_ccr (icr,
gen_rtx_fmt_ee (((INTVAL (operands[1]) == 0)
? EQ : NE), CC_CCRmode,
r0, const0_rtx)));
 
if (! ICR_P (REGNO (operands[0])))
emit_insn (gen_movcc_ccr (operands[0], icr));
 
operands[2] = get_insns ();
end_sequence ();
}")
 
;; ::::::::::::::::::::
;; ::
;; :: Conversions
;; ::
;; ::::::::::::::::::::
 
;; Signed conversions from a smaller integer to a larger integer
;;
;; These operations are optional. If they are not
;; present GCC will synthesize them for itself
;; Even though frv does not provide these instructions, we define them
;; to allow load + sign extend to be collapsed together
(define_insn "extendqihi2"
[(set (match_operand:HI 0 "integer_register_operand" "=d,d")
(sign_extend:HI (match_operand:QI 1 "gpr_or_memory_operand" "d,m")))]
""
"@
#
ldsb%I1%U1 %M1,%0"
[(set_attr "length" "8,4")
(set_attr "type" "multi,gload")])
 
(define_split
[(set (match_operand:HI 0 "integer_register_operand" "")
(sign_extend:HI (match_operand:QI 1 "integer_register_operand" "")))]
"reload_completed"
[(match_dup 2)
(match_dup 3)]
"
{
rtx op0 = gen_lowpart (SImode, operands[0]);
rtx op1 = gen_lowpart (SImode, operands[1]);
rtx shift = GEN_INT (24);
 
operands[2] = gen_ashlsi3 (op0, op1, shift);
operands[3] = gen_ashrsi3 (op0, op0, shift);
}")
 
(define_insn "extendqisi2"
[(set (match_operand:SI 0 "integer_register_operand" "=d,d")
(sign_extend:SI (match_operand:QI 1 "gpr_or_memory_operand" "d,m")))]
""
"@
#
ldsb%I1%U1 %M1,%0"
[(set_attr "length" "8,4")
(set_attr "type" "multi,gload")])
 
(define_split
[(set (match_operand:SI 0 "integer_register_operand" "")
(sign_extend:SI (match_operand:QI 1 "integer_register_operand" "")))]
"reload_completed"
[(match_dup 2)
(match_dup 3)]
"
{
rtx op0 = gen_lowpart (SImode, operands[0]);
rtx op1 = gen_lowpart (SImode, operands[1]);
rtx shift = GEN_INT (24);
 
operands[2] = gen_ashlsi3 (op0, op1, shift);
operands[3] = gen_ashrsi3 (op0, op0, shift);
}")
 
;;(define_insn "extendqidi2"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (sign_extend:DI (match_operand:QI 1 "general_operand" "g")))]
;; ""
;; "extendqihi2 %0,%1"
;; [(set_attr "length" "4")])
 
(define_insn "extendhisi2"
[(set (match_operand:SI 0 "integer_register_operand" "=d,d")
(sign_extend:SI (match_operand:HI 1 "gpr_or_memory_operand" "d,m")))]
""
"@
#
ldsh%I1%U1 %M1,%0"
[(set_attr "length" "8,4")
(set_attr "type" "multi,gload")])
 
(define_split
[(set (match_operand:SI 0 "integer_register_operand" "")
(sign_extend:SI (match_operand:HI 1 "integer_register_operand" "")))]
"reload_completed"
[(match_dup 2)
(match_dup 3)]
"
{
rtx op0 = gen_lowpart (SImode, operands[0]);
rtx op1 = gen_lowpart (SImode, operands[1]);
rtx shift = GEN_INT (16);
 
operands[2] = gen_ashlsi3 (op0, op1, shift);
operands[3] = gen_ashrsi3 (op0, op0, shift);
}")
 
;;(define_insn "extendhidi2"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (sign_extend:DI (match_operand:HI 1 "general_operand" "g")))]
;; ""
;; "extendhihi2 %0,%1"
;; [(set_attr "length" "4")])
;;
;;(define_insn "extendsidi2"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (sign_extend:DI (match_operand:SI 1 "general_operand" "g")))]
;; ""
;; "extendsidi2 %0,%1"
;; [(set_attr "length" "4")])
 
;; Unsigned conversions from a smaller integer to a larger integer
(define_insn "zero_extendqihi2"
[(set (match_operand:HI 0 "integer_register_operand" "=d,d,d")
(zero_extend:HI
(match_operand:QI 1 "gpr_or_memory_operand" "d,L,m")))]
""
"@
andi %1,#0xff,%0
setlos %1,%0
ldub%I1%U1 %M1,%0"
[(set_attr "length" "4")
(set_attr "type" "int,int,gload")])
 
(define_insn "zero_extendqisi2"
[(set (match_operand:SI 0 "integer_register_operand" "=d,d,d")
(zero_extend:SI
(match_operand:QI 1 "gpr_or_memory_operand" "d,L,m")))]
""
"@
andi %1,#0xff,%0
setlos %1,%0
ldub%I1%U1 %M1,%0"
[(set_attr "length" "4")
(set_attr "type" "int,int,gload")])
 
;;(define_insn "zero_extendqidi2"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (zero_extend:DI (match_operand:QI 1 "general_operand" "g")))]
;; ""
;; "zero_extendqihi2 %0,%1"
;; [(set_attr "length" "4")])
 
;; Do not set the type for the sethi to "sethi", since the scheduler will think
;; the sethi takes 0 cycles as part of allowing sethi/setlo to be in the same
;; VLIW instruction.
(define_insn "zero_extendhisi2"
[(set (match_operand:SI 0 "integer_register_operand" "=d,d")
(zero_extend:SI (match_operand:HI 1 "gpr_or_memory_operand" "0,m")))]
""
"@
sethi #hi(#0),%0
lduh%I1%U1 %M1,%0"
[(set_attr "length" "4")
(set_attr "type" "int,gload")])
 
;;(define_insn "zero_extendhidi2"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (zero_extend:DI (match_operand:HI 1 "general_operand" "g")))]
;; ""
;; "zero_extendhihi2 %0,%1"
;; [(set_attr "length" "4")])
;;
;;(define_insn "zero_extendsidi2"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (zero_extend:DI (match_operand:SI 1 "general_operand" "g")))]
;; ""
;; "zero_extendsidi2 %0,%1"
;; [(set_attr "length" "4")])
;;
;;;; Convert between floating point types of different sizes.
;;
;;(define_insn "extendsfdf2"
;; [(set (match_operand:DF 0 "register_operand" "=r")
;; (float_extend:DF (match_operand:SF 1 "register_operand" "r")))]
;; ""
;; "extendsfdf2 %0,%1"
;; [(set_attr "length" "4")])
;;
;;(define_insn "truncdfsf2"
;; [(set (match_operand:SF 0 "register_operand" "=r")
;; (float_truncate:SF (match_operand:DF 1 "register_operand" "r")))]
;; ""
;; "truncdfsf2 %0,%1"
;; [(set_attr "length" "4")])
 
;;;; Convert between signed integer types and floating point.
(define_insn "floatsisf2"
[(set (match_operand:SF 0 "fpr_operand" "=f")
(float:SF (match_operand:SI 1 "fpr_operand" "f")))]
"TARGET_HARD_FLOAT"
"fitos %1,%0"
[(set_attr "length" "4")
(set_attr "type" "fsconv")])
 
(define_insn "floatsidf2"
[(set (match_operand:DF 0 "fpr_operand" "=h")
(float:DF (match_operand:SI 1 "fpr_operand" "f")))]
"TARGET_HARD_FLOAT && TARGET_DOUBLE"
"fitod %1,%0"
[(set_attr "length" "4")
(set_attr "type" "fdconv")])
 
;;(define_insn "floatdisf2"
;; [(set (match_operand:SF 0 "register_operand" "=r")
;; (float:SF (match_operand:DI 1 "register_operand" "r")))]
;; ""
;; "floatdisf2 %0,%1"
;; [(set_attr "length" "4")])
;;
;;(define_insn "floatdidf2"
;; [(set (match_operand:DF 0 "register_operand" "=r")
;; (float:DF (match_operand:DI 1 "register_operand" "r")))]
;; ""
;; "floatdidf2 %0,%1"
;; [(set_attr "length" "4")])
 
(define_insn "fix_truncsfsi2"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(fix:SI (match_operand:SF 1 "fpr_operand" "f")))]
"TARGET_HARD_FLOAT"
"fstoi %1,%0"
[(set_attr "length" "4")
(set_attr "type" "fsconv")])
 
(define_insn "fix_truncdfsi2"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(fix:SI (match_operand:DF 1 "fpr_operand" "h")))]
"TARGET_HARD_FLOAT && TARGET_DOUBLE"
"fdtoi %1,%0"
[(set_attr "length" "4")
(set_attr "type" "fdconv")])
 
;;(define_insn "fix_truncsfdi2"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (fix:DI (match_operand:SF 1 "register_operand" "r")))]
;; ""
;; "fix_truncsfdi2 %0,%1"
;; [(set_attr "length" "4")])
;;
;;(define_insn "fix_truncdfdi2"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (fix:DI (match_operand:DF 1 "register_operand" "r")))]
;; ""
;; "fix_truncdfdi2 %0,%1"
;; [(set_attr "length" "4")])
;;
;;;; Convert between unsigned integer types and floating point.
;;
;;(define_insn "floatunssisf2"
;; [(set (match_operand:SF 0 "register_operand" "=r")
;; (unsigned_float:SF (match_operand:SI 1 "register_operand" "r")))]
;; ""
;; "floatunssisf2 %0,%1"
;; [(set_attr "length" "4")])
;;
;;(define_insn "floatunssidf2"
;; [(set (match_operand:DF 0 "register_operand" "=r")
;; (unsigned_float:DF (match_operand:SI 1 "register_operand" "r")))]
;; ""
;; "floatunssidf2 %0,%1"
;; [(set_attr "length" "4")])
;;
;;(define_insn "floatunsdisf2"
;; [(set (match_operand:SF 0 "register_operand" "=r")
;; (unsigned_float:SF (match_operand:DI 1 "register_operand" "r")))]
;; ""
;; "floatunsdisf2 %0,%1"
;; [(set_attr "length" "4")])
;;
;;(define_insn "floatunsdidf2"
;; [(set (match_operand:DF 0 "register_operand" "=r")
;; (unsigned_float:DF (match_operand:DI 1 "register_operand" "r")))]
;; ""
;; "floatunsdidf2 %0,%1"
;; [(set_attr "length" "4")])
;;
;;(define_insn "fixuns_truncsfsi2"
;; [(set (match_operand:SI 0 "register_operand" "=r")
;; (unsigned_fix:SI (match_operand:SF 1 "register_operand" "r")))]
;; ""
;; "fixuns_truncsfsi2 %0,%1"
;; [(set_attr "length" "4")])
;;
;;(define_insn "fixuns_truncdfsi2"
;; [(set (match_operand:SI 0 "register_operand" "=r")
;; (unsigned_fix:SI (match_operand:DF 1 "register_operand" "r")))]
;; ""
;; "fixuns_truncdfsi2 %0,%1"
;; [(set_attr "length" "4")])
;;
;;(define_insn "fixuns_truncsfdi2"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (unsigned_fix:DI (match_operand:SF 1 "register_operand" "r")))]
;; ""
;; "fixuns_truncsfdi2 %0,%1"
;; [(set_attr "length" "4")])
;;
;;(define_insn "fixuns_truncdfdi2"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (unsigned_fix:DI (match_operand:DF 1 "register_operand" "r")))]
;; ""
;; "fixuns_truncdfdi2 %0,%1"
;; [(set_attr "length" "4")])
 
;; ::::::::::::::::::::
;; ::
;; :: 32 bit Integer arithmetic
;; ::
;; ::::::::::::::::::::
 
;; Addition
(define_insn "addsi3"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(plus:SI (match_operand:SI 1 "integer_register_operand" "%d")
(match_operand:SI 2 "gpr_or_int12_operand" "dNOPQ")))]
""
"add%I2 %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
;; Subtraction. No need to worry about constants, since the compiler
;; canonicalizes them into addsi3's. We prevent SUBREG's here to work around a
;; combine bug, that combines the 32x32->upper 32 bit multiply that uses a
;; SUBREG with a minus that shows up in modulus by constants.
(define_insn "subsi3"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(minus:SI (match_operand:SI 1 "gpr_no_subreg_operand" "d")
(match_operand:SI 2 "gpr_no_subreg_operand" "d")))]
""
"sub %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
;; Signed multiplication producing 64 bit results from 32 bit inputs
;; Note, frv doesn't have a 32x32->32 bit multiply, but the compiler
;; will do the 32x32->64 bit multiply and use the bottom word.
(define_expand "mulsidi3"
[(set (match_operand:DI 0 "integer_register_operand" "")
(mult:DI (sign_extend:DI (match_operand:SI 1 "integer_register_operand" ""))
(sign_extend:DI (match_operand:SI 2 "gpr_or_int12_operand" ""))))]
""
"
{
if (GET_CODE (operands[2]) == CONST_INT)
{
emit_insn (gen_mulsidi3_const (operands[0], operands[1], operands[2]));
DONE;
}
}")
 
(define_insn "*mulsidi3_reg"
[(set (match_operand:DI 0 "even_gpr_operand" "=e")
(mult:DI (sign_extend:DI (match_operand:SI 1 "integer_register_operand" "%d"))
(sign_extend:DI (match_operand:SI 2 "integer_register_operand" "d"))))]
""
"smul %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "mul")])
 
(define_insn "mulsidi3_const"
[(set (match_operand:DI 0 "even_gpr_operand" "=e")
(mult:DI (sign_extend:DI (match_operand:SI 1 "integer_register_operand" "d"))
(match_operand:SI 2 "int12_operand" "NOP")))]
""
"smuli %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "mul")])
 
;; Unsigned multiplication producing 64 bit results from 32 bit inputs
(define_expand "umulsidi3"
[(set (match_operand:DI 0 "even_gpr_operand" "")
(mult:DI (zero_extend:DI (match_operand:SI 1 "integer_register_operand" ""))
(zero_extend:DI (match_operand:SI 2 "gpr_or_int12_operand" ""))))]
""
"
{
if (GET_CODE (operands[2]) == CONST_INT)
{
emit_insn (gen_umulsidi3_const (operands[0], operands[1], operands[2]));
DONE;
}
}")
 
(define_insn "*mulsidi3_reg"
[(set (match_operand:DI 0 "even_gpr_operand" "=e")
(mult:DI (zero_extend:DI (match_operand:SI 1 "integer_register_operand" "%d"))
(zero_extend:DI (match_operand:SI 2 "integer_register_operand" "d"))))]
""
"umul %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "mul")])
 
(define_insn "umulsidi3_const"
[(set (match_operand:DI 0 "even_gpr_operand" "=e")
(mult:DI (zero_extend:DI (match_operand:SI 1 "integer_register_operand" "d"))
(match_operand:SI 2 "int12_operand" "NOP")))]
""
"umuli %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "mul")])
 
;; Signed Division
(define_insn "divsi3"
[(set (match_operand:SI 0 "register_operand" "=d,d")
(div:SI (match_operand:SI 1 "register_operand" "d,d")
(match_operand:SI 2 "gpr_or_int12_operand" "d,NOP")))]
""
"sdiv%I2 %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "div")])
 
;; Unsigned Division
(define_insn "udivsi3"
[(set (match_operand:SI 0 "register_operand" "=d,d")
(udiv:SI (match_operand:SI 1 "register_operand" "d,d")
(match_operand:SI 2 "gpr_or_int12_operand" "d,NOP")))]
""
"udiv%I2 %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "div")])
 
;; Negation
(define_insn "negsi2"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(neg:SI (match_operand:SI 1 "integer_register_operand" "d")))]
""
"sub %.,%1,%0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
;; Find first one bit
;; (define_insn "ffssi2"
;; [(set (match_operand:SI 0 "register_operand" "=r")
;; (ffs:SI (match_operand:SI 1 "register_operand" "r")))]
;; ""
;; "ffssi2 %0,%1"
;; [(set_attr "length" "4")])
 
;; ::::::::::::::::::::
;; ::
;; :: 64 bit Integer arithmetic
;; ::
;; ::::::::::::::::::::
 
;; Addition
(define_insn_and_split "adddi3"
[(set (match_operand:DI 0 "integer_register_operand" "=&e,e")
(plus:DI (match_operand:DI 1 "integer_register_operand" "%e,0")
(match_operand:DI 2 "gpr_or_int10_operand" "eJ,eJ")))
(clobber (match_scratch:CC 3 "=t,t"))]
""
"#"
"reload_completed"
[(match_dup 4)
(match_dup 5)]
"
{
rtx parts[3][2];
int op, part;
 
for (op = 0; op < 3; op++)
for (part = 0; part < 2; part++)
parts[op][part] = simplify_gen_subreg (SImode, operands[op],
DImode, part * UNITS_PER_WORD);
 
operands[4] = gen_adddi3_lower (parts[0][1], parts[1][1], parts[2][1],
operands[3]);
operands[5] = gen_adddi3_upper (parts[0][0], parts[1][0], parts[2][0],
copy_rtx (operands[3]));
}"
[(set_attr "length" "8")
(set_attr "type" "multi")])
 
;; Subtraction No need to worry about constants, since the compiler
;; canonicalizes them into adddi3's.
(define_insn_and_split "subdi3"
[(set (match_operand:DI 0 "integer_register_operand" "=&e,e,e")
(minus:DI (match_operand:DI 1 "integer_register_operand" "e,0,e")
(match_operand:DI 2 "integer_register_operand" "e,e,0")))
(clobber (match_scratch:CC 3 "=t,t,t"))]
""
"#"
"reload_completed"
[(match_dup 4)
(match_dup 5)]
"
{
rtx op0_high = gen_highpart (SImode, operands[0]);
rtx op1_high = gen_highpart (SImode, operands[1]);
rtx op2_high = gen_highpart (SImode, operands[2]);
rtx op0_low = gen_lowpart (SImode, operands[0]);
rtx op1_low = gen_lowpart (SImode, operands[1]);
rtx op2_low = gen_lowpart (SImode, operands[2]);
rtx op3 = operands[3];
 
operands[4] = gen_subdi3_lower (op0_low, op1_low, op2_low, op3);
operands[5] = gen_subdi3_upper (op0_high, op1_high, op2_high, op3);
}"
[(set_attr "length" "8")
(set_attr "type" "multi")])
 
;; Patterns for addsi3/subdi3 after splitting
(define_insn "adddi3_lower"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(plus:SI (match_operand:SI 1 "integer_register_operand" "d")
(match_operand:SI 2 "gpr_or_int10_operand" "dJ")))
(set (match_operand:CC 3 "icc_operand" "=t")
(compare:CC (plus:SI (match_dup 1)
(match_dup 2))
(const_int 0)))]
""
"add%I2cc %1,%2,%0,%3"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_insn "adddi3_upper"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(plus:SI (match_operand:SI 1 "integer_register_operand" "d")
(plus:SI (match_operand:SI 2 "gpr_or_int10_operand" "dJ")
(match_operand:CC 3 "icc_operand" "t"))))]
""
"addx%I2 %1,%2,%0,%3"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_insn "subdi3_lower"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(minus:SI (match_operand:SI 1 "integer_register_operand" "d")
(match_operand:SI 2 "integer_register_operand" "d")))
(set (match_operand:CC 3 "icc_operand" "=t")
(compare:CC (plus:SI (match_dup 1)
(match_dup 2))
(const_int 0)))]
""
"subcc %1,%2,%0,%3"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_insn "subdi3_upper"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(minus:SI (match_operand:SI 1 "integer_register_operand" "d")
(minus:SI (match_operand:SI 2 "integer_register_operand" "d")
(match_operand:CC 3 "icc_operand" "t"))))]
""
"subx %1,%2,%0,%3"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_insn_and_split "negdi2"
[(set (match_operand:DI 0 "integer_register_operand" "=&e,e")
(neg:DI (match_operand:DI 1 "integer_register_operand" "e,0")))
(clobber (match_scratch:CC 2 "=t,t"))]
""
"#"
"reload_completed"
[(match_dup 3)
(match_dup 4)]
"
{
rtx op0_high = gen_highpart (SImode, operands[0]);
rtx op1_high = gen_rtx_REG (SImode, GPR_FIRST);
rtx op2_high = gen_highpart (SImode, operands[1]);
rtx op0_low = gen_lowpart (SImode, operands[0]);
rtx op1_low = op1_high;
rtx op2_low = gen_lowpart (SImode, operands[1]);
rtx op3 = operands[2];
 
operands[3] = gen_subdi3_lower (op0_low, op1_low, op2_low, op3);
operands[4] = gen_subdi3_upper (op0_high, op1_high, op2_high, op3);
}"
[(set_attr "length" "8")
(set_attr "type" "multi")])
 
;; Multiplication (same size)
;; (define_insn "muldi3"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (mult:DI (match_operand:DI 1 "register_operand" "%r")
;; (match_operand:DI 2 "nonmemory_operand" "ri")))]
;; ""
;; "muldi3 %0,%1,%2"
;; [(set_attr "length" "4")])
 
;; Signed Division
;; (define_insn "divdi3"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (div:DI (match_operand:DI 1 "register_operand" "r")
;; (match_operand:DI 2 "nonmemory_operand" "ri")))]
;; ""
;; "divdi3 %0,%1,%2"
;; [(set_attr "length" "4")])
 
;; Undsgned Division
;; (define_insn "udivdi3"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (udiv:DI (match_operand:DI 1 "register_operand" "r")
;; (match_operand:DI 2 "nonmemory_operand" "ri")))]
;; ""
;; "udivdi3 %0,%1,%2"
;; [(set_attr "length" "4")])
 
;; Negation
;; (define_insn "negdi2"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (neg:DI (match_operand:DI 1 "register_operand" "r")))]
;; ""
;; "negdi2 %0,%1"
;; [(set_attr "length" "4")])
 
;; Find first one bit
;; (define_insn "ffsdi2"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (ffs:DI (match_operand:DI 1 "register_operand" "r")))]
;; ""
;; "ffsdi2 %0,%1"
;; [(set_attr "length" "4")])
 
;; ::::::::::::::::::::
;; ::
;; :: 32 bit floating point arithmetic
;; ::
;; ::::::::::::::::::::
 
;; Addition
(define_insn "addsf3"
[(set (match_operand:SF 0 "fpr_operand" "=f")
(plus:SF (match_operand:SF 1 "fpr_operand" "%f")
(match_operand:SF 2 "fpr_operand" "f")))]
"TARGET_HARD_FLOAT"
"fadds %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "fsadd")])
 
;; Subtraction
(define_insn "subsf3"
[(set (match_operand:SF 0 "fpr_operand" "=f")
(minus:SF (match_operand:SF 1 "fpr_operand" "f")
(match_operand:SF 2 "fpr_operand" "f")))]
"TARGET_HARD_FLOAT"
"fsubs %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "fsadd")])
 
;; Multiplication
(define_insn "mulsf3"
[(set (match_operand:SF 0 "fpr_operand" "=f")
(mult:SF (match_operand:SF 1 "fpr_operand" "%f")
(match_operand:SF 2 "fpr_operand" "f")))]
"TARGET_HARD_FLOAT"
"fmuls %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "fsmul")])
 
;; Multiplication with addition/subtraction
(define_insn "*muladdsf4"
[(set (match_operand:SF 0 "fpr_operand" "=f")
(plus:SF (mult:SF (match_operand:SF 1 "fpr_operand" "%f")
(match_operand:SF 2 "fpr_operand" "f"))
(match_operand:SF 3 "fpr_operand" "0")))]
"TARGET_HARD_FLOAT && TARGET_MULADD"
"fmadds %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "fsmadd")])
 
(define_insn "*mulsubsf4"
[(set (match_operand:SF 0 "fpr_operand" "=f")
(minus:SF (mult:SF (match_operand:SF 1 "fpr_operand" "%f")
(match_operand:SF 2 "fpr_operand" "f"))
(match_operand:SF 3 "fpr_operand" "0")))]
"TARGET_HARD_FLOAT && TARGET_MULADD"
"fmsubs %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "fsmadd")])
 
;; Division
(define_insn "divsf3"
[(set (match_operand:SF 0 "fpr_operand" "=f")
(div:SF (match_operand:SF 1 "fpr_operand" "f")
(match_operand:SF 2 "fpr_operand" "f")))]
"TARGET_HARD_FLOAT"
"fdivs %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "fsdiv")])
 
;; Negation
(define_insn "negsf2"
[(set (match_operand:SF 0 "fpr_operand" "=f")
(neg:SF (match_operand:SF 1 "fpr_operand" "f")))]
"TARGET_HARD_FLOAT"
"fnegs %1,%0"
[(set_attr "length" "4")
(set_attr "type" "fsconv")])
 
;; Absolute value
(define_insn "abssf2"
[(set (match_operand:SF 0 "fpr_operand" "=f")
(abs:SF (match_operand:SF 1 "fpr_operand" "f")))]
"TARGET_HARD_FLOAT"
"fabss %1,%0"
[(set_attr "length" "4")
(set_attr "type" "fsconv")])
 
;; Square root
(define_insn "sqrtsf2"
[(set (match_operand:SF 0 "fpr_operand" "=f")
(sqrt:SF (match_operand:SF 1 "fpr_operand" "f")))]
"TARGET_HARD_FLOAT"
"fsqrts %1,%0"
[(set_attr "length" "4")
(set_attr "type" "sqrt_single")])
 
;; ::::::::::::::::::::
;; ::
;; :: 64 bit floating point arithmetic
;; ::
;; ::::::::::::::::::::
 
;; Addition
(define_insn "adddf3"
[(set (match_operand:DF 0 "even_fpr_operand" "=h")
(plus:DF (match_operand:DF 1 "fpr_operand" "%h")
(match_operand:DF 2 "fpr_operand" "h")))]
"TARGET_HARD_FLOAT && TARGET_DOUBLE"
"faddd %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "fdadd")])
 
;; Subtraction
(define_insn "subdf3"
[(set (match_operand:DF 0 "even_fpr_operand" "=h")
(minus:DF (match_operand:DF 1 "fpr_operand" "h")
(match_operand:DF 2 "fpr_operand" "h")))]
"TARGET_HARD_FLOAT && TARGET_DOUBLE"
"fsubd %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "fdadd")])
 
;; Multiplication
(define_insn "muldf3"
[(set (match_operand:DF 0 "even_fpr_operand" "=h")
(mult:DF (match_operand:DF 1 "fpr_operand" "%h")
(match_operand:DF 2 "fpr_operand" "h")))]
"TARGET_HARD_FLOAT && TARGET_DOUBLE"
"fmuld %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "fdmul")])
 
;; Multiplication with addition/subtraction
(define_insn "*muladddf4"
[(set (match_operand:DF 0 "fpr_operand" "=f")
(plus:DF (mult:DF (match_operand:DF 1 "fpr_operand" "%f")
(match_operand:DF 2 "fpr_operand" "f"))
(match_operand:DF 3 "fpr_operand" "0")))]
"TARGET_HARD_FLOAT && TARGET_DOUBLE && TARGET_MULADD"
"fmaddd %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "fdmadd")])
 
(define_insn "*mulsubdf4"
[(set (match_operand:DF 0 "fpr_operand" "=f")
(minus:DF (mult:DF (match_operand:DF 1 "fpr_operand" "%f")
(match_operand:DF 2 "fpr_operand" "f"))
(match_operand:DF 3 "fpr_operand" "0")))]
"TARGET_HARD_FLOAT && TARGET_DOUBLE && TARGET_MULADD"
"fmsubd %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "fdmadd")])
 
;; Division
(define_insn "divdf3"
[(set (match_operand:DF 0 "even_fpr_operand" "=h")
(div:DF (match_operand:DF 1 "fpr_operand" "h")
(match_operand:DF 2 "fpr_operand" "h")))]
"TARGET_HARD_FLOAT && TARGET_DOUBLE"
"fdivd %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "fddiv")])
 
;; Negation
(define_insn "negdf2"
[(set (match_operand:DF 0 "even_fpr_operand" "=h")
(neg:DF (match_operand:DF 1 "fpr_operand" "h")))]
"TARGET_HARD_FLOAT && TARGET_DOUBLE"
"fnegd %1,%0"
[(set_attr "length" "4")
(set_attr "type" "fdconv")])
 
;; Absolute value
(define_insn "absdf2"
[(set (match_operand:DF 0 "even_fpr_operand" "=h")
(abs:DF (match_operand:DF 1 "fpr_operand" "h")))]
"TARGET_HARD_FLOAT && TARGET_DOUBLE"
"fabsd %1,%0"
[(set_attr "length" "4")
(set_attr "type" "fdconv")])
 
;; Square root
(define_insn "sqrtdf2"
[(set (match_operand:DF 0 "even_fpr_operand" "=h")
(sqrt:DF (match_operand:DF 1 "fpr_operand" "h")))]
"TARGET_HARD_FLOAT && TARGET_DOUBLE"
"fsqrtd %1,%0"
[(set_attr "length" "4")
(set_attr "type" "sqrt_double")])
 
;; ::::::::::::::::::::
;; ::
;; :: 32 bit Integer Shifts and Rotates
;; ::
;; ::::::::::::::::::::
 
;; Arithmetic Shift Left
(define_insn "ashlsi3"
[(set (match_operand:SI 0 "integer_register_operand" "=d,d")
(ashift:SI (match_operand:SI 1 "integer_register_operand" "d,d")
(match_operand:SI 2 "gpr_or_int12_operand" "d,NOP")))]
""
"sll%I2 %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
;; Arithmetic Shift Right
(define_insn "ashrsi3"
[(set (match_operand:SI 0 "integer_register_operand" "=d,d")
(ashiftrt:SI (match_operand:SI 1 "integer_register_operand" "d,d")
(match_operand:SI 2 "gpr_or_int12_operand" "d,NOP")))]
""
"sra%I2 %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
;; Logical Shift Right
(define_insn "lshrsi3"
[(set (match_operand:SI 0 "integer_register_operand" "=d,d")
(lshiftrt:SI (match_operand:SI 1 "integer_register_operand" "d,d")
(match_operand:SI 2 "gpr_or_int12_operand" "d,NOP")))]
""
"srl%I2 %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
;; Rotate Left
;; (define_insn "rotlsi3"
;; [(set (match_operand:SI 0 "register_operand" "=r")
;; (rotate:SI (match_operand:SI 1 "register_operand" "r")
;; (match_operand:SI 2 "nonmemory_operand" "ri")))]
;; ""
;; "rotlsi3 %0,%1,%2"
;; [(set_attr "length" "4")])
 
;; Rotate Right
;; (define_insn "rotrsi3"
;; [(set (match_operand:SI 0 "register_operand" "=r")
;; (rotatert:SI (match_operand:SI 1 "register_operand" "r")
;; (match_operand:SI 2 "nonmemory_operand" "ri")))]
;; ""
;; "rotrsi3 %0,%1,%2"
;; [(set_attr "length" "4")])
 
;; ::::::::::::::::::::
;; ::
;; :: 64 bit Integer Shifts and Rotates
;; ::
;; ::::::::::::::::::::
 
;; Arithmetic Shift Left
;; (define_insn "ashldi3"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (ashift:DI (match_operand:DI 1 "register_operand" "r")
;; (match_operand:SI 2 "nonmemory_operand" "ri")))]
;; ""
;; "ashldi3 %0,%1,%2"
;; [(set_attr "length" "4")])
 
;; Arithmetic Shift Right
;; (define_insn "ashrdi3"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (ashiftrt:DI (match_operand:DI 1 "register_operand" "r")
;; (match_operand:SI 2 "nonmemory_operand" "ri")))]
;; ""
;; "ashrdi3 %0,%1,%2"
;; [(set_attr "length" "4")])
 
;; Logical Shift Right
;; (define_insn "lshrdi3"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (lshiftrt:DI (match_operand:DI 1 "register_operand" "r")
;; (match_operand:SI 2 "nonmemory_operand" "ri")))]
;; ""
;; "lshrdi3 %0,%1,%2"
;; [(set_attr "length" "4")])
 
;; Rotate Left
;; (define_insn "rotldi3"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (rotate:DI (match_operand:DI 1 "register_operand" "r")
;; (match_operand:SI 2 "nonmemory_operand" "ri")))]
;; ""
;; "rotldi3 %0,%1,%2"
;; [(set_attr "length" "4")])
 
;; Rotate Right
;; (define_insn "rotrdi3"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (rotatert:DI (match_operand:DI 1 "register_operand" "r")
;; (match_operand:SI 2 "nonmemory_operand" "ri")))]
;; ""
;; "rotrdi3 %0,%1,%2"
;; [(set_attr "length" "4")])
 
;; ::::::::::::::::::::
;; ::
;; :: 32 Bit Integer Logical operations
;; ::
;; ::::::::::::::::::::
 
;; Logical AND, 32 bit integers
(define_insn "andsi3_media"
[(set (match_operand:SI 0 "gpr_or_fpr_operand" "=d,f")
(and:SI (match_operand:SI 1 "gpr_or_fpr_operand" "%d,f")
(match_operand:SI 2 "gpr_fpr_or_int12_operand" "dNOP,f")))]
"TARGET_MEDIA"
"@
and%I2 %1, %2, %0
mand %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "int,mlogic")])
 
(define_insn "andsi3_nomedia"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(and:SI (match_operand:SI 1 "integer_register_operand" "%d")
(match_operand:SI 2 "gpr_or_int12_operand" "dNOP")))]
"!TARGET_MEDIA"
"and%I2 %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_expand "andsi3"
[(set (match_operand:SI 0 "gpr_or_fpr_operand" "")
(and:SI (match_operand:SI 1 "gpr_or_fpr_operand" "")
(match_operand:SI 2 "gpr_fpr_or_int12_operand" "")))]
""
"")
 
;; Inclusive OR, 32 bit integers
(define_insn "iorsi3_media"
[(set (match_operand:SI 0 "gpr_or_fpr_operand" "=d,f")
(ior:SI (match_operand:SI 1 "gpr_or_fpr_operand" "%d,f")
(match_operand:SI 2 "gpr_fpr_or_int12_operand" "dNOP,f")))]
"TARGET_MEDIA"
"@
or%I2 %1, %2, %0
mor %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "int,mlogic")])
 
(define_insn "iorsi3_nomedia"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(ior:SI (match_operand:SI 1 "integer_register_operand" "%d")
(match_operand:SI 2 "gpr_or_int12_operand" "dNOP")))]
"!TARGET_MEDIA"
"or%I2 %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_expand "iorsi3"
[(set (match_operand:SI 0 "gpr_or_fpr_operand" "")
(ior:SI (match_operand:SI 1 "gpr_or_fpr_operand" "")
(match_operand:SI 2 "gpr_fpr_or_int12_operand" "")))]
""
"")
 
;; Exclusive OR, 32 bit integers
(define_insn "xorsi3_media"
[(set (match_operand:SI 0 "gpr_or_fpr_operand" "=d,f")
(xor:SI (match_operand:SI 1 "gpr_or_fpr_operand" "%d,f")
(match_operand:SI 2 "gpr_fpr_or_int12_operand" "dNOP,f")))]
"TARGET_MEDIA"
"@
xor%I2 %1, %2, %0
mxor %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "int,mlogic")])
 
(define_insn "xorsi3_nomedia"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(xor:SI (match_operand:SI 1 "integer_register_operand" "%d")
(match_operand:SI 2 "gpr_or_int12_operand" "dNOP")))]
"!TARGET_MEDIA"
"xor%I2 %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_expand "xorsi3"
[(set (match_operand:SI 0 "gpr_or_fpr_operand" "")
(xor:SI (match_operand:SI 1 "gpr_or_fpr_operand" "")
(match_operand:SI 2 "gpr_fpr_or_int12_operand" "")))]
""
"")
 
;; One's complement, 32 bit integers
(define_insn "one_cmplsi2_media"
[(set (match_operand:SI 0 "gpr_or_fpr_operand" "=d,f")
(not:SI (match_operand:SI 1 "gpr_or_fpr_operand" "d,f")))]
"TARGET_MEDIA"
"@
not %1, %0
mnot %1, %0"
[(set_attr "length" "4")
(set_attr "type" "int,mlogic")])
 
(define_insn "one_cmplsi2_nomedia"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(not:SI (match_operand:SI 1 "integer_register_operand" "d")))]
"!TARGET_MEDIA"
"not %1,%0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_expand "one_cmplsi2"
[(set (match_operand:SI 0 "gpr_or_fpr_operand" "")
(not:SI (match_operand:SI 1 "gpr_or_fpr_operand" "")))]
""
"")
 
;; ::::::::::::::::::::
;; ::
;; :: 64 Bit Integer Logical operations
;; ::
;; ::::::::::::::::::::
 
;; Logical AND, 64 bit integers
;; (define_insn "anddi3"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (and:DI (match_operand:DI 1 "register_operand" "%r")
;; (match_operand:DI 2 "nonmemory_operand" "ri")))]
;; ""
;; "anddi3 %0,%1,%2"
;; [(set_attr "length" "4")])
 
;; Inclusive OR, 64 bit integers
;; (define_insn "iordi3"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (ior:DI (match_operand:DI 1 "register_operand" "%r")
;; (match_operand:DI 2 "nonmemory_operand" "ri")))]
;; ""
;; "iordi3 %0,%1,%2"
;; [(set_attr "length" "4")])
 
;; Exclusive OR, 64 bit integers
;; (define_insn "xordi3"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (xor:DI (match_operand:DI 1 "register_operand" "%r")
;; (match_operand:DI 2 "nonmemory_operand" "ri")))]
;; ""
;; "xordi3 %0,%1,%2"
;; [(set_attr "length" "4")])
 
;; One's complement, 64 bit integers
;; (define_insn "one_cmpldi2"
;; [(set (match_operand:DI 0 "register_operand" "=r")
;; (not:DI (match_operand:DI 1 "register_operand" "r")))]
;; ""
;; "notdi3 %0,%1"
;; [(set_attr "length" "4")])
 
;; ::::::::::::::::::::
;; ::
;; :: Combination of integer operation with comparison
;; ::
;; ::::::::::::::::::::
 
(define_insn "*combo_intop_compare1"
[(set (match_operand:CC_NZ 0 "icc_operand" "=t")
(compare:CC_NZ
(match_operator:SI 1 "intop_compare_operator"
[(match_operand:SI 2 "integer_register_operand" "d")
(match_operand:SI 3 "gpr_or_int10_operand" "dJ")])
(const_int 0)))]
""
"%O1%I3cc %2, %3, %., %0"
[(set_attr "type" "int")
(set_attr "length" "4")])
 
(define_insn "*combo_intop_compare2"
[(set (match_operand:CC_NZ 0 "icc_operand" "=t")
(compare:CC_NZ
(match_operator:SI 1 "intop_compare_operator"
[(match_operand:SI 2 "integer_register_operand" "d")
(match_operand:SI 3 "gpr_or_int10_operand" "dJ")])
(const_int 0)))
(set (match_operand:SI 4 "integer_register_operand" "=d")
(match_operator:SI 5 "intop_compare_operator"
[(match_dup 2)
(match_dup 3)]))]
"GET_CODE (operands[1]) == GET_CODE (operands[5])"
"%O1%I3cc %2, %3, %4, %0"
[(set_attr "type" "int")
(set_attr "length" "4")])
;; ::::::::::::::::::::
;; ::
;; :: Comparisons
;; ::
;; ::::::::::::::::::::
 
;; Note, we store the operands in the comparison insns, and use them later
;; when generating the branch or scc operation.
 
;; First the routines called by the machine independent part of the compiler
(define_expand "cmpsi"
[(set (cc0)
(compare (match_operand:SI 0 "integer_register_operand" "")
(match_operand:SI 1 "gpr_or_int10_operand" "")))]
""
"
{
frv_compare_op0 = operands[0];
frv_compare_op1 = operands[1];
DONE;
}")
 
;(define_expand "cmpdi"
; [(set (cc0)
; (compare (match_operand:DI 0 "register_operand" "")
; (match_operand:DI 1 "nonmemory_operand" "")))]
; ""
; "
;{
; frv_compare_op0 = operands[0];
; frv_compare_op1 = operands[1];
; DONE;
;}")
 
(define_expand "cmpsf"
[(set (cc0)
(compare (match_operand:SF 0 "fpr_operand" "")
(match_operand:SF 1 "fpr_operand" "")))]
"TARGET_HARD_FLOAT"
"
{
frv_compare_op0 = operands[0];
frv_compare_op1 = operands[1];
DONE;
}")
 
(define_expand "cmpdf"
[(set (cc0)
(compare (match_operand:DF 0 "fpr_operand" "")
(match_operand:DF 1 "fpr_operand" "")))]
"TARGET_HARD_FLOAT && TARGET_DOUBLE"
"
{
frv_compare_op0 = operands[0];
frv_compare_op1 = operands[1];
DONE;
}")
 
;; Now, the actual comparisons, generated by the branch and/or scc operations
 
(define_insn "cmpsi_cc"
[(set (match_operand:CC 0 "icc_operand" "=t,t")
(compare:CC (match_operand:SI 1 "integer_register_operand" "d,d")
(match_operand:SI 2 "gpr_or_int10_operand" "d,J")))]
""
"cmp%I2 %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_insn "*cmpsi_cc_uns"
[(set (match_operand:CC_UNS 0 "icc_operand" "=t,t")
(compare:CC_UNS (match_operand:SI 1 "integer_register_operand" "d,d")
(match_operand:SI 2 "gpr_or_int10_operand" "d,J")))]
""
"cmp%I2 %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
;; The only requirement for a CC_NZmode GPR or memory value is that
;; comparing it against zero must set the Z and N flags appropriately.
;; The source operand is therefore a valid CC_NZmode value.
(define_insn "*cmpsi_cc_nz"
[(set (match_operand:CC_NZ 0 "nonimmediate_operand" "=t,d,m")
(compare:CC_NZ (match_operand:SI 1 "integer_register_operand" "d,d,d")
(const_int 0)))]
""
"@
cmpi %1, #0, %0
mov %1, %0
st%I0%U0 %1, %M0"
[(set_attr "length" "4,4,4")
(set_attr "type" "int,int,gstore")])
 
(define_insn "*cmpsf_cc_fp"
[(set (match_operand:CC_FP 0 "fcc_operand" "=u")
(compare:CC_FP (match_operand:SF 1 "fpr_operand" "f")
(match_operand:SF 2 "fpr_operand" "f")))]
"TARGET_HARD_FLOAT"
"fcmps %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "fscmp")])
 
(define_insn "*cmpdf_cc_fp"
[(set (match_operand:CC_FP 0 "fcc_operand" "=u")
(compare:CC_FP (match_operand:DF 1 "even_fpr_operand" "h")
(match_operand:DF 2 "even_fpr_operand" "h")))]
"TARGET_HARD_FLOAT && TARGET_DOUBLE"
"fcmpd %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "fdcmp")])
 
;; ::::::::::::::::::::
;; ::
;; :: Branches
;; ::
;; ::::::::::::::::::::
 
;; Define_expands called by the machine independent part of the compiler
;; to allocate a new comparison register. Each of these named patterns
;; must be present, and they cannot be amalgamated into one pattern.
;;
;; If a fixed condition code register is being used, (as opposed to, say,
;; using cc0), then the expands should look like this:
;;
;; (define_expand "<name_of_test>"
;; [(set (reg:CC <number_of_CC_register>)
;; (compare:CC (match_dup 1)
;; (match_dup 2)))
;; (set (pc)
;; (if_then_else (eq:CC (reg:CC <number_of_CC_register>)
;; (const_int 0))
;; (label_ref (match_operand 0 "" ""))
;; (pc)))]
;; ""
;; "{
;; operands[1] = frv_compare_op0;
;; operands[2] = frv_compare_op1;
;; }"
;; )
 
(define_expand "beq"
[(use (match_operand 0 "" ""))]
""
"
{
if (! frv_emit_cond_branch (EQ, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "bne"
[(use (match_operand 0 "" ""))]
""
"
{
if (! frv_emit_cond_branch (NE, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "blt"
[(use (match_operand 0 "" ""))]
""
"
{
if (! frv_emit_cond_branch (LT, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "ble"
[(use (match_operand 0 "" ""))]
""
"
{
if (! frv_emit_cond_branch (LE, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "bgt"
[(use (match_operand 0 "" ""))]
""
"
{
if (! frv_emit_cond_branch (GT, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "bge"
[(use (match_operand 0 "" ""))]
""
"
{
if (! frv_emit_cond_branch (GE, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "bltu"
[(use (match_operand 0 "" ""))]
""
"
{
if (! frv_emit_cond_branch (LTU, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "bleu"
[(use (match_operand 0 "" ""))]
""
"
{
if (! frv_emit_cond_branch (LEU, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "bgtu"
[(use (match_operand 0 "" ""))]
""
"
{
if (! frv_emit_cond_branch (GTU, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "bgeu"
[(use (match_operand 0 "" ""))]
""
"
{
if (! frv_emit_cond_branch (GEU, operands[0]))
FAIL;
 
DONE;
}")
 
;; Actual branches. We must allow for the (label_ref) and the (pc) to be
;; swapped. If they are swapped, it reverses the sense of the branch.
;;
;; Note - unlike the define expands above, these patterns can be amalgamated
;; into one pattern for branch-if-true and one for branch-if-false. This does
;; require an operand operator to select the correct branch mnemonic.
;;
;; If a fixed condition code register is being used, (as opposed to, say,
;; using cc0), then the expands could look like this:
;;
;; (define_insn "*branch_true"
;; [(set (pc)
;; (if_then_else (match_operator:CC 0 "comparison_operator"
;; [(reg:CC <number_of_CC_register>)
;; (const_int 0)])
;; (label_ref (match_operand 1 "" ""))
;; (pc)))]
;; ""
;; "b%B0 %1"
;; [(set_attr "length" "4")]
;; )
;;
;; In the above example the %B is a directive to frv_print_operand()
;; to decode and print the correct branch mnemonic.
 
(define_insn "*branch_int_true"
[(set (pc)
(if_then_else (match_operator 0 "integer_relational_operator"
[(match_operand 1 "icc_operand" "t")
(const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
""
"*
{
if (get_attr_length (insn) == 4)
return \"b%c0 %1,%#,%l2\";
else
return \"b%C0 %1,%#,1f\;call %l2\\n1:\";
}"
[(set (attr "length")
(if_then_else
(and (ge (minus (match_dup 2) (pc)) (const_int -32768))
(le (minus (match_dup 2) (pc)) (const_int 32764)))
(const_int 4)
(const_int 8)))
(set (attr "far_jump")
(if_then_else
(eq_attr "length" "4")
(const_string "no")
(const_string "yes")))
(set (attr "type")
(if_then_else
(eq_attr "length" "4")
(const_string "branch")
(const_string "multi")))])
 
(define_insn "*branch_int_false"
[(set (pc)
(if_then_else (match_operator 0 "integer_relational_operator"
[(match_operand 1 "icc_operand" "t")
(const_int 0)])
(pc)
(label_ref (match_operand 2 "" ""))))]
""
"*
{
if (get_attr_length (insn) == 4)
return \"b%C0 %1,%#,%l2\";
else
return \"b%c0 %1,%#,1f\;call %l2\\n1:\";
}"
[(set (attr "length")
(if_then_else
(and (ge (minus (match_dup 2) (pc)) (const_int -32768))
(le (minus (match_dup 2) (pc)) (const_int 32764)))
(const_int 4)
(const_int 8)))
(set (attr "far_jump")
(if_then_else
(eq_attr "length" "4")
(const_string "no")
(const_string "yes")))
(set (attr "type")
(if_then_else
(eq_attr "length" "4")
(const_string "branch")
(const_string "multi")))])
 
(define_insn "*branch_fp_true"
[(set (pc)
(if_then_else (match_operator:CC_FP 0 "float_relational_operator"
[(match_operand 1 "fcc_operand" "u")
(const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
""
"*
{
if (get_attr_length (insn) == 4)
return \"fb%f0 %1,%#,%l2\";
else
return \"fb%F0 %1,%#,1f\;call %l2\\n1:\";
}"
[(set (attr "length")
(if_then_else
(and (ge (minus (match_dup 2) (pc)) (const_int -32768))
(le (minus (match_dup 2) (pc)) (const_int 32764)))
(const_int 4)
(const_int 8)))
(set (attr "far_jump")
(if_then_else
(eq_attr "length" "4")
(const_string "no")
(const_string "yes")))
(set (attr "type")
(if_then_else
(eq_attr "length" "4")
(const_string "branch")
(const_string "multi")))])
 
(define_insn "*branch_fp_false"
[(set (pc)
(if_then_else (match_operator:CC_FP 0 "float_relational_operator"
[(match_operand 1 "fcc_operand" "u")
(const_int 0)])
(pc)
(label_ref (match_operand 2 "" ""))))]
""
"*
{
if (get_attr_length (insn) == 4)
return \"fb%F0 %1,%#,%l2\";
else
return \"fb%f0 %1,%#,1f\;call %l2\\n1:\";
}"
[(set (attr "length")
(if_then_else
(and (ge (minus (match_dup 2) (pc)) (const_int -32768))
(le (minus (match_dup 2) (pc)) (const_int 32764)))
(const_int 4)
(const_int 8)))
(set (attr "far_jump")
(if_then_else
(eq_attr "length" "4")
(const_string "no")
(const_string "yes")))
(set (attr "type")
(if_then_else
(eq_attr "length" "4")
(const_string "branch")
(const_string "multi")))])
 
;; ::::::::::::::::::::
;; ::
;; :: Set flag operations
;; ::
;; ::::::::::::::::::::
 
;; Define_expands called by the machine independent part of the compiler
;; to allocate a new comparison register
 
(define_expand "seq"
[(match_operand:SI 0 "integer_register_operand" "")]
"TARGET_SCC"
"
{
if (! frv_emit_scc (EQ, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "sne"
[(match_operand:SI 0 "integer_register_operand" "")]
"TARGET_SCC"
"
{
if (! frv_emit_scc (NE, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "slt"
[(match_operand:SI 0 "integer_register_operand" "")]
"TARGET_SCC"
"
{
if (! frv_emit_scc (LT, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "sle"
[(match_operand:SI 0 "integer_register_operand" "")]
"TARGET_SCC"
"
{
if (! frv_emit_scc (LE, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "sgt"
[(match_operand:SI 0 "integer_register_operand" "")]
"TARGET_SCC"
"
{
if (! frv_emit_scc (GT, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "sge"
[(match_operand:SI 0 "integer_register_operand" "")]
"TARGET_SCC"
"
{
if (! frv_emit_scc (GE, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "sltu"
[(match_operand:SI 0 "integer_register_operand" "")]
"TARGET_SCC"
"
{
if (! frv_emit_scc (LTU, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "sleu"
[(match_operand:SI 0 "integer_register_operand" "")]
"TARGET_SCC"
"
{
if (! frv_emit_scc (LEU, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "sgtu"
[(match_operand:SI 0 "integer_register_operand" "")]
"TARGET_SCC"
"
{
if (! frv_emit_scc (GTU, operands[0]))
FAIL;
 
DONE;
}")
 
(define_expand "sgeu"
[(match_operand:SI 0 "integer_register_operand" "")]
"TARGET_SCC"
"
{
if (! frv_emit_scc (GEU, operands[0]))
FAIL;
 
DONE;
}")
 
(define_insn "*scc_int"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(match_operator:SI 1 "integer_relational_operator"
[(match_operand 2 "icc_operand" "t")
(const_int 0)]))
(clobber (match_operand:CC_CCR 3 "icr_operand" "=v"))]
""
"#"
[(set_attr "length" "12")
(set_attr "type" "multi")])
 
(define_insn "*scc_float"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(match_operator:SI 1 "float_relational_operator"
[(match_operand:CC_FP 2 "fcc_operand" "u")
(const_int 0)]))
(clobber (match_operand:CC_CCR 3 "fcr_operand" "=w"))]
""
"#"
[(set_attr "length" "12")
(set_attr "type" "multi")])
 
;; XXX -- add reload_completed to the splits, because register allocation
;; currently isn't ready to see cond_exec packets.
(define_split
[(set (match_operand:SI 0 "integer_register_operand" "")
(match_operator:SI 1 "relational_operator"
[(match_operand 2 "cc_operand" "")
(const_int 0)]))
(clobber (match_operand 3 "cr_operand" ""))]
"reload_completed"
[(match_dup 4)]
"operands[4] = frv_split_scc (operands[0], operands[1], operands[2],
operands[3], (HOST_WIDE_INT) 1);")
 
(define_insn "*scc_neg1_int"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(neg:SI (match_operator:SI 1 "integer_relational_operator"
[(match_operand 2 "icc_operand" "t")
(const_int 0)])))
(clobber (match_operand:CC_CCR 3 "icr_operand" "=v"))]
""
"#"
[(set_attr "length" "12")
(set_attr "type" "multi")])
 
(define_insn "*scc_neg1_float"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(neg:SI (match_operator:SI 1 "float_relational_operator"
[(match_operand:CC_FP 2 "fcc_operand" "u")
(const_int 0)])))
(clobber (match_operand:CC_CCR 3 "fcr_operand" "=w"))]
""
"#"
[(set_attr "length" "12")
(set_attr "type" "multi")])
 
(define_split
[(set (match_operand:SI 0 "integer_register_operand" "")
(neg:SI (match_operator:SI 1 "relational_operator"
[(match_operand 2 "cc_operand" "")
(const_int 0)])))
(clobber (match_operand 3 "cr_operand" ""))]
"reload_completed"
[(match_dup 4)]
"operands[4] = frv_split_scc (operands[0], operands[1], operands[2],
operands[3], (HOST_WIDE_INT) -1);")
 
;; ::::::::::::::::::::
;; ::
;; :: Conditionally executed instructions
;; ::
;; ::::::::::::::::::::
 
;; Convert ICC/FCC comparison into CCR bits so we can do conditional execution
(define_insn "*ck_signed"
[(set (match_operand:CC_CCR 0 "icr_operand" "=v")
(match_operator:CC_CCR 1 "integer_relational_operator"
[(match_operand 2 "icc_operand" "t")
(const_int 0)]))]
""
"ck%c1 %2, %0"
[(set_attr "length" "4")
(set_attr "type" "ccr")])
 
(define_insn "*fck_float"
[(set (match_operand:CC_CCR 0 "fcr_operand" "=w")
(match_operator:CC_CCR 1 "float_relational_operator"
[(match_operand:CC_FP 2 "fcc_operand" "u")
(const_int 0)]))]
"TARGET_HAS_FPRS"
"fck%c1 %2, %0"
[(set_attr "length" "4")
(set_attr "type" "ccr")])
 
;; Conditionally convert ICC/FCC comparison into CCR bits to provide && and ||
;; tests in conditional execution
(define_insn "cond_exec_ck"
[(set (match_operand:CC_CCR 0 "cr_operand" "=v,w")
(if_then_else:CC_CCR (match_operator 1 "ccr_eqne_operator"
[(match_operand 2 "cr_operand" "C,C")
(const_int 0)])
(match_operator 3 "relational_operator"
[(match_operand 4 "cc_operand" "t,u")
(const_int 0)])
(const_int 0)))]
""
"@
cck%c3 %4, %0, %2, %e1
cfck%f3 %4, %0, %2, %e1"
[(set_attr "length" "4")
(set_attr "type" "ccr")])
 
;; Conditionally set a register to either 0 or another register
(define_insn "*cond_exec_movqi"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C,C,C,C,C,C")
(const_int 0)])
(set (match_operand:QI 2 "condexec_dest_operand" "=d,d,U,?f,?f,?d")
(match_operand:QI 3 "condexec_source_operand" "dO,U,dO,f,d,f")))]
"register_operand(operands[2], QImode) || reg_or_0_operand (operands[3], QImode)"
"* return output_condmove_single (operands, insn);"
[(set_attr "length" "4")
(set_attr "type" "int,gload,gstore,fsconv,movgf,movfg")])
 
(define_insn "*cond_exec_movhi"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C,C,C,C,C,C")
(const_int 0)])
(set (match_operand:HI 2 "condexec_dest_operand" "=d,d,U,?f,?f,?d")
(match_operand:HI 3 "condexec_source_operand" "dO,U,dO,f,d,f")))]
"register_operand(operands[2], HImode) || reg_or_0_operand (operands[3], HImode)"
"* return output_condmove_single (operands, insn);"
[(set_attr "length" "4")
(set_attr "type" "int,gload,gstore,fsconv,movgf,movfg")])
 
(define_insn "*cond_exec_movsi"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C,C,C,C,C,C,C,C")
(const_int 0)])
(set (match_operand:SI 2 "condexec_dest_operand" "=d,d,U,?f,?f,?d,?f,?m")
(match_operand:SI 3 "condexec_source_operand" "dO,U,dO,f,d,f,m,f")))]
"register_operand(operands[2], SImode) || reg_or_0_operand (operands[3], SImode)"
"* return output_condmove_single (operands, insn);"
[(set_attr "length" "4")
(set_attr "type" "int,gload,gstore,fsconv,movgf,movfg,fload,fstore")])
 
 
(define_insn "*cond_exec_movsf_has_fprs"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C,C,C,C,C,C,C,C,C,C")
(const_int 0)])
(set (match_operand:SF 2 "condexec_dest_operand" "=f,?d,?d,?f,f,f,?d,U,?U,U")
(match_operand:SF 3 "condexec_source_operand" "f,d,f,d,G,U,U,f,d,G")))]
"TARGET_HAS_FPRS"
"* return output_condmove_single (operands, insn);"
[(set_attr "length" "4")
(set_attr "type" "fsconv,int,movgf,movfg,movgf,fload,gload,fstore,gstore,gstore")])
 
(define_insn "*cond_exec_movsf_no_fprs"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C,C,C")
(const_int 0)])
(set (match_operand:SF 2 "condexec_dest_operand" "=d,d,U")
(match_operand:SF 3 "condexec_source_operand" "d,U,dG")))]
"! TARGET_HAS_FPRS"
"* return output_condmove_single (operands, insn);"
[(set_attr "length" "4")
(set_attr "type" "int,gload,gstore")])
 
(define_insn "*cond_exec_si_binary1"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:SI 2 "integer_register_operand" "=d")
(match_operator:SI 3 "condexec_si_binary_operator"
[(match_operand:SI 4 "integer_register_operand" "d")
(match_operand:SI 5 "integer_register_operand" "d")])))]
""
"*
{
switch (GET_CODE (operands[3]))
{
case PLUS: return \"cadd %4, %z5, %2, %1, %e0\";
case MINUS: return \"csub %4, %z5, %2, %1, %e0\";
case AND: return \"cand %4, %z5, %2, %1, %e0\";
case IOR: return \"cor %4, %z5, %2, %1, %e0\";
case XOR: return \"cxor %4, %z5, %2, %1, %e0\";
case ASHIFT: return \"csll %4, %z5, %2, %1, %e0\";
case ASHIFTRT: return \"csra %4, %z5, %2, %1, %e0\";
case LSHIFTRT: return \"csrl %4, %z5, %2, %1, %e0\";
default: gcc_unreachable ();
}
}"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_insn "*cond_exec_si_binary2"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:SI 2 "fpr_operand" "=f")
(match_operator:SI 3 "condexec_si_media_operator"
[(match_operand:SI 4 "fpr_operand" "f")
(match_operand:SI 5 "fpr_operand" "f")])))]
"TARGET_MEDIA"
"*
{
switch (GET_CODE (operands[3]))
{
case AND: return \"cmand %4, %5, %2, %1, %e0\";
case IOR: return \"cmor %4, %5, %2, %1, %e0\";
case XOR: return \"cmxor %4, %5, %2, %1, %e0\";
default: gcc_unreachable ();
}
}"
[(set_attr "length" "4")
(set_attr "type" "mlogic")])
 
;; Note, flow does not (currently) know how to handle an operation that uses
;; only part of the hard registers allocated for a multiregister value, such as
;; DImode in this case if the user is only interested in the lower 32-bits. So
;; we emit a USE of the entire register after the csmul instruction so it won't
;; get confused. See frv_ifcvt_modify_insn for more details.
 
(define_insn "*cond_exec_si_smul"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:DI 2 "even_gpr_operand" "=e")
(mult:DI (sign_extend:DI (match_operand:SI 3 "integer_register_operand" "%d"))
(sign_extend:DI (match_operand:SI 4 "integer_register_operand" "d")))))]
""
"csmul %3, %4, %2, %1, %e0"
[(set_attr "length" "4")
(set_attr "type" "mul")])
 
(define_insn "*cond_exec_si_divide"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:SI 2 "integer_register_operand" "=d")
(match_operator:SI 3 "condexec_si_divide_operator"
[(match_operand:SI 4 "integer_register_operand" "d")
(match_operand:SI 5 "integer_register_operand" "d")])))]
""
"*
{
switch (GET_CODE (operands[3]))
{
case DIV: return \"csdiv %4, %z5, %2, %1, %e0\";
case UDIV: return \"cudiv %4, %z5, %2, %1, %e0\";
default: gcc_unreachable ();
}
}"
[(set_attr "length" "4")
(set_attr "type" "div")])
 
(define_insn "*cond_exec_si_unary1"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:SI 2 "integer_register_operand" "=d")
(match_operator:SI 3 "condexec_si_unary_operator"
[(match_operand:SI 4 "integer_register_operand" "d")])))]
""
"*
{
switch (GET_CODE (operands[3]))
{
case NOT: return \"cnot %4, %2, %1, %e0\";
case NEG: return \"csub %., %4, %2, %1, %e0\";
default: gcc_unreachable ();
}
}"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_insn "*cond_exec_si_unary2"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:SI 2 "fpr_operand" "=f")
(not:SI (match_operand:SI 3 "fpr_operand" "f"))))]
"TARGET_MEDIA"
"cmnot %3, %2, %1, %e0"
[(set_attr "length" "4")
(set_attr "type" "mlogic")])
 
(define_insn "*cond_exec_cmpsi_cc"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:CC 2 "icc_operand" "=t")
(compare:CC (match_operand:SI 3 "integer_register_operand" "d")
(match_operand:SI 4 "reg_or_0_operand" "dO"))))]
"reload_completed
&& REGNO (operands[1]) == REGNO (operands[2]) - ICC_FIRST + ICR_FIRST"
"ccmp %3, %z4, %1, %e0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_insn "*cond_exec_cmpsi_cc_uns"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:CC_UNS 2 "icc_operand" "=t")
(compare:CC_UNS (match_operand:SI 3 "integer_register_operand" "d")
(match_operand:SI 4 "reg_or_0_operand" "dO"))))]
"reload_completed
&& REGNO (operands[1]) == REGNO (operands[2]) - ICC_FIRST + ICR_FIRST"
"ccmp %3, %z4, %1, %e0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_insn "*cond_exec_cmpsi_cc_nz"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:CC_NZ 2 "icc_operand" "=t")
(compare:CC_NZ (match_operand:SI 3 "integer_register_operand" "d")
(const_int 0))))]
"reload_completed
&& REGNO (operands[1]) == REGNO (operands[2]) - ICC_FIRST + ICR_FIRST"
"ccmp %3, %., %1, %e0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_insn "*cond_exec_sf_conv"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:SF 2 "fpr_operand" "=f")
(match_operator:SF 3 "condexec_sf_conv_operator"
[(match_operand:SF 4 "fpr_operand" "f")])))]
"TARGET_HARD_FLOAT"
"*
{
switch (GET_CODE (operands[3]))
{
case ABS: return \"cfabss %4, %2, %1, %e0\";
case NEG: return \"cfnegs %4, %2, %1, %e0\";
default: gcc_unreachable ();
}
}"
[(set_attr "length" "4")
(set_attr "type" "fsconv")])
 
(define_insn "*cond_exec_sf_add"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:SF 2 "fpr_operand" "=f")
(match_operator:SF 3 "condexec_sf_add_operator"
[(match_operand:SF 4 "fpr_operand" "f")
(match_operand:SF 5 "fpr_operand" "f")])))]
"TARGET_HARD_FLOAT"
"*
{
switch (GET_CODE (operands[3]))
{
case PLUS: return \"cfadds %4, %5, %2, %1, %e0\";
case MINUS: return \"cfsubs %4, %5, %2, %1, %e0\";
default: gcc_unreachable ();
}
}"
[(set_attr "length" "4")
(set_attr "type" "fsadd")])
 
(define_insn "*cond_exec_sf_mul"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:SF 2 "fpr_operand" "=f")
(mult:SF (match_operand:SF 3 "fpr_operand" "f")
(match_operand:SF 4 "fpr_operand" "f"))))]
"TARGET_HARD_FLOAT"
"cfmuls %3, %4, %2, %1, %e0"
[(set_attr "length" "4")
(set_attr "type" "fsmul")])
 
(define_insn "*cond_exec_sf_div"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:SF 2 "fpr_operand" "=f")
(div:SF (match_operand:SF 3 "fpr_operand" "f")
(match_operand:SF 4 "fpr_operand" "f"))))]
"TARGET_HARD_FLOAT"
"cfdivs %3, %4, %2, %1, %e0"
[(set_attr "length" "4")
(set_attr "type" "fsdiv")])
 
(define_insn "*cond_exec_sf_sqrt"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:SF 2 "fpr_operand" "=f")
(sqrt:SF (match_operand:SF 3 "fpr_operand" "f"))))]
"TARGET_HARD_FLOAT"
"cfsqrts %3, %2, %1, %e0"
[(set_attr "length" "4")
(set_attr "type" "fsdiv")])
 
(define_insn "*cond_exec_cmpsi_cc_fp"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:CC_FP 2 "fcc_operand" "=u")
(compare:CC_FP (match_operand:SF 3 "fpr_operand" "f")
(match_operand:SF 4 "fpr_operand" "f"))))]
"reload_completed && TARGET_HARD_FLOAT
&& REGNO (operands[1]) == REGNO (operands[2]) - FCC_FIRST + FCR_FIRST"
"cfcmps %3, %4, %2, %1, %e0"
[(set_attr "length" "4")
(set_attr "type" "fsconv")])
 
;; ::::::::::::::::::::
;; ::
;; :: Logical operations on CR registers
;; ::
;; ::::::::::::::::::::
 
;; We use UNSPEC to encode andcr/iorcr/etc. rather than the normal RTL
;; operations, since the RTL operations only have an idea of TRUE and FALSE,
;; while the CRs have TRUE, FALSE, and UNDEFINED.
 
(define_expand "andcr"
[(set (match_operand:CC_CCR 0 "cr_operand" "")
(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
(match_operand:CC_CCR 2 "cr_operand" "")
(const_int 0)] UNSPEC_CR_LOGIC))]
""
"")
 
(define_expand "orcr"
[(set (match_operand:CC_CCR 0 "cr_operand" "")
(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
(match_operand:CC_CCR 2 "cr_operand" "")
(const_int 1)] UNSPEC_CR_LOGIC))]
""
"")
 
(define_expand "xorcr"
[(set (match_operand:CC_CCR 0 "cr_operand" "")
(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
(match_operand:CC_CCR 2 "cr_operand" "")
(const_int 2)] UNSPEC_CR_LOGIC))]
""
"")
 
(define_expand "nandcr"
[(set (match_operand:CC_CCR 0 "cr_operand" "")
(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
(match_operand:CC_CCR 2 "cr_operand" "")
(const_int 3)] UNSPEC_CR_LOGIC))]
""
"")
 
(define_expand "norcr"
[(set (match_operand:CC_CCR 0 "cr_operand" "")
(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
(match_operand:CC_CCR 2 "cr_operand" "")
(const_int 4)] UNSPEC_CR_LOGIC))]
""
"")
 
(define_expand "andncr"
[(set (match_operand:CC_CCR 0 "cr_operand" "")
(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
(match_operand:CC_CCR 2 "cr_operand" "")
(const_int 5)] UNSPEC_CR_LOGIC))]
""
"")
 
(define_expand "orncr"
[(set (match_operand:CC_CCR 0 "cr_operand" "")
(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
(match_operand:CC_CCR 2 "cr_operand" "")
(const_int 6)] UNSPEC_CR_LOGIC))]
""
"")
 
(define_expand "nandncr"
[(set (match_operand:CC_CCR 0 "cr_operand" "")
(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
(match_operand:CC_CCR 2 "cr_operand" "")
(const_int 7)] UNSPEC_CR_LOGIC))]
""
"")
 
(define_expand "norncr"
[(set (match_operand:CC_CCR 0 "cr_operand" "")
(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
(match_operand:CC_CCR 2 "cr_operand" "")
(const_int 8)] UNSPEC_CR_LOGIC))]
""
"")
 
(define_expand "notcr"
[(set (match_operand:CC_CCR 0 "cr_operand" "")
(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
(match_dup 1)
(const_int 9)] UNSPEC_CR_LOGIC))]
""
"")
 
(define_insn "*logical_cr"
[(set (match_operand:CC_CCR 0 "cr_operand" "=C")
(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "C")
(match_operand:CC_CCR 2 "cr_operand" "C")
(match_operand:SI 3 "const_int_operand" "n")]
UNSPEC_CR_LOGIC))]
""
"*
{
switch (INTVAL (operands[3]))
{
default: break;
case 0: return \"andcr %1, %2, %0\";
case 1: return \"orcr %1, %2, %0\";
case 2: return \"xorcr %1, %2, %0\";
case 3: return \"nandcr %1, %2, %0\";
case 4: return \"norcr %1, %2, %0\";
case 5: return \"andncr %1, %2, %0\";
case 6: return \"orncr %1, %2, %0\";
case 7: return \"nandncr %1, %2, %0\";
case 8: return \"norncr %1, %2, %0\";
case 9: return \"notcr %1, %0\";
}
 
fatal_insn (\"logical_cr\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "ccr")])
 
;; ::::::::::::::::::::
;; ::
;; :: Conditional move instructions
;; ::
;; ::::::::::::::::::::
 
 
;; - conditional moves based on floating-point comparisons require
;; TARGET_HARD_FLOAT, because an FPU is required to do the comparison.
 
;; - conditional moves between FPRs based on integer comparisons
;; require TARGET_HAS_FPRS.
 
(define_expand "movqicc"
[(set (match_operand:QI 0 "integer_register_operand" "")
(if_then_else:QI (match_operand 1 "" "")
(match_operand:QI 2 "gpr_or_int_operand" "")
(match_operand:QI 3 "gpr_or_int_operand" "")))]
"TARGET_COND_MOVE"
"
{
if (!frv_emit_cond_move (operands[0], operands[1], operands[2], operands[3]))
FAIL;
 
DONE;
}")
 
(define_insn "*movqicc_internal1_int"
[(set (match_operand:QI 0 "integer_register_operand" "=d,d,d")
(if_then_else:QI (match_operator 1 "integer_relational_operator"
[(match_operand 2 "icc_operand" "t,t,t")
(const_int 0)])
(match_operand:QI 3 "reg_or_0_operand" "0,dO,dO")
(match_operand:QI 4 "reg_or_0_operand" "dO,0,dO")))
(clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))]
""
"#"
[(set_attr "length" "8,8,12")
(set_attr "type" "multi")])
 
(define_insn "*movqicc_internal1_float"
[(set (match_operand:QI 0 "integer_register_operand" "=d,d,d")
(if_then_else:QI (match_operator:CC_FP 1 "float_relational_operator"
[(match_operand:CC_FP 2 "fcc_operand" "u,u,u")
(const_int 0)])
(match_operand:QI 3 "reg_or_0_operand" "0,dO,dO")
(match_operand:QI 4 "reg_or_0_operand" "dO,0,dO")))
(clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w"))]
"TARGET_HARD_FLOAT"
"#"
[(set_attr "length" "8,8,12")
(set_attr "type" "multi")])
 
(define_insn "*movqicc_internal2_int"
[(set (match_operand:QI 0 "integer_register_operand" "=d,d,d,d,d")
(if_then_else:QI (match_operator 1 "integer_relational_operator"
[(match_operand 2 "icc_operand" "t,t,t,t,t")
(const_int 0)])
(match_operand:QI 3 "const_int_operand" "O,O,L,n,n")
(match_operand:QI 4 "const_int_operand" "L,n,O,O,n")))
(clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v,v,v"))]
"(INTVAL (operands[3]) == 0
|| INTVAL (operands[4]) == 0
|| (IN_RANGE_P (INTVAL (operands[3]), -2048, 2047)
&& IN_RANGE_P (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))"
"#"
[(set_attr "length" "8,12,8,12,12")
(set_attr "type" "multi")])
 
(define_insn "*movqicc_internal2_float"
[(set (match_operand:QI 0 "integer_register_operand" "=d,d,d,d,d")
(if_then_else:QI (match_operator:CC_FP 1 "float_relational_operator"
[(match_operand:CC_FP 2 "fcc_operand" "u,u,u,u,u")
(const_int 0)])
(match_operand:QI 3 "const_int_operand" "O,O,L,n,n")
(match_operand:QI 4 "const_int_operand" "L,n,O,O,n")))
(clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w,w,w"))]
"TARGET_HARD_FLOAT
&& (INTVAL (operands[3]) == 0
|| INTVAL (operands[4]) == 0
|| (IN_RANGE_P (INTVAL (operands[3]), -2048, 2047)
&& IN_RANGE_P (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))"
"#"
[(set_attr "length" "8,12,8,12,12")
(set_attr "type" "multi")])
 
(define_split
[(set (match_operand:QI 0 "integer_register_operand" "")
(if_then_else:QI (match_operator 1 "relational_operator"
[(match_operand 2 "cc_operand" "")
(const_int 0)])
(match_operand:QI 3 "gpr_or_int_operand" "")
(match_operand:QI 4 "gpr_or_int_operand" "")))
(clobber (match_operand:CC_CCR 5 "cr_operand" ""))]
"reload_completed"
[(match_dup 6)]
"operands[6] = frv_split_cond_move (operands);")
 
(define_expand "movhicc"
[(set (match_operand:HI 0 "integer_register_operand" "")
(if_then_else:HI (match_operand 1 "" "")
(match_operand:HI 2 "gpr_or_int_operand" "")
(match_operand:HI 3 "gpr_or_int_operand" "")))]
"TARGET_COND_MOVE"
"
{
if (!frv_emit_cond_move (operands[0], operands[1], operands[2], operands[3]))
FAIL;
 
DONE;
}")
 
(define_insn "*movhicc_internal1_int"
[(set (match_operand:HI 0 "integer_register_operand" "=d,d,d")
(if_then_else:HI (match_operator 1 "integer_relational_operator"
[(match_operand 2 "icc_operand" "t,t,t")
(const_int 0)])
(match_operand:HI 3 "reg_or_0_operand" "0,dO,dO")
(match_operand:HI 4 "reg_or_0_operand" "dO,0,dO")))
(clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))]
""
"#"
[(set_attr "length" "8,8,12")
(set_attr "type" "multi")])
 
(define_insn "*movhicc_internal1_float"
[(set (match_operand:HI 0 "integer_register_operand" "=d,d,d")
(if_then_else:HI (match_operator:CC_FP 1 "float_relational_operator"
[(match_operand:CC_FP 2 "fcc_operand" "u,u,u")
(const_int 0)])
(match_operand:HI 3 "reg_or_0_operand" "0,dO,dO")
(match_operand:HI 4 "reg_or_0_operand" "dO,0,dO")))
(clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w"))]
"TARGET_HARD_FLOAT"
"#"
[(set_attr "length" "8,8,12")
(set_attr "type" "multi")])
 
(define_insn "*movhicc_internal2_int"
[(set (match_operand:HI 0 "integer_register_operand" "=d,d,d,d,d")
(if_then_else:HI (match_operator 1 "integer_relational_operator"
[(match_operand 2 "icc_operand" "t,t,t,t,t")
(const_int 0)])
(match_operand:HI 3 "const_int_operand" "O,O,L,n,n")
(match_operand:HI 4 "const_int_operand" "L,n,O,O,n")))
(clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v,v,v"))]
"(INTVAL (operands[3]) == 0
|| INTVAL (operands[4]) == 0
|| (IN_RANGE_P (INTVAL (operands[3]), -2048, 2047)
&& IN_RANGE_P (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))"
"#"
[(set_attr "length" "8,12,8,12,12")
(set_attr "type" "multi")])
 
(define_insn "*movhicc_internal2_float"
[(set (match_operand:HI 0 "integer_register_operand" "=d,d,d,d,d")
(if_then_else:HI (match_operator:CC_FP 1 "float_relational_operator"
[(match_operand:CC_FP 2 "fcc_operand" "u,u,u,u,u")
(const_int 0)])
(match_operand:HI 3 "const_int_operand" "O,O,L,n,n")
(match_operand:HI 4 "const_int_operand" "L,n,O,O,n")))
(clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w,w,w"))]
"TARGET_HARD_FLOAT
&& (INTVAL (operands[3]) == 0
|| INTVAL (operands[4]) == 0
|| (IN_RANGE_P (INTVAL (operands[3]), -2048, 2047)
&& IN_RANGE_P (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))"
"#"
[(set_attr "length" "8,12,8,12,12")
(set_attr "type" "multi")])
 
(define_split
[(set (match_operand:HI 0 "integer_register_operand" "")
(if_then_else:HI (match_operator 1 "relational_operator"
[(match_operand 2 "cc_operand" "")
(const_int 0)])
(match_operand:HI 3 "gpr_or_int_operand" "")
(match_operand:HI 4 "gpr_or_int_operand" "")))
(clobber (match_operand:CC_CCR 5 "cr_operand" ""))]
"reload_completed"
[(match_dup 6)]
"operands[6] = frv_split_cond_move (operands);")
 
(define_expand "movsicc"
[(set (match_operand:SI 0 "integer_register_operand" "")
(if_then_else:SI (match_operand 1 "" "")
(match_operand:SI 2 "gpr_or_int_operand" "")
(match_operand:SI 3 "gpr_or_int_operand" "")))]
"TARGET_COND_MOVE"
"
{
if (!frv_emit_cond_move (operands[0], operands[1], operands[2], operands[3]))
FAIL;
 
DONE;
}")
 
(define_insn "*movsicc_internal1_int"
[(set (match_operand:SI 0 "integer_register_operand" "=d,d,d")
(if_then_else:SI (match_operator 1 "integer_relational_operator"
[(match_operand 2 "icc_operand" "t,t,t")
(const_int 0)])
(match_operand:SI 3 "reg_or_0_operand" "0,dO,dO")
(match_operand:SI 4 "reg_or_0_operand" "dO,0,dO")))
(clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))]
""
"#"
[(set_attr "length" "8,8,12")
(set_attr "type" "multi")])
 
(define_insn "*movsicc_internal1_float"
[(set (match_operand:SI 0 "integer_register_operand" "=d,d,d")
(if_then_else:SI (match_operator:CC_FP 1 "float_relational_operator"
[(match_operand:CC_FP 2 "fcc_operand" "u,u,u")
(const_int 0)])
(match_operand:SI 3 "reg_or_0_operand" "0,dO,dO")
(match_operand:SI 4 "reg_or_0_operand" "dO,0,dO")))
(clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w"))]
"TARGET_HARD_FLOAT"
"#"
[(set_attr "length" "8,8,12")
(set_attr "type" "multi")])
 
(define_insn "*movsicc_internal2_int"
[(set (match_operand:SI 0 "integer_register_operand" "=d,d,d,d,d")
(if_then_else:SI (match_operator 1 "integer_relational_operator"
[(match_operand 2 "icc_operand" "t,t,t,t,t")
(const_int 0)])
(match_operand:SI 3 "const_int_operand" "O,O,L,n,n")
(match_operand:SI 4 "const_int_operand" "L,n,O,O,n")))
(clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v,v,v"))]
"(INTVAL (operands[3]) == 0
|| INTVAL (operands[4]) == 0
|| (IN_RANGE_P (INTVAL (operands[3]), -2048, 2047)
&& IN_RANGE_P (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))"
"#"
[(set_attr "length" "8,12,8,12,12")
(set_attr "type" "multi")])
 
(define_insn "*movsicc_internal2_float"
[(set (match_operand:SI 0 "integer_register_operand" "=d,d,d,d,d")
(if_then_else:SI (match_operator:CC_FP 1 "float_relational_operator"
[(match_operand:CC_FP 2 "fcc_operand" "u,u,u,u,u")
(const_int 0)])
(match_operand:SI 3 "const_int_operand" "O,O,L,n,n")
(match_operand:SI 4 "const_int_operand" "L,n,O,O,n")))
(clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w,w,w"))]
"TARGET_HARD_FLOAT
&& (INTVAL (operands[3]) == 0
|| INTVAL (operands[4]) == 0
|| (IN_RANGE_P (INTVAL (operands[3]), -2048, 2047)
&& IN_RANGE_P (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))"
"#"
[(set_attr "length" "8,12,8,12,12")
(set_attr "type" "multi")])
 
(define_split
[(set (match_operand:SI 0 "integer_register_operand" "")
(if_then_else:SI (match_operator 1 "relational_operator"
[(match_operand 2 "cc_operand" "")
(const_int 0)])
(match_operand:SI 3 "gpr_or_int_operand" "")
(match_operand:SI 4 "gpr_or_int_operand" "")))
(clobber (match_operand:CC_CCR 5 "cr_operand" ""))]
"reload_completed"
[(match_dup 6)]
"operands[6] = frv_split_cond_move (operands);")
 
(define_expand "movsfcc"
[(set (match_operand:SF 0 "register_operand" "")
(if_then_else:SF (match_operand 1 "" "")
(match_operand:SF 2 "register_operand" "")
(match_operand:SF 3 "register_operand" "")))]
"TARGET_COND_MOVE"
"
{
if (!frv_emit_cond_move (operands[0], operands[1], operands[2], operands[3]))
FAIL;
 
DONE;
}")
 
(define_insn "*movsfcc_has_fprs_int"
[(set (match_operand:SF 0 "register_operand" "=f,f,f,?f,?f,?d")
(if_then_else:SF (match_operator 1 "integer_relational_operator"
[(match_operand 2 "icc_operand" "t,t,t,t,t,t")
(const_int 0)])
(match_operand:SF 3 "register_operand" "0,f,f,f,d,fd")
(match_operand:SF 4 "register_operand" "f,0,f,d,fd,fd")))
(clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v,v,v,v"))]
"TARGET_HAS_FPRS"
"#"
[(set_attr "length" "8,8,12,12,12,12")
(set_attr "type" "multi")])
 
(define_insn "*movsfcc_hardfloat_float"
[(set (match_operand:SF 0 "register_operand" "=f,f,f,?f,?f,?d")
(if_then_else:SF (match_operator:CC_FP 1 "float_relational_operator"
[(match_operand:CC_FP 2 "fcc_operand" "u,u,u,u,u,u")
(const_int 0)])
(match_operand:SF 3 "register_operand" "0,f,f,f,d,fd")
(match_operand:SF 4 "register_operand" "f,0,f,d,fd,fd")))
(clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w,w,w,w"))]
"TARGET_HARD_FLOAT"
"#"
[(set_attr "length" "8,8,12,12,12,12")
(set_attr "type" "multi")])
 
(define_insn "*movsfcc_no_fprs_int"
[(set (match_operand:SF 0 "integer_register_operand" "=d,d,d")
(if_then_else:SF (match_operator 1 "integer_relational_operator"
[(match_operand 2 "icc_operand" "t,t,t")
(const_int 0)])
(match_operand:SF 3 "integer_register_operand" "0,d,d")
(match_operand:SF 4 "integer_register_operand" "d,0,d")))
(clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))]
"! TARGET_HAS_FPRS"
"#"
[(set_attr "length" "8,8,12")
(set_attr "type" "multi")])
 
(define_split
[(set (match_operand:SF 0 "register_operand" "")
(if_then_else:SF (match_operator 1 "relational_operator"
[(match_operand 2 "cc_operand" "")
(const_int 0)])
(match_operand:SF 3 "register_operand" "")
(match_operand:SF 4 "register_operand" "")))
(clobber (match_operand:CC_CCR 5 "cr_operand" ""))]
"reload_completed"
[(match_dup 6)]
"operands[6] = frv_split_cond_move (operands);")
 
;; ::::::::::::::::::::
;; ::
;; :: Minimum, maximum, and integer absolute value
;; ::
;; ::::::::::::::::::::
 
;; These 'instructions' are provided to give the compiler a slightly better
;; nudge at register allocation, then it would if it constructed the
;; instructions from basic building blocks (since it indicates it prefers one
;; of the operands to be the same as the destination. It also helps the
;; earlier passes of the compiler, by not breaking things into small basic
;; blocks.
 
(define_expand "abssi2"
[(parallel [(set (match_operand:SI 0 "integer_register_operand" "")
(abs:SI (match_operand:SI 1 "integer_register_operand" "")))
(clobber (match_dup 2))
(clobber (match_dup 3))])]
"TARGET_COND_MOVE"
"
{
operands[2] = gen_reg_rtx (CCmode);
operands[3] = gen_reg_rtx (CC_CCRmode);
}")
 
(define_insn_and_split "*abssi2_internal"
[(set (match_operand:SI 0 "integer_register_operand" "=d,d")
(abs:SI (match_operand:SI 1 "integer_register_operand" "0,d")))
(clobber (match_operand:CC 2 "icc_operand" "=t,t"))
(clobber (match_operand:CC_CCR 3 "icr_operand" "=v,v"))]
"TARGET_COND_MOVE"
"#"
"reload_completed"
[(match_dup 4)]
"operands[4] = frv_split_abs (operands);"
[(set_attr "length" "12,16")
(set_attr "type" "multi")])
 
(define_expand "sminsi3"
[(parallel [(set (match_operand:SI 0 "integer_register_operand" "")
(smin:SI (match_operand:SI 1 "integer_register_operand" "")
(match_operand:SI 2 "gpr_or_int10_operand" "")))
(clobber (match_dup 3))
(clobber (match_dup 4))])]
"TARGET_COND_MOVE"
"
{
operands[3] = gen_reg_rtx (CCmode);
operands[4] = gen_reg_rtx (CC_CCRmode);
}")
 
(define_expand "smaxsi3"
[(parallel [(set (match_operand:SI 0 "integer_register_operand" "")
(smax:SI (match_operand:SI 1 "integer_register_operand" "")
(match_operand:SI 2 "gpr_or_int10_operand" "")))
(clobber (match_dup 3))
(clobber (match_dup 4))])]
"TARGET_COND_MOVE"
"
{
operands[3] = gen_reg_rtx (CCmode);
operands[4] = gen_reg_rtx (CC_CCRmode);
}")
 
(define_insn_and_split "*minmax_si_signed"
[(set (match_operand:SI 0 "integer_register_operand" "=d,d,&d")
(match_operator:SI 1 "minmax_operator"
[(match_operand:SI 2 "integer_register_operand" "%0,dO,d")
(match_operand:SI 3 "gpr_or_int10_operand" "dO,0,dJ")]))
(clobber (match_operand:CC 4 "icc_operand" "=t,t,t"))
(clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))]
"TARGET_COND_MOVE"
"#"
"reload_completed"
[(match_dup 6)]
"operands[6] = frv_split_minmax (operands);"
[(set_attr "length" "12,12,16")
(set_attr "type" "multi")])
 
(define_expand "uminsi3"
[(parallel [(set (match_operand:SI 0 "integer_register_operand" "")
(umin:SI (match_operand:SI 1 "integer_register_operand" "")
(match_operand:SI 2 "gpr_or_int10_operand" "")))
(clobber (match_dup 3))
(clobber (match_dup 4))])]
"TARGET_COND_MOVE"
"
{
operands[3] = gen_reg_rtx (CC_UNSmode);
operands[4] = gen_reg_rtx (CC_CCRmode);
}")
 
(define_expand "umaxsi3"
[(parallel [(set (match_operand:SI 0 "integer_register_operand" "")
(umax:SI (match_operand:SI 1 "integer_register_operand" "")
(match_operand:SI 2 "gpr_or_int10_operand" "")))
(clobber (match_dup 3))
(clobber (match_dup 4))])]
"TARGET_COND_MOVE"
"
{
operands[3] = gen_reg_rtx (CC_UNSmode);
operands[4] = gen_reg_rtx (CC_CCRmode);
}")
 
(define_insn_and_split "*minmax_si_unsigned"
[(set (match_operand:SI 0 "integer_register_operand" "=d,d,&d")
(match_operator:SI 1 "minmax_operator"
[(match_operand:SI 2 "integer_register_operand" "%0,dO,d")
(match_operand:SI 3 "gpr_or_int10_operand" "dO,0,dJ")]))
(clobber (match_operand:CC_UNS 4 "icc_operand" "=t,t,t"))
(clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))]
"TARGET_COND_MOVE"
"#"
"reload_completed"
[(match_dup 6)]
"operands[6] = frv_split_minmax (operands);"
[(set_attr "length" "12,12,16")
(set_attr "type" "multi")])
 
(define_expand "sminsf3"
[(parallel [(set (match_operand:SF 0 "fpr_operand" "")
(smin:SF (match_operand:SF 1 "fpr_operand" "")
(match_operand:SF 2 "fpr_operand" "")))
(clobber (match_dup 3))
(clobber (match_dup 4))])]
"TARGET_COND_MOVE && TARGET_HARD_FLOAT"
"
{
operands[3] = gen_reg_rtx (CC_FPmode);
operands[4] = gen_reg_rtx (CC_CCRmode);
}")
 
(define_expand "smaxsf3"
[(parallel [(set (match_operand:SF 0 "fpr_operand" "")
(smax:SF (match_operand:SF 1 "fpr_operand" "")
(match_operand:SF 2 "fpr_operand" "")))
(clobber (match_dup 3))
(clobber (match_dup 4))])]
"TARGET_COND_MOVE && TARGET_HARD_FLOAT"
"
{
operands[3] = gen_reg_rtx (CC_FPmode);
operands[4] = gen_reg_rtx (CC_CCRmode);
}")
 
(define_insn_and_split "*minmax_sf"
[(set (match_operand:SF 0 "fpr_operand" "=f,f,f")
(match_operator:SF 1 "minmax_operator"
[(match_operand:SF 2 "fpr_operand" "%0,f,f")
(match_operand:SF 3 "fpr_operand" "f,0,f")]))
(clobber (match_operand:CC_FP 4 "fcc_operand" "=u,u,u"))
(clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w"))]
"TARGET_COND_MOVE && TARGET_HARD_FLOAT"
"#"
"reload_completed"
[(match_dup 6)]
"operands[6] = frv_split_minmax (operands);"
[(set_attr "length" "12,12,16")
(set_attr "type" "multi")])
 
(define_expand "smindf3"
[(parallel [(set (match_operand:DF 0 "fpr_operand" "")
(smin:DF (match_operand:DF 1 "fpr_operand" "")
(match_operand:DF 2 "fpr_operand" "")))
(clobber (match_dup 3))
(clobber (match_dup 4))])]
"TARGET_COND_MOVE && TARGET_HARD_FLOAT && TARGET_DOUBLE"
"
{
operands[3] = gen_reg_rtx (CC_FPmode);
operands[4] = gen_reg_rtx (CC_CCRmode);
}")
 
(define_expand "smaxdf3"
[(parallel [(set (match_operand:DF 0 "fpr_operand" "")
(smax:DF (match_operand:DF 1 "fpr_operand" "")
(match_operand:DF 2 "fpr_operand" "")))
(clobber (match_dup 3))
(clobber (match_dup 4))])]
"TARGET_COND_MOVE && TARGET_HARD_FLOAT && TARGET_DOUBLE"
"
{
operands[3] = gen_reg_rtx (CC_FPmode);
operands[4] = gen_reg_rtx (CC_CCRmode);
}")
 
(define_insn_and_split "*minmax_df"
[(set (match_operand:DF 0 "fpr_operand" "=f,f,f")
(match_operator:DF 1 "minmax_operator"
[(match_operand:DF 2 "fpr_operand" "%0,f,f")
(match_operand:DF 3 "fpr_operand" "f,0,f")]))
(clobber (match_operand:CC_FP 4 "fcc_operand" "=u,u,u"))
(clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w"))]
"TARGET_COND_MOVE && TARGET_HARD_FLOAT && TARGET_DOUBLE"
"#"
"reload_completed"
[(match_dup 6)]
"operands[6] = frv_split_minmax (operands);"
[(set_attr "length" "12,12,16")
(set_attr "type" "multi")])
 
;; ::::::::::::::::::::
;; ::
;; :: Call and branch instructions
;; ::
;; ::::::::::::::::::::
 
;; Subroutine call instruction returning no value. Operand 0 is the function
;; to call; operand 1 is the number of bytes of arguments pushed (in mode
;; `SImode', except it is normally a `const_int'); operand 2 is the number of
;; registers used as operands.
 
;; On most machines, operand 2 is not actually stored into the RTL pattern. It
;; is supplied for the sake of some RISC machines which need to put this
;; information into the assembler code; they can put it in the RTL instead of
;; operand 1.
 
(define_expand "call"
[(use (match_operand:QI 0 "" ""))
(use (match_operand 1 "" ""))
(use (match_operand 2 "" ""))
(use (match_operand 3 "" ""))]
""
"
{
rtx lr = gen_rtx_REG (Pmode, LR_REGNO);
rtx addr;
 
gcc_assert (GET_CODE (operands[0]) == MEM);
 
addr = XEXP (operands[0], 0);
if (! call_operand (addr, Pmode))
addr = force_reg (Pmode, addr);
 
if (! operands[2])
operands[2] = const0_rtx;
 
if (TARGET_FDPIC)
frv_expand_fdpic_call (operands, false, false);
else
emit_call_insn (gen_call_internal (addr, operands[1], operands[2], lr));
 
DONE;
}")
 
(define_insn "call_internal"
[(call (mem:QI (match_operand:SI 0 "call_operand" "S,dNOP"))
(match_operand 1 "" ""))
(use (match_operand 2 "" ""))
(clobber (match_operand:SI 3 "lr_operand" "=l,l"))]
"! TARGET_FDPIC"
"@
call %0
call%i0l %M0"
[(set_attr "length" "4")
(set_attr "type" "call,jumpl")])
 
;; The odd use of GR0 within the UNSPEC below prevents cseing or
;; hoisting function descriptor loads out of loops. This is almost
;; never desirable, since if we preserve the function descriptor in a
;; pair of registers, it takes two insns to move it to gr14/gr15, and
;; if it's in the stack, we just waste space with the store, since
;; we'll have to load back from memory anyway. And, in the worst
;; case, we may end up reusing a function descriptor still pointing at
;; a PLT entry, instead of to the resolved function, which means going
;; through the resolver for every call that uses the outdated value.
;; Bad!
 
;; The explicit MEM inside the SPEC prevents the compiler from moving
;; the load before a branch after a NULL test, or before a store that
;; initializes a function descriptor.
 
(define_insn "movdi_ldd"
[(set (match_operand:DI 0 "fdpic_fptr_operand" "=e")
(unspec:DI [(mem:DI (match_operand:SI 1 "ldd_address_operand" "p"))
(reg:SI 0)] UNSPEC_LDD))]
""
"ldd%I1 %M1, %0"
[(set_attr "length" "4")
(set_attr "type" "gload")])
 
(define_insn "call_fdpicdi"
[(call (mem:QI (match_operand:DI 0 "fdpic_fptr_operand" "W"))
(match_operand 1 "" ""))
(clobber (match_operand:SI 2 "lr_operand" "=l"))]
"TARGET_FDPIC"
"call%i0l %M0"
[(set_attr "length" "4")
(set_attr "type" "jumpl")])
 
(define_insn "call_fdpicsi"
[(call (mem:QI (match_operand:SI 0 "call_operand" "S,dNOP"))
(match_operand 1 "" ""))
(use (match_operand 2 "" ""))
(use (match_operand:SI 3 "fdpic_operand" "Z,Z"))
(clobber (match_operand:SI 4 "lr_operand" "=l,l"))]
"TARGET_FDPIC"
"@
call %0
call%i0l %M0"
[(set_attr "length" "4")
(set_attr "type" "call,jumpl")])
 
(define_expand "sibcall"
[(use (match_operand:QI 0 "" ""))
(use (match_operand 1 "" ""))
(use (match_operand 2 "" ""))
(use (match_operand 3 "" ""))]
""
"
{
rtx addr;
 
gcc_assert (GET_CODE (operands[0]) == MEM);
 
addr = XEXP (operands[0], 0);
if (! sibcall_operand (addr, Pmode))
addr = force_reg (Pmode, addr);
 
if (! operands[2])
operands[2] = const0_rtx;
 
if (TARGET_FDPIC)
frv_expand_fdpic_call (operands, false, true);
else
emit_call_insn (gen_sibcall_internal (addr, operands[1], operands[2]));
 
DONE;
}")
;; It might seem that these sibcall patterns are missing references to
;; LR, but they're not necessary because sibcall_epilogue will make
;; sure LR is restored, and having LR here will set
;; regs_ever_used[REG_LR], forcing it to be saved on the stack, and
;; then restored in sibcalls and regular return code paths, even if
;; the function becomes a leaf function after tail-call elimination.
 
;; We must not use a call-saved register here. `W' limits ourselves
;; to gr14 or gr15, but since we're almost running out of constraint
;; letters, and most other call-clobbered registers are often used for
;; argument-passing, this will do.
(define_insn "sibcall_internal"
[(call (mem:QI (match_operand:SI 0 "sibcall_operand" "WNOP"))
(match_operand 1 "" ""))
(use (match_operand 2 "" ""))
(return)]
"! TARGET_FDPIC"
"jmp%i0l %M0"
[(set_attr "length" "4")
(set_attr "type" "jumpl")])
 
(define_insn "sibcall_fdpicdi"
[(call (mem:QI (match_operand:DI 0 "fdpic_fptr_operand" "W"))
(match_operand 1 "" ""))
(return)]
"TARGET_FDPIC"
"jmp%i0l %M0"
[(set_attr "length" "4")
(set_attr "type" "jumpl")])
 
 
;; Subroutine call instruction returning a value. Operand 0 is the hard
;; register in which the value is returned. There are three more operands, the
;; same as the three operands of the `call' instruction (but with numbers
;; increased by one).
 
;; Subroutines that return `BLKmode' objects use the `call' insn.
 
(define_expand "call_value"
[(use (match_operand 0 "" ""))
(use (match_operand:QI 1 "" ""))
(use (match_operand 2 "" ""))
(use (match_operand 3 "" ""))
(use (match_operand 4 "" ""))]
""
"
{
rtx lr = gen_rtx_REG (Pmode, LR_REGNO);
rtx addr;
 
gcc_assert (GET_CODE (operands[1]) == MEM);
 
addr = XEXP (operands[1], 0);
if (! call_operand (addr, Pmode))
addr = force_reg (Pmode, addr);
 
if (! operands[3])
operands[3] = const0_rtx;
 
if (TARGET_FDPIC)
frv_expand_fdpic_call (operands, true, false);
else
emit_call_insn (gen_call_value_internal (operands[0], addr, operands[2],
operands[3], lr));
 
DONE;
}")
 
(define_insn "call_value_internal"
[(set (match_operand 0 "register_operand" "=d,d")
(call (mem:QI (match_operand:SI 1 "call_operand" "S,dNOP"))
(match_operand 2 "" "")))
(use (match_operand 3 "" ""))
(clobber (match_operand:SI 4 "lr_operand" "=l,l"))]
"! TARGET_FDPIC"
"@
call %1
call%i1l %M1"
[(set_attr "length" "4")
(set_attr "type" "call,jumpl")])
 
(define_insn "call_value_fdpicdi"
[(set (match_operand 0 "register_operand" "=d")
(call (mem:QI (match_operand:DI 1 "fdpic_fptr_operand" "W"))
(match_operand 2 "" "")))
(clobber (match_operand:SI 3 "lr_operand" "=l"))]
"TARGET_FDPIC"
"call%i1l %M1"
[(set_attr "length" "4")
(set_attr "type" "jumpl")])
 
(define_insn "call_value_fdpicsi"
[(set (match_operand 0 "register_operand" "=d,d")
(call (mem:QI (match_operand:SI 1 "call_operand" "S,dNOP"))
(match_operand 2 "" "")))
(use (match_operand 3 "" ""))
(use (match_operand:SI 4 "fdpic_operand" "Z,Z"))
(clobber (match_operand:SI 5 "lr_operand" "=l,l"))]
"TARGET_FDPIC"
"@
call %1
call%i1l %M1"
[(set_attr "length" "4")
(set_attr "type" "call,jumpl")])
 
(define_expand "sibcall_value"
[(use (match_operand 0 "" ""))
(use (match_operand:QI 1 "" ""))
(use (match_operand 2 "" ""))
(use (match_operand 3 "" ""))
(use (match_operand 4 "" ""))]
""
"
{
rtx addr;
 
gcc_assert (GET_CODE (operands[1]) == MEM);
 
addr = XEXP (operands[1], 0);
if (! sibcall_operand (addr, Pmode))
addr = force_reg (Pmode, addr);
 
if (! operands[3])
operands[3] = const0_rtx;
 
if (TARGET_FDPIC)
frv_expand_fdpic_call (operands, true, true);
else
emit_call_insn (gen_sibcall_value_internal (operands[0], addr, operands[2],
operands[3]));
DONE;
}")
 
(define_insn "sibcall_value_internal"
[(set (match_operand 0 "register_operand" "=d")
(call (mem:QI (match_operand:SI 1 "sibcall_operand" "WNOP"))
(match_operand 2 "" "")))
(use (match_operand 3 "" ""))
(return)]
"! TARGET_FDPIC"
"jmp%i1l %M1"
[(set_attr "length" "4")
(set_attr "type" "jumpl")])
 
(define_insn "sibcall_value_fdpicdi"
[(set (match_operand 0 "register_operand" "=d")
(call (mem:QI (match_operand:DI 1 "fdpic_fptr_operand" "W"))
(match_operand 2 "" "")))
(return)]
"TARGET_FDPIC"
"jmp%i1l %M1"
[(set_attr "length" "4")
(set_attr "type" "jumpl")])
 
;; return instruction generated instead of jmp to epilog
(define_expand "return"
[(parallel [(return)
(use (match_dup 0))
(use (const_int 1))])]
"direct_return_p ()"
"
{
operands[0] = gen_rtx_REG (Pmode, LR_REGNO);
}")
 
;; return instruction generated by the epilogue
(define_expand "epilogue_return"
[(parallel [(return)
(use (match_operand:SI 0 "register_operand" ""))
(use (const_int 0))])]
""
"")
 
(define_insn "*return_internal"
[(return)
(use (match_operand:SI 0 "register_operand" "l,d"))
(use (match_operand:SI 1 "immediate_operand" "n,n"))]
""
"@
ret
jmpl @(%0,%.)"
[(set_attr "length" "4")
(set_attr "type" "jump,jumpl")])
 
(define_insn "*return_true"
[(set (pc)
(if_then_else (match_operator 0 "integer_relational_operator"
[(match_operand 1 "icc_operand" "t")
(const_int 0)])
(return)
(pc)))]
"direct_return_p ()"
"b%c0lr %1,%#"
[(set_attr "length" "4")
(set_attr "type" "jump")])
 
(define_insn "*return_false"
[(set (pc)
(if_then_else (match_operator 0 "integer_relational_operator"
[(match_operand 1 "icc_operand" "t")
(const_int 0)])
(pc)
(return)))]
"direct_return_p ()"
"b%C0lr %1,%#"
[(set_attr "length" "4")
(set_attr "type" "jump")])
 
;; A version of addsi3 for deallocating stack space at the end of the
;; epilogue. The addition is done in parallel with an (unspec_volatile),
;; which represents the clobbering of the deallocated space.
(define_insn "stack_adjust"
[(set (match_operand:SI 0 "register_operand" "=d")
(plus:SI (match_operand:SI 1 "register_operand" "d")
(match_operand:SI 2 "general_operand" "dNOP")))
(unspec_volatile [(const_int 0)] UNSPEC_STACK_ADJUST)]
""
"add%I2 %1,%2,%0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
;; Normal unconditional jump
 
;; Use the "call" instruction for long branches, but prefer to use "bra" for
;; short ones since it does not force us to save the link register.
 
;; This define_insn uses the branch-shortening code to decide which
;; instruction it emits. Since the main branch-shortening interface is
;; through get_attr_length(), the two alternatives must be given different
;; lengths. Here we pretend that the far jump is 8 rather than 4 bytes
;; long, though both alternatives are really the same size.
(define_insn "jump"
[(set (pc) (label_ref (match_operand 0 "" "")))]
""
"*
{
if (get_attr_length (insn) == 4)
return \"bra %l0\";
else
return \"call %l0\";
}"
[(set (attr "length")
(if_then_else
(and (ge (minus (match_dup 0) (pc)) (const_int -32768))
(le (minus (match_dup 0) (pc)) (const_int 32764)))
(const_int 4)
(const_int 8)))
(set (attr "far_jump")
(if_then_else
(eq_attr "length" "4")
(const_string "no")
(const_string "yes")))
(set (attr "type")
(if_then_else
(eq_attr "length" "4")
(const_string "jump")
(const_string "call")))])
 
;; Indirect jump through a register
(define_insn "indirect_jump"
[(set (pc) (match_operand:SI 0 "register_operand" "d,l"))]
""
"@
jmpl @(%0,%.)
bralr"
[(set_attr "length" "4")
(set_attr "type" "jumpl,branch")])
 
;; Instruction to jump to a variable address. This is a low-level capability
;; which can be used to implement a dispatch table when there is no `casesi'
;; pattern. Either the 'casesi' pattern or the 'tablejump' pattern, or both,
;; MUST be present in this file.
 
;; This pattern requires two operands: the address or offset, and a label which
;; should immediately precede the jump table. If the macro
;; `CASE_VECTOR_PC_RELATIVE' is defined then the first operand is an offset
;; which counts from the address of the table; otherwise, it is an absolute
;; address to jump to. In either case, the first operand has mode `Pmode'.
 
;; The `tablejump' insn is always the last insn before the jump table it uses.
;; Its assembler code normally has no need to use the second operand, but you
;; should incorporate it in the RTL pattern so that the jump optimizer will not
;; delete the table as unreachable code.
 
(define_expand "tablejump"
[(parallel [(set (pc) (match_operand:SI 0 "address_operand" "p"))
(use (label_ref (match_operand 1 "" "")))])]
"!flag_pic"
"")
 
(define_insn "tablejump_insn"
[(set (pc) (match_operand:SI 0 "address_operand" "p"))
(use (label_ref (match_operand 1 "" "")))]
""
"jmp%I0l %M0"
[(set_attr "length" "4")
(set_attr "type" "jumpl")])
 
;; Implement switch statements when generating PIC code. Switches are
;; implemented by `tablejump' when not using -fpic.
 
;; Emit code here to do the range checking and make the index zero based.
;; operand 0 is the index
;; operand 1 is the lower bound
;; operand 2 is the range of indices (highest - lowest + 1)
;; operand 3 is the label that precedes the table itself
;; operand 4 is the fall through label
 
(define_expand "casesi"
[(use (match_operand:SI 0 "integer_register_operand" ""))
(use (match_operand:SI 1 "const_int_operand" ""))
(use (match_operand:SI 2 "const_int_operand" ""))
(use (match_operand 3 "" ""))
(use (match_operand 4 "" ""))]
"flag_pic"
"
{
rtx indx;
rtx scale;
rtx low = operands[1];
rtx range = operands[2];
rtx table = operands[3];
rtx treg;
rtx fail = operands[4];
rtx mem;
rtx reg2;
rtx reg3;
 
gcc_assert (GET_CODE (operands[1]) == CONST_INT);
 
gcc_assert (GET_CODE (operands[2]) == CONST_INT);
 
/* If we can't generate an immediate instruction, promote to register. */
if (! IN_RANGE_P (INTVAL (range), -2048, 2047))
range = force_reg (SImode, range);
 
/* If low bound is 0, we don't have to subtract it. */
if (INTVAL (operands[1]) == 0)
indx = operands[0];
else
{
indx = gen_reg_rtx (SImode);
if (IN_RANGE_P (INTVAL (low), -2047, 2048))
emit_insn (gen_addsi3 (indx, operands[0], GEN_INT (- INTVAL (low))));
else
emit_insn (gen_subsi3 (indx, operands[0], force_reg (SImode, low)));
}
 
/* Do an unsigned comparison (in the proper mode) between the index
expression and the value which represents the length of the range.
Since we just finished subtracting the lower bound of the range
from the index expression, this comparison allows us to simultaneously
check that the original index expression value is both greater than
or equal to the minimum value of the range and less than or equal to
the maximum value of the range. */
 
emit_cmp_and_jump_insns (indx, range, GTU, NULL_RTX, SImode, 1, fail);
 
/* Move the table address to a register. */
treg = gen_reg_rtx (Pmode);
emit_insn (gen_movsi (treg, gen_rtx_LABEL_REF (VOIDmode, table)));
 
/* Scale index-low by wordsize. */
scale = gen_reg_rtx (SImode);
emit_insn (gen_ashlsi3 (scale, indx, const2_rtx));
 
/* Load the address, add the start of the table back in,
and jump to it. */
mem = gen_rtx_MEM (SImode, gen_rtx_PLUS (Pmode, scale, treg));
reg2 = gen_reg_rtx (SImode);
reg3 = gen_reg_rtx (SImode);
emit_insn (gen_movsi (reg2, mem));
emit_insn (gen_addsi3 (reg3, reg2, treg));
emit_jump_insn (gen_tablejump_insn (reg3, table));
DONE;
}")
 
;; ::::::::::::::::::::
;; ::
;; :: Prologue and Epilogue instructions
;; ::
;; ::::::::::::::::::::
 
;; Called after register allocation to add any instructions needed for the
;; prologue. Using a prologue insn is favored compared to putting all of the
;; instructions in the FUNCTION_PROLOGUE macro, since it allows the scheduler
;; 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
;; insn to prevent such scheduling.
(define_expand "prologue"
[(const_int 1)]
""
"
{
frv_expand_prologue ();
DONE;
}")
 
;; Called after register allocation to add any instructions needed for the
;; epilogue. Using an epilogue insn is favored compared to putting all of the
;; instructions in the FUNCTION_EPILOGUE macro, since it allows the scheduler
;; 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
;; first insn to prevent such scheduling.
(define_expand "epilogue"
[(const_int 2)]
""
"
{
frv_expand_epilogue (true);
DONE;
}")
 
;; This pattern, if defined, emits RTL for exit from a function without the final
;; branch back to the calling function. This pattern will be emitted before any
;; sibling call (aka tail call) sites.
;;
;; The sibcall_epilogue pattern must not clobber any arguments used for
;; parameter passing or any stack slots for arguments passed to the current
;; function.
(define_expand "sibcall_epilogue"
[(const_int 3)]
""
"
{
frv_expand_epilogue (false);
DONE;
}")
 
;; Set up the pic register to hold the address of the pic table
(define_insn "pic_prologue"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(unspec_volatile:SI [(const_int 0)] UNSPEC_PIC_PROLOGUE))
(clobber (match_operand:SI 1 "lr_operand" "=l"))
(clobber (match_operand:SI 2 "integer_register_operand" "=d"))]
""
"*
{
static int frv_pic_labelno = 0;
 
operands[3] = GEN_INT (frv_pic_labelno++);
return \"call %P3\\n%P3:\;movsg %1, %0\;sethi #gprelhi(%P3), %2\;setlo #gprello(%P3), %2\;sub %0,%2,%0\";
}"
[(set_attr "length" "16")
(set_attr "type" "multi")])
;; ::::::::::::::::::::
;; ::
;; :: Miscellaneous instructions
;; ::
;; ::::::::::::::::::::
 
;; No operation, needed in case the user uses -g but not -O.
(define_insn "nop"
[(const_int 0)]
""
"nop"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_insn "fnop"
[(const_int 1)]
""
"fnop"
[(set_attr "length" "4")
(set_attr "type" "fnop")])
 
(define_insn "mnop"
[(const_int 2)]
""
"mnop"
[(set_attr "length" "4")
(set_attr "type" "mnop")])
 
;; Pseudo instruction that prevents the scheduler from moving code above this
;; point. Note, type unknown is used to make sure the VLIW instructions are
;; not continued past this point.
(define_insn "blockage"
[(unspec_volatile [(const_int 0)] UNSPEC_BLOCKAGE)]
""
"# blockage"
[(set_attr "length" "0")
(set_attr "type" "unknown")])
;; ::::::::::::::::::::
;; ::
;; :: Media instructions
;; ::
;; ::::::::::::::::::::
 
;; Unimplemented instructions:
;; - MCMPSH, MCMPUH
 
(define_constants
[(UNSPEC_MLOGIC 100)
(UNSPEC_MNOT 101)
(UNSPEC_MAVEH 102)
(UNSPEC_MSATH 103)
(UNSPEC_MADDH 104)
(UNSPEC_MQADDH 105)
(UNSPEC_MPACKH 106)
(UNSPEC_MUNPACKH 107)
(UNSPEC_MDPACKH 108)
(UNSPEC_MBTOH 109)
(UNSPEC_MHTOB 110)
(UNSPEC_MROT 111)
(UNSPEC_MSHIFT 112)
(UNSPEC_MEXPDHW 113)
(UNSPEC_MEXPDHD 114)
(UNSPEC_MWCUT 115)
(UNSPEC_MMULH 116)
(UNSPEC_MMULXH 117)
(UNSPEC_MMACH 118)
(UNSPEC_MMRDH 119)
(UNSPEC_MQMULH 120)
(UNSPEC_MQMULXH 121)
(UNSPEC_MQMACH 122)
(UNSPEC_MCPX 123)
(UNSPEC_MQCPX 124)
(UNSPEC_MCUT 125)
(UNSPEC_MRDACC 126)
(UNSPEC_MRDACCG 127)
(UNSPEC_MWTACC 128)
(UNSPEC_MWTACCG 129)
(UNSPEC_MTRAP 130)
(UNSPEC_MCLRACC 131)
(UNSPEC_MCLRACCA 132)
(UNSPEC_MCOP1 133)
(UNSPEC_MCOP2 134)
(UNSPEC_MDUNPACKH 135)
(UNSPEC_MDUNPACKH_INTERNAL 136)
(UNSPEC_MBTOHE 137)
(UNSPEC_MBTOHE_INTERNAL 138)
(UNSPEC_MBTOHE 137)
(UNSPEC_MBTOHE_INTERNAL 138)
(UNSPEC_MQMACH2 139)
(UNSPEC_MADDACC 140)
(UNSPEC_MDADDACC 141)
(UNSPEC_MABSHS 142)
(UNSPEC_MDROTLI 143)
(UNSPEC_MCPLHI 144)
(UNSPEC_MCPLI 145)
(UNSPEC_MDCUTSSI 146)
(UNSPEC_MQSATHS 147)
(UNSPEC_MHSETLOS 148)
(UNSPEC_MHSETLOH 149)
(UNSPEC_MHSETHIS 150)
(UNSPEC_MHSETHIH 151)
(UNSPEC_MHDSETS 152)
(UNSPEC_MHDSETH 153)
(UNSPEC_MQLCLRHS 154)
(UNSPEC_MQLMTHS 155)
(UNSPEC_MQSLLHI 156)
(UNSPEC_MQSRAHI 157)
(UNSPEC_MASACCS 158)
(UNSPEC_MDASACCS 159)
])
 
;; Logic operations: type "mlogic"
 
(define_expand "mand"
[(set (match_operand:SI 0 "fpr_operand" "")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "")
(match_operand:SI 2 "fpr_operand" "")
(match_dup 3)]
UNSPEC_MLOGIC))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MAND);")
 
(define_expand "mor"
[(set (match_operand:SI 0 "fpr_operand" "")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "")
(match_operand:SI 2 "fpr_operand" "")
(match_dup 3)]
UNSPEC_MLOGIC))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MOR);")
 
(define_expand "mxor"
[(set (match_operand:SI 0 "fpr_operand" "")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "")
(match_operand:SI 2 "fpr_operand" "")
(match_dup 3)]
UNSPEC_MLOGIC))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MXOR);")
 
(define_insn "*mlogic"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_operand:SI 3 "const_int_operand" "n")]
UNSPEC_MLOGIC))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[3]))
{
default: break;
case FRV_BUILTIN_MAND: return \"mand %1, %2, %0\";
case FRV_BUILTIN_MOR: return \"mor %1, %2, %0\";
case FRV_BUILTIN_MXOR: return \"mxor %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, mlogic\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mlogic")])
 
(define_insn "*cond_exec_mlogic"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:SI 2 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 3 "fpr_operand" "f")
(match_operand:SI 4 "fpr_operand" "f")
(match_operand:SI 5 "const_int_operand" "n")]
UNSPEC_MLOGIC)))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[5]))
{
default: break;
case FRV_BUILTIN_MAND: return \"cmand %3, %4, %2, %1, %e0\";
case FRV_BUILTIN_MOR: return \"cmor %3, %4, %2, %1, %e0\";
case FRV_BUILTIN_MXOR: return \"cmxor %3, %4, %2, %1, %e0\";
}
 
fatal_insn (\"Bad media insn, cond_exec_mlogic\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mlogic")])
 
;; Logical not: type "mlogic"
 
(define_insn "mnot"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")] UNSPEC_MNOT))]
"TARGET_MEDIA"
"mnot %1, %0"
[(set_attr "length" "4")
(set_attr "type" "mlogic")])
 
(define_insn "*cond_exec_mnot"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:SI 2 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 3 "fpr_operand" "f")] UNSPEC_MNOT)))]
"TARGET_MEDIA"
"cmnot %3, %2, %1, %e0"
[(set_attr "length" "4")
(set_attr "type" "mlogic")])
 
;; Dual average (halfword): type "maveh"
 
(define_insn "maveh"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")]
UNSPEC_MAVEH))]
"TARGET_MEDIA"
"maveh %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "maveh")])
 
;; Dual saturation (halfword): type "msath"
 
(define_expand "msaths"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_dup 3)]
UNSPEC_MSATH))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MSATHS);")
 
(define_expand "msathu"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_dup 3)]
UNSPEC_MSATH))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MSATHU);")
 
(define_insn "*msath"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_operand:SI 3 "const_int_operand" "n")]
UNSPEC_MSATH))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[3]))
{
default: break;
case FRV_BUILTIN_MSATHS: return \"msaths %1, %2, %0\";
case FRV_BUILTIN_MSATHU: return \"msathu %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, msath\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "msath")])
 
;; Dual addition/subtraction with saturation (halfword): type "maddh"
 
(define_expand "maddhss"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_dup 3)]
UNSPEC_MADDH))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MADDHSS);")
 
(define_expand "maddhus"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_dup 3)]
UNSPEC_MADDH))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MADDHUS);")
 
(define_expand "msubhss"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_dup 3)]
UNSPEC_MADDH))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MSUBHSS);")
 
(define_expand "msubhus"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_dup 3)]
UNSPEC_MADDH))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MSUBHUS);")
 
(define_insn "*maddh"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_operand:SI 3 "const_int_operand" "n")]
UNSPEC_MADDH))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[3]))
{
default: break;
case FRV_BUILTIN_MADDHSS: return \"maddhss %1, %2, %0\";
case FRV_BUILTIN_MADDHUS: return \"maddhus %1, %2, %0\";
case FRV_BUILTIN_MSUBHSS: return \"msubhss %1, %2, %0\";
case FRV_BUILTIN_MSUBHUS: return \"msubhus %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, maddh\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "maddh")])
 
(define_insn "*cond_exec_maddh"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:SI 2 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 3 "fpr_operand" "f")
(match_operand:SI 4 "fpr_operand" "f")
(match_operand:SI 5 "const_int_operand" "n")]
UNSPEC_MADDH)))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[5]))
{
default: break;
case FRV_BUILTIN_MADDHSS: return \"cmaddhss %3, %4, %2, %1, %e0\";
case FRV_BUILTIN_MADDHUS: return \"cmaddhus %3, %4, %2, %1, %e0\";
case FRV_BUILTIN_MSUBHSS: return \"cmsubhss %3, %4, %2, %1, %e0\";
case FRV_BUILTIN_MSUBHUS: return \"cmsubhus %3, %4, %2, %1, %e0\";
}
 
fatal_insn (\"Bad media insn, cond_exec_maddh\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "maddh")])
 
;; Quad addition/subtraction with saturation (halfword): type "mqaddh"
 
(define_expand "mqaddhss"
[(set (match_operand:DI 0 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")
(match_dup 3)]
UNSPEC_MQADDH))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MQADDHSS);")
 
(define_expand "mqaddhus"
[(set (match_operand:DI 0 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")
(match_dup 3)]
UNSPEC_MQADDH))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MQADDHUS);")
 
(define_expand "mqsubhss"
[(set (match_operand:DI 0 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")
(match_dup 3)]
UNSPEC_MQADDH))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MQSUBHSS);")
 
(define_expand "mqsubhus"
[(set (match_operand:DI 0 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")
(match_dup 3)]
UNSPEC_MQADDH))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MQSUBHUS);")
 
(define_insn "*mqaddh"
[(set (match_operand:DI 0 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")
(match_operand:SI 3 "const_int_operand" "n")]
UNSPEC_MQADDH))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[3]))
{
default: break;
case FRV_BUILTIN_MQADDHSS: return \"mqaddhss %1, %2, %0\";
case FRV_BUILTIN_MQADDHUS: return \"mqaddhus %1, %2, %0\";
case FRV_BUILTIN_MQSUBHSS: return \"mqsubhss %1, %2, %0\";
case FRV_BUILTIN_MQSUBHUS: return \"mqsubhus %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, mqaddh\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mqaddh")])
 
(define_insn "*cond_exec_mqaddh"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:DI 2 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:DI 3 "even_fpr_operand" "h")
(match_operand:DI 4 "even_fpr_operand" "h")
(match_operand:SI 5 "const_int_operand" "n")]
UNSPEC_MQADDH)))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[5]))
{
default: break;
case FRV_BUILTIN_MQADDHSS: return \"cmqaddhss %3, %4, %2, %1, %e0\";
case FRV_BUILTIN_MQADDHUS: return \"cmqaddhus %3, %4, %2, %1, %e0\";
case FRV_BUILTIN_MQSUBHSS: return \"cmqsubhss %3, %4, %2, %1, %e0\";
case FRV_BUILTIN_MQSUBHUS: return \"cmqsubhus %3, %4, %2, %1, %e0\";
}
 
fatal_insn (\"Bad media insn, cond_exec_mqaddh\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mqaddh")])
 
;; Pack halfword: type "mpackh"
 
(define_insn "mpackh"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:HI 1 "fpr_operand" "f")
(match_operand:HI 2 "fpr_operand" "f")]
UNSPEC_MPACKH))]
"TARGET_MEDIA"
"mpackh %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mpackh")])
 
;; Unpack halfword: type "mpackh"
 
(define_insn "munpackh"
[(set (match_operand:DI 0 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:SI 1 "fpr_operand" "f")]
UNSPEC_MUNPACKH))]
"TARGET_MEDIA"
"munpackh %1, %0"
[(set_attr "length" "4")
(set_attr "type" "munpackh")])
 
;; Dual pack halfword: type "mdpackh"
 
(define_insn "mdpackh"
[(set (match_operand:DI 0 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")]
UNSPEC_MDPACKH))]
"TARGET_MEDIA"
"mdpackh %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mdpackh")])
 
;; Byte-halfword conversion: type "mbhconv"
 
(define_insn "mbtoh"
[(set (match_operand:DI 0 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:SI 1 "fpr_operand" "f")]
UNSPEC_MBTOH))]
"TARGET_MEDIA"
"mbtoh %1, %0"
[(set_attr "length" "4")
(set_attr "type" "mbhconv")])
 
(define_insn "*cond_exec_mbtoh"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:DI 2 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:SI 3 "fpr_operand" "f")]
UNSPEC_MBTOH)))]
"TARGET_MEDIA"
"cmbtoh %3, %2, %1, %e0"
[(set_attr "length" "4")
(set_attr "type" "mbhconv")])
 
(define_insn "mhtob"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:DI 1 "even_fpr_operand" "h")]
UNSPEC_MHTOB))]
"TARGET_MEDIA"
"mhtob %1, %0"
[(set_attr "length" "4")
(set_attr "type" "mbhconv")])
 
(define_insn "*cond_exec_mhtob"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:SI 2 "fpr_operand" "=f")
(unspec:SI [(match_operand:DI 3 "even_fpr_operand" "h")]
UNSPEC_MHTOB)))]
"TARGET_MEDIA"
"cmhtob %3, %2, %1, %e0"
[(set_attr "length" "4")
(set_attr "type" "mbhconv")])
 
;; Rotate: type "mrot"
 
(define_expand "mrotli"
[(set (match_operand:SI 0 "fpr_operand" "")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "")
(match_operand:SI 2 "uint5_operand" "")
(match_dup 3)]
UNSPEC_MROT))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MROTLI);")
 
(define_expand "mrotri"
[(set (match_operand:SI 0 "fpr_operand" "")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "")
(match_operand:SI 2 "uint5_operand" "")
(match_dup 3)]
UNSPEC_MROT))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MROTRI);")
 
(define_insn "*mrot"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "uint5_operand" "I")
(match_operand:SI 3 "const_int_operand" "n")]
UNSPEC_MROT))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[3]))
{
default: break;
case FRV_BUILTIN_MROTLI: return \"mrotli %1, %2, %0\";
case FRV_BUILTIN_MROTRI: return \"mrotri %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, mrot\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mrot")])
 
;; Dual shift halfword: type "msh"
 
(define_expand "msllhi"
[(set (match_operand:SI 0 "fpr_operand" "")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "")
(match_operand:SI 2 "uint4_operand" "")
(match_dup 3)]
UNSPEC_MSHIFT))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MSLLHI);")
 
(define_expand "msrlhi"
[(set (match_operand:SI 0 "fpr_operand" "")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "")
(match_operand:SI 2 "uint4_operand" "")
(match_dup 3)]
UNSPEC_MSHIFT))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MSRLHI);")
 
(define_expand "msrahi"
[(set (match_operand:SI 0 "fpr_operand" "")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "")
(match_operand:SI 2 "uint4_operand" "")
(match_dup 3)]
UNSPEC_MSHIFT))]
"TARGET_MEDIA"
"operands[3] = GEN_INT (FRV_BUILTIN_MSRAHI);")
 
(define_insn "*mshift"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "uint4_operand" "I")
(match_operand:SI 3 "const_int_operand" "n")]
UNSPEC_MSHIFT))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[3]))
{
default: break;
case FRV_BUILTIN_MSLLHI: return \"msllhi %1, %2, %0\";
case FRV_BUILTIN_MSRLHI: return \"msrlhi %1, %2, %0\";
case FRV_BUILTIN_MSRAHI: return \"msrahi %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, mshift\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mshift")])
 
;; Expand halfword to word: type "mexpdhw"
 
(define_insn "mexpdhw"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "uint1_operand" "I")]
UNSPEC_MEXPDHW))]
"TARGET_MEDIA"
"mexpdhw %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mexpdhw")])
 
(define_insn "*cond_exec_mexpdhw"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:SI 2 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 3 "fpr_operand" "f")
(match_operand:SI 4 "uint1_operand" "I")]
UNSPEC_MEXPDHW)))]
"TARGET_MEDIA"
"cmexpdhw %3, %4, %2, %1, %e0"
[(set_attr "length" "4")
(set_attr "type" "mexpdhw")])
 
;; Expand halfword to double: type "mexpdhd"
 
(define_insn "mexpdhd"
[(set (match_operand:DI 0 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "uint1_operand" "I")]
UNSPEC_MEXPDHD))]
"TARGET_MEDIA"
"mexpdhd %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mexpdhd")])
 
(define_insn "*cond_exec_mexpdhd"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(set (match_operand:DI 2 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:SI 3 "fpr_operand" "f")
(match_operand:SI 4 "uint1_operand" "I")]
UNSPEC_MEXPDHD)))]
"TARGET_MEDIA"
"cmexpdhd %3, %4, %2, %1, %e0"
[(set_attr "length" "4")
(set_attr "type" "mexpdhd")])
 
;; FR cut: type "mwcut"
 
(define_insn "mwcut"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:DI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_or_int6_operand" "fI")]
UNSPEC_MWCUT))]
"TARGET_MEDIA"
"mwcut%i2 %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mwcut")])
 
;; Dual multiplication (halfword): type "mmulh"
 
(define_expand "mmulhs"
[(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b")
(unspec:DI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_dup 4)]
UNSPEC_MMULH))
(set (match_operand:HI 3 "accg_operand" "=B")
(unspec:HI [(const_int 0)] UNSPEC_MMULH))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MMULHS);")
 
(define_expand "mmulhu"
[(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b")
(unspec:DI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_dup 4)]
UNSPEC_MMULH))
(set (match_operand:HI 3 "accg_operand" "=B")
(unspec:HI [(const_int 0)] UNSPEC_MMULH))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MMULHU);")
 
(define_insn "*mmulh"
[(set (match_operand:DI 0 "even_acc_operand" "=b")
(unspec:DI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_operand:SI 3 "const_int_operand" "n")]
UNSPEC_MMULH))
(set (match_operand:HI 4 "accg_operand" "=B")
(unspec:HI [(const_int 0)] UNSPEC_MMULH))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[3]))
{
default: break;
case FRV_BUILTIN_MMULHS: return \"mmulhs %1, %2, %0\";
case FRV_BUILTIN_MMULHU: return \"mmulhu %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, mmulh\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mmulh")])
 
(define_insn "*cond_exec_mmulh"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(parallel [(set (match_operand:DI 2 "even_acc_operand" "=b")
(unspec:DI [(match_operand:SI 3 "fpr_operand" "f")
(match_operand:SI 4 "fpr_operand" "f")
(match_operand:SI 5 "const_int_operand" "n")]
UNSPEC_MMULH))
(set (match_operand:HI 6 "accg_operand" "=B")
(unspec:HI [(const_int 0)] UNSPEC_MMULH))]))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[5]))
{
default: break;
case FRV_BUILTIN_MMULHS: return \"cmmulhs %3, %4, %2, %1, %e0\";
case FRV_BUILTIN_MMULHU: return \"cmmulhu %3, %4, %2, %1, %e0\";
}
 
fatal_insn (\"Bad media insn, cond_exec_mmulh\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mmulh")])
 
;; Dual cross multiplication (halfword): type "mmulxh"
 
(define_expand "mmulxhs"
[(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b")
(unspec:DI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_dup 4)]
UNSPEC_MMULXH))
(set (match_operand:HI 3 "accg_operand" "=B")
(unspec:HI [(const_int 0)] UNSPEC_MMULXH))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MMULXHS);")
 
(define_expand "mmulxhu"
[(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b")
(unspec:DI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_dup 4)]
UNSPEC_MMULXH))
(set (match_operand:HI 3 "accg_operand" "=B")
(unspec:HI [(const_int 0)] UNSPEC_MMULXH))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MMULXHU);")
 
(define_insn "*mmulxh"
[(set (match_operand:DI 0 "even_acc_operand" "=b")
(unspec:DI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_operand:SI 3 "const_int_operand" "n")]
UNSPEC_MMULXH))
(set (match_operand:HI 4 "accg_operand" "=B")
(unspec:HI [(const_int 0)] UNSPEC_MMULXH))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[3]))
{
default: break;
case FRV_BUILTIN_MMULXHS: return \"mmulxhs %1, %2, %0\";
case FRV_BUILTIN_MMULXHU: return \"mmulxhu %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, mmulxh\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mmulxh")])
 
;; Dual product-sum (halfword): type "mmach"
 
(define_expand "mmachs"
[(parallel [(set (match_operand:DI 0 "even_acc_operand" "+b")
(unspec:DI [(match_dup 0)
(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_operand:HI 3 "accg_operand" "+B")
(match_dup 4)]
UNSPEC_MMACH))
(set (match_dup 3)
(unspec:HI [(const_int 0)] UNSPEC_MMACH))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MMACHS);")
 
(define_expand "mmachu"
[(parallel [(set (match_operand:DI 0 "even_acc_operand" "+b")
(unspec:DI [(match_dup 0)
(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_operand:HI 3 "accg_operand" "+B")
(match_dup 4)]
UNSPEC_MMACH))
(set (match_dup 3)
(unspec:HI [(const_int 0)] UNSPEC_MMACH))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MMACHU);")
 
(define_insn "*mmach"
[(set (match_operand:DI 0 "even_acc_operand" "+b")
(unspec:DI [(match_dup 0)
(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_operand:HI 3 "accg_operand" "+B")
(match_operand:SI 4 "const_int_operand" "n")]
UNSPEC_MMACH))
(set (match_dup 3) (unspec:HI [(const_int 0)] UNSPEC_MMACH))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[4]))
{
default: break;
case FRV_BUILTIN_MMACHS: return \"mmachs %1, %2, %0\";
case FRV_BUILTIN_MMACHU: return \"mmachu %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, mmach\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mmach")])
 
(define_insn "*cond_exec_mmach"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(parallel [(set (match_operand:DI 2 "even_acc_operand" "+b")
(unspec:DI [(match_dup 2)
(match_operand:SI 3 "fpr_operand" "f")
(match_operand:SI 4 "fpr_operand" "f")
(match_operand:HI 5 "accg_operand" "+B")
(match_operand:SI 6 "const_int_operand" "n")]
UNSPEC_MMACH))
(set (match_dup 5)
(unspec:HI [(const_int 0)] UNSPEC_MMACH))]))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[6]))
{
default: break;
case FRV_BUILTIN_MMACHS: return \"cmmachs %3, %4, %2, %1, %e0\";
case FRV_BUILTIN_MMACHU: return \"cmmachu %3, %4, %2, %1, %e0\";
}
 
fatal_insn (\"Bad media insn, cond_exec_mmach\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mmach")])
 
;; Dual product-difference: type "mmrdh"
 
(define_expand "mmrdhs"
[(parallel [(set (match_operand:DI 0 "even_acc_operand" "+b")
(unspec:DI [(match_dup 0)
(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_operand:HI 3 "accg_operand" "+B")
(match_dup 4)]
UNSPEC_MMRDH))
(set (match_dup 3)
(unspec:HI [(const_int 0)] UNSPEC_MMRDH))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MMRDHS);")
 
(define_expand "mmrdhu"
[(parallel [(set (match_operand:DI 0 "even_acc_operand" "+b")
(unspec:DI [(match_dup 0)
(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_operand:HI 3 "accg_operand" "+B")
(match_dup 4)]
UNSPEC_MMRDH))
(set (match_dup 3)
(unspec:HI [(const_int 0)] UNSPEC_MMRDH))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MMRDHU);")
 
(define_insn "*mmrdh"
[(set (match_operand:DI 0 "even_acc_operand" "+b")
(unspec:DI [(match_dup 0)
(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_operand:HI 3 "accg_operand" "+B")
(match_operand:SI 4 "const_int_operand" "n")]
UNSPEC_MMRDH))
(set (match_dup 3)
(unspec:HI [(const_int 0)] UNSPEC_MMRDH))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[4]))
{
default: break;
case FRV_BUILTIN_MMRDHS: return \"mmrdhs %1, %2, %0\";
case FRV_BUILTIN_MMRDHU: return \"mmrdhu %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, mrdh\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mmrdh")])
 
;; Quad multiply (halfword): type "mqmulh"
 
(define_expand "mqmulhs"
[(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "=A")
(unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")
(match_dup 4)]
UNSPEC_MQMULH))
(set (match_operand:V4QI 3 "accg_operand" "=B")
(unspec:V4QI [(const_int 0)] UNSPEC_MQMULH))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MQMULHS);")
 
(define_expand "mqmulhu"
[(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "=A")
(unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")
(match_dup 4)]
UNSPEC_MQMULH))
(set (match_operand:V4QI 3 "accg_operand" "=B")
(unspec:V4QI [(const_int 0)] UNSPEC_MQMULH))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MQMULHU);")
 
(define_insn "*mqmulh"
[(set (match_operand:V4SI 0 "quad_acc_operand" "=A")
(unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")
(match_operand:SI 3 "const_int_operand" "n")]
UNSPEC_MQMULH))
(set (match_operand:V4QI 4 "accg_operand" "=B")
(unspec:V4QI [(const_int 0)] UNSPEC_MQMULH))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[3]))
{
default: break;
case FRV_BUILTIN_MQMULHS: return \"mqmulhs %1, %2, %0\";
case FRV_BUILTIN_MQMULHU: return \"mqmulhu %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, mqmulh\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mqmulh")])
 
(define_insn "*cond_exec_mqmulh"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(parallel [(set (match_operand:V4SI 2 "quad_acc_operand" "=A")
(unspec:V4SI [(match_operand:DI 3 "even_fpr_operand" "h")
(match_operand:DI 4 "even_fpr_operand" "h")
(match_operand:SI 5 "const_int_operand" "n")]
UNSPEC_MQMULH))
(set (match_operand:V4QI 6 "accg_operand" "=B")
(unspec:V4QI [(const_int 0)] UNSPEC_MQMULH))]))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[5]))
{
default: break;
case FRV_BUILTIN_MQMULHS: return \"cmqmulhs %3, %4, %2, %1, %e0\";
case FRV_BUILTIN_MQMULHU: return \"cmqmulhu %3, %4, %2, %1, %e0\";
}
 
fatal_insn (\"Bad media insn, cond_exec_mqmulh\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mqmulh")])
 
;; Quad cross multiply (halfword): type "mqmulxh"
 
(define_expand "mqmulxhs"
[(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "=A")
(unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")
(match_dup 4)]
UNSPEC_MQMULXH))
(set (match_operand:V4QI 3 "accg_operand" "=B")
(unspec:V4QI [(const_int 0)] UNSPEC_MQMULXH))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MQMULXHS);")
 
(define_expand "mqmulxhu"
[(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "=A")
(unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")
(match_dup 4)]
UNSPEC_MQMULXH))
(set (match_operand:V4QI 3 "accg_operand" "=B")
(unspec:V4QI [(const_int 0)] UNSPEC_MQMULXH))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MQMULXHU);")
 
(define_insn "*mqmulxh"
[(set (match_operand:V4SI 0 "quad_acc_operand" "=A")
(unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")
(match_operand:SI 3 "const_int_operand" "n")]
UNSPEC_MQMULXH))
(set (match_operand:V4QI 4 "accg_operand" "=B")
(unspec:V4QI [(const_int 0)] UNSPEC_MQMULXH))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[3]))
{
default: break;
case FRV_BUILTIN_MQMULXHS: return \"mqmulxhs %1, %2, %0\";
case FRV_BUILTIN_MQMULXHU: return \"mqmulxhu %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, mqmulxh\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mqmulxh")])
 
;; Quad product-sum (halfword): type "mqmach"
 
(define_expand "mqmachs"
[(parallel [(set (match_operand:V4SI 0 "even_acc_operand" "+A")
(unspec:V4SI [(match_dup 0)
(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")
(match_operand:V4QI 3 "accg_operand" "+B")
(match_dup 4)]
UNSPEC_MQMACH))
(set (match_dup 3)
(unspec:V4QI [(const_int 0)] UNSPEC_MQMACH))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MQMACHS);")
 
(define_expand "mqmachu"
[(parallel [(set (match_operand:V4SI 0 "even_acc_operand" "+A")
(unspec:V4SI [(match_dup 0)
(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")
(match_operand:V4QI 3 "accg_operand" "+B")
(match_dup 4)]
UNSPEC_MQMACH))
(set (match_dup 3)
(unspec:V4QI [(const_int 0)] UNSPEC_MQMACH))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MQMACHU);")
 
(define_insn "*mqmach"
[(set (match_operand:V4SI 0 "even_acc_operand" "+A")
(unspec:V4SI [(match_dup 0)
(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")
(match_operand:V4QI 3 "accg_operand" "+B")
(match_operand:SI 4 "const_int_operand" "n")]
UNSPEC_MQMACH))
(set (match_dup 3)
(unspec:V4QI [(const_int 0)] UNSPEC_MQMACH))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[4]))
{
default: break;
case FRV_BUILTIN_MQMACHS: return \"mqmachs %1, %2, %0\";
case FRV_BUILTIN_MQMACHU: return \"mqmachu %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, mqmach\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mqmach")])
 
(define_insn "*cond_exec_mqmach"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(parallel [(set (match_operand:V4SI 2 "even_acc_operand" "+A")
(unspec:V4SI [(match_dup 2)
(match_operand:DI 3 "even_fpr_operand" "h")
(match_operand:DI 4 "even_fpr_operand" "h")
(match_operand:V4QI 5 "accg_operand" "+B")
(match_operand:SI 6 "const_int_operand" "n")]
UNSPEC_MQMACH))
(set (match_dup 5)
(unspec:V4QI [(const_int 0)] UNSPEC_MQMACH))]))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[6]))
{
default: break;
case FRV_BUILTIN_MQMACHS: return \"cmqmachs %3, %4, %2, %1, %e0\";
case FRV_BUILTIN_MQMACHU: return \"cmqmachu %3, %4, %2, %1, %e0\";
}
 
fatal_insn (\"Bad media insn, cond_exec_mqmach\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mqmach")])
 
;; Dual complex number product-sum (halfword)
 
(define_expand "mcpxrs"
[(parallel [(set (match_operand:SI 0 "acc_operand" "=a")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_dup 4)]
UNSPEC_MCPX))
(set (match_operand:QI 3 "accg_operand" "=B")
(unspec:QI [(const_int 0)] UNSPEC_MCPX))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MCPXRS);")
 
(define_expand "mcpxru"
[(parallel [(set (match_operand:SI 0 "acc_operand" "=a")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_dup 4)]
UNSPEC_MCPX))
(set (match_operand:QI 3 "accg_operand" "=B")
(unspec:QI [(const_int 0)] UNSPEC_MCPX))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MCPXRU);")
 
(define_expand "mcpxis"
[(parallel [(set (match_operand:SI 0 "acc_operand" "=a")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_dup 4)]
UNSPEC_MCPX))
(set (match_operand:QI 3 "accg_operand" "=B")
(unspec:QI [(const_int 0)] UNSPEC_MCPX))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MCPXIS);")
 
(define_expand "mcpxiu"
[(parallel [(set (match_operand:SI 0 "acc_operand" "=a")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_dup 4)]
UNSPEC_MCPX))
(set (match_operand:QI 3 "accg_operand" "=B")
(unspec:QI [(const_int 0)] UNSPEC_MCPX))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MCPXIU);")
 
(define_insn "*mcpx"
[(parallel [(set (match_operand:SI 0 "acc_operand" "=a")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")
(match_operand:SI 3 "const_int_operand" "n")]
UNSPEC_MCPX))
(set (match_operand:QI 4 "accg_operand" "=B")
(unspec:QI [(const_int 0)] UNSPEC_MCPX))])]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[3]))
{
default: break;
case FRV_BUILTIN_MCPXRS: return \"mcpxrs %1, %2, %0\";
case FRV_BUILTIN_MCPXRU: return \"mcpxru %1, %2, %0\";
case FRV_BUILTIN_MCPXIS: return \"mcpxis %1, %2, %0\";
case FRV_BUILTIN_MCPXIU: return \"mcpxiu %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, mcpx\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mcpx")])
 
(define_insn "*cond_exec_mcpx"
[(cond_exec
(match_operator 0 "ccr_eqne_operator"
[(match_operand 1 "cr_operand" "C")
(const_int 0)])
(parallel [(set (match_operand:SI 2 "acc_operand" "=a")
(unspec:SI [(match_operand:SI 3 "fpr_operand" "f")
(match_operand:SI 4 "fpr_operand" "f")
(match_operand:SI 5 "const_int_operand" "n")]
UNSPEC_MCPX))
(set (match_operand:QI 6 "accg_operand" "=B")
(unspec:QI [(const_int 0)] UNSPEC_MCPX))]))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[5]))
{
default: break;
case FRV_BUILTIN_MCPXRS: return \"cmcpxrs %3, %4, %2, %1, %e0\";
case FRV_BUILTIN_MCPXRU: return \"cmcpxru %3, %4, %2, %1, %e0\";
case FRV_BUILTIN_MCPXIS: return \"cmcpxis %3, %4, %2, %1, %e0\";
case FRV_BUILTIN_MCPXIU: return \"cmcpxiu %3, %4, %2, %1, %e0\";
}
 
fatal_insn (\"Bad media insn, cond_exec_mcpx\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mcpx")])
 
;; Quad complex number product-sum (halfword): type "mqcpx"
 
(define_expand "mqcpxrs"
[(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b")
(unspec:DI [(match_operand:DI 1 "fpr_operand" "f")
(match_operand:DI 2 "fpr_operand" "f")
(match_dup 4)]
UNSPEC_MQCPX))
(set (match_operand:HI 3 "accg_operand" "=B")
(unspec:HI [(const_int 0)] UNSPEC_MQCPX))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MQCPXRS);")
 
(define_expand "mqcpxru"
[(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b")
(unspec:DI [(match_operand:DI 1 "fpr_operand" "f")
(match_operand:DI 2 "fpr_operand" "f")
(match_dup 4)]
UNSPEC_MQCPX))
(set (match_operand:HI 3 "accg_operand" "=B")
(unspec:HI [(const_int 0)] UNSPEC_MQCPX))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MQCPXRU);")
 
(define_expand "mqcpxis"
[(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b")
(unspec:DI [(match_operand:DI 1 "fpr_operand" "f")
(match_operand:DI 2 "fpr_operand" "f")
(match_dup 4)]
UNSPEC_MQCPX))
(set (match_operand:HI 3 "accg_operand" "=B")
(unspec:HI [(const_int 0)] UNSPEC_MQCPX))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MQCPXIS);")
 
(define_expand "mqcpxiu"
[(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b")
(unspec:DI [(match_operand:DI 1 "fpr_operand" "f")
(match_operand:DI 2 "fpr_operand" "f")
(match_dup 4)]
UNSPEC_MQCPX))
(set (match_operand:HI 3 "accg_operand" "=B")
(unspec:HI [(const_int 0)] UNSPEC_MQCPX))])]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MQCPXIU);")
 
(define_insn "*mqcpx"
[(set (match_operand:DI 0 "even_acc_operand" "=b")
(unspec:DI [(match_operand:DI 1 "fpr_operand" "f")
(match_operand:DI 2 "fpr_operand" "f")
(match_operand:SI 3 "const_int_operand" "n")]
UNSPEC_MQCPX))
(set (match_operand:HI 4 "accg_operand" "=B")
(unspec:HI [(const_int 0)] UNSPEC_MQCPX))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[3]))
{
default: break;
case FRV_BUILTIN_MQCPXRS: return \"mqcpxrs %1, %2, %0\";
case FRV_BUILTIN_MQCPXRU: return \"mqcpxru %1, %2, %0\";
case FRV_BUILTIN_MQCPXIS: return \"mqcpxis %1, %2, %0\";
case FRV_BUILTIN_MQCPXIU: return \"mqcpxiu %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, mqcpx\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mqcpx")])
 
;; Cut: type "mcut"
 
(define_expand "mcut"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "acc_operand" "a")
(match_operand:SI 2 "fpr_or_int6_operand" "fI")
(match_operand:QI 3 "accg_operand" "B")
(match_dup 4)]
UNSPEC_MCUT))]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MCUT);")
 
(define_expand "mcutss"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "acc_operand" "a")
(match_operand:SI 2 "fpr_or_int6_operand" "fI")
(match_operand:QI 3 "accg_operand" "B")
(match_dup 4)]
UNSPEC_MCUT))]
"TARGET_MEDIA"
"operands[4] = GEN_INT (FRV_BUILTIN_MCUTSS);")
 
(define_insn "*mcut"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "acc_operand" "a")
(match_operand:SI 2 "fpr_or_int6_operand" "fI")
(match_operand:QI 3 "accg_operand" "B")
(match_operand:SI 4 "const_int_operand" "n")]
UNSPEC_MCUT))]
"TARGET_MEDIA"
"*
{
switch (INTVAL (operands[4]))
{
default: break;
case FRV_BUILTIN_MCUT: return \"mcut%i2 %1, %2, %0\";
case FRV_BUILTIN_MCUTSS: return \"mcutss%i2 %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, mcut\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mcut")])
 
;; Accumulator read: type "mrdacc"
 
(define_insn "mrdacc"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "acc_operand" "a")] UNSPEC_MRDACC))]
"TARGET_MEDIA"
"mrdacc %1, %0"
[(set_attr "length" "4")
(set_attr "type" "mrdacc")])
 
(define_insn "mrdaccg"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:QI 1 "accg_operand" "B")] UNSPEC_MRDACCG))]
"TARGET_MEDIA"
"mrdaccg %1, %0"
[(set_attr "length" "4")
(set_attr "type" "mrdacc")])
 
;; Accumulator write: type "mwtacc"
 
(define_insn "mwtacc"
[(set (match_operand:SI 0 "acc_operand" "=a")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")] UNSPEC_MWTACC))]
"TARGET_MEDIA"
"mwtacc %1, %0"
[(set_attr "length" "4")
(set_attr "type" "mwtacc")])
 
(define_insn "mwtaccg"
[(set (match_operand:QI 0 "accg_operand" "=B")
(unspec:QI [(match_operand:SI 1 "fpr_operand" "f")] UNSPEC_MWTACCG))]
"TARGET_MEDIA"
"mwtaccg %1, %0"
[(set_attr "length" "4")
(set_attr "type" "mwtacc")])
 
;; Trap: This one executes on the control unit, not the media units.
 
(define_insn "mtrap"
[(unspec_volatile [(const_int 0)] UNSPEC_MTRAP)]
"TARGET_MEDIA"
"mtrap"
[(set_attr "length" "4")
(set_attr "type" "trap")])
 
;; Clear single accumulator: type "mclracc"
 
(define_insn "mclracc_internal"
[(set (match_operand:SI 0 "acc_operand" "=a")
(unspec:SI [(const_int 0)] UNSPEC_MCLRACC))
(set (match_operand:QI 1 "accg_operand" "=B")
(unspec:QI [(const_int 0)] UNSPEC_MCLRACC))]
"TARGET_MEDIA"
"mclracc %0,#0"
[(set_attr "length" "4")
(set_attr "type" "mclracc")])
 
(define_expand "mclracc"
[(parallel [(set (match_operand:SI 0 "acc_operand" "=a")
(unspec:SI [(const_int 0)] UNSPEC_MCLRACC))
(set (match_dup 1)
(unspec:QI [(const_int 0)] UNSPEC_MCLRACC))])]
"TARGET_MEDIA"
"
{
if (GET_CODE (operands[0]) != REG || !ACC_P (REGNO (operands[0])))
FAIL;
 
operands[1] = frv_matching_accg_for_acc (operands[0]);
}")
 
;; Clear all accumulators: type "mclracca"
 
(define_insn "mclracca8_internal"
[(set (match_operand:V4SI 0 "quad_acc_operand" "=b")
(unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA))
(set (match_operand:V4SI 1 "quad_acc_operand" "=b")
(unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA))
(set (match_operand:V4QI 2 "accg_operand" "=B")
(unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA))
(set (match_operand:V4QI 3 "accg_operand" "=B")
(unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA))]
"TARGET_MEDIA && TARGET_ACC_8"
"mclracc acc0,#1"
[(set_attr "length" "4")
(set_attr "type" "mclracca")])
 
(define_insn "mclracca4_internal"
[(set (match_operand:V4SI 0 "quad_acc_operand" "=b")
(unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA))
(set (match_operand:V4QI 1 "accg_operand" "=B")
(unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA))]
"TARGET_MEDIA && TARGET_ACC_4"
"mclracc acc0,#1"
[(set_attr "length" "4")
(set_attr "type" "mclracca")])
 
(define_expand "mclracca8"
[(parallel [(set (match_dup 0) (unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA))
(set (match_dup 1) (unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA))
(set (match_dup 2) (unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA))
(set (match_dup 3) (unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA))])]
"TARGET_MEDIA && TARGET_ACC_8"
"
{
operands[0] = gen_rtx_REG (V4SImode, ACC_FIRST);
operands[1] = gen_rtx_REG (V4SImode, ACC_FIRST + (~3 & ACC_MASK));
operands[2] = gen_rtx_REG (V4QImode, ACCG_FIRST);
operands[3] = gen_rtx_REG (V4QImode, ACCG_FIRST + (~3 & ACC_MASK));
}")
 
(define_expand "mclracca4"
[(parallel [(set (match_dup 0) (unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA))
(set (match_dup 1) (unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA))])]
"TARGET_MEDIA && TARGET_ACC_4"
"
{
operands[0] = gen_rtx_REG (V4SImode, ACC_FIRST);
operands[1] = gen_rtx_REG (V4QImode, ACCG_FIRST);
}")
 
(define_insn "mcop1"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")] UNSPEC_MCOP1))]
"TARGET_MEDIA_REV1"
"mcop1 %1, %2, %0"
[(set_attr "length" "4")
;; What is the class of the insn ???
(set_attr "type" "multi")])
 
(define_insn "mcop2"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
(match_operand:SI 2 "fpr_operand" "f")] UNSPEC_MCOP2))]
"TARGET_MEDIA_REV1"
"mcop2 %1, %2, %0"
[(set_attr "length" "4")
;; What is the class of the insn ???
(set_attr "type" "multi")])
 
(define_insn "*mdunpackh_internal"
[(set (match_operand:V4SI 0 "quad_fpr_operand" "=x")
(unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")]
UNSPEC_MDUNPACKH_INTERNAL))]
"TARGET_MEDIA_REV1"
"mdunpackh %1, %0"
[(set_attr "length" "4")
(set_attr "type" "mdunpackh")])
 
(define_insn_and_split "mdunpackh"
[(set (match_operand:V4SI 0 "memory_operand" "=o")
(unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")]
UNSPEC_MDUNPACKH))
(clobber (match_scratch:V4SI 2 "=x"))]
"TARGET_MEDIA_REV1"
"#"
"reload_completed"
[(set (match_dup 2)
(unspec:V4SI [(match_dup 1)] UNSPEC_MDUNPACKH_INTERNAL))
(set (match_dup 3)
(match_dup 4))
(set (match_dup 5)
(match_dup 6))]
"
{
operands[3] = change_address (operands[0], DImode, NULL_RTX);
operands[4] = gen_rtx_REG (DImode, REGNO (operands[2]));
operands[5] = frv_index_memory (operands[0], DImode, 1);
operands[6] = gen_rtx_REG (DImode, REGNO (operands[2])+2);
}"
[(set_attr "length" "20")
(set_attr "type" "multi")])
 
(define_insn "*mbtohe_internal"
[(set (match_operand:V4SI 0 "quad_fpr_operand" "=x")
(unspec:V4SI [(match_operand:SI 1 "fpr_operand" "f")]
UNSPEC_MBTOHE_INTERNAL))]
"TARGET_MEDIA_REV1"
"mbtohe %1, %0"
[(set_attr "length" "4")
(set_attr "type" "mbhconve")])
 
(define_insn_and_split "mbtohe"
[(set (match_operand:V4SI 0 "memory_operand" "=o")
(unspec:V4SI [(match_operand:SI 1 "fpr_operand" "f")]
UNSPEC_MBTOHE))
(clobber (match_scratch:V4SI 2 "=x"))]
"TARGET_MEDIA_REV1"
"#"
"reload_completed"
[(set (match_dup 2)
(unspec:V4SI [(match_dup 1)] UNSPEC_MBTOHE_INTERNAL))
(set (match_dup 3)
(match_dup 4))
(set (match_dup 5)
(match_dup 6))]
"
{
operands[3] = change_address (operands[0], DImode, NULL_RTX);
operands[4] = gen_rtx_REG (DImode, REGNO (operands[2]));
operands[5] = frv_index_memory (operands[0], DImode, 1);
operands[6] = gen_rtx_REG (DImode, REGNO (operands[2])+2);
}"
[(set_attr "length" "20")
(set_attr "type" "multi")])
 
;; Quad product-sum (halfword) instructions only found on the FR400.
;; type "mqmach"
 
(define_expand "mqxmachs"
[(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "")
(unspec:V4SI [(match_dup 0)
(match_operand:DI 1 "even_fpr_operand" "")
(match_operand:DI 2 "even_fpr_operand" "")
(match_operand:V4QI 3 "accg_operand" "")
(match_dup 4)]
UNSPEC_MQMACH2))
(set (match_dup 3)
(unspec:V4QI [(const_int 0)] UNSPEC_MQMACH2))])]
"TARGET_MEDIA_REV2"
"operands[4] = GEN_INT (FRV_BUILTIN_MQXMACHS);")
 
(define_expand "mqxmacxhs"
[(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "")
(unspec:V4SI [(match_dup 0)
(match_operand:DI 1 "even_fpr_operand" "")
(match_operand:DI 2 "even_fpr_operand" "")
(match_operand:V4QI 3 "accg_operand" "")
(match_dup 4)]
UNSPEC_MQMACH2))
(set (match_dup 3)
(unspec:V4QI [(const_int 0)] UNSPEC_MQMACH2))])]
"TARGET_MEDIA_REV2"
"operands[4] = GEN_INT (FRV_BUILTIN_MQXMACXHS);")
 
(define_expand "mqmacxhs"
[(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "")
(unspec:V4SI [(match_dup 0)
(match_operand:DI 1 "even_fpr_operand" "")
(match_operand:DI 2 "even_fpr_operand" "")
(match_operand:V4QI 3 "accg_operand" "")
(match_dup 4)]
UNSPEC_MQMACH2))
(set (match_dup 3)
(unspec:V4QI [(const_int 0)] UNSPEC_MQMACH2))])]
"TARGET_MEDIA_REV2"
"operands[4] = GEN_INT (FRV_BUILTIN_MQMACXHS);")
 
(define_insn "*mqmach2"
[(set (match_operand:V4SI 0 "quad_acc_operand" "+A")
(unspec:V4SI [(match_dup 0)
(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")
(match_operand:V4QI 3 "accg_operand" "+B")
(match_operand:SI 4 "const_int_operand" "n")]
UNSPEC_MQMACH2))
(set (match_dup 3)
(unspec:V4QI [(const_int 0)] UNSPEC_MQMACH2))]
"TARGET_MEDIA_REV2"
"*
{
switch (INTVAL (operands[4]))
{
default: break;
case FRV_BUILTIN_MQXMACHS: return \"mqxmachs %1, %2, %0\";
case FRV_BUILTIN_MQXMACXHS: return \"mqxmacxhs %1, %2, %0\";
case FRV_BUILTIN_MQMACXHS: return \"mqmacxhs %1, %2, %0\";
}
 
fatal_insn (\"Bad media insn, mqmach2\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mqmach")])
 
;; Accumulator addition/subtraction: type "maddacc"
 
(define_expand "maddaccs"
[(parallel [(set (match_operand:SI 0 "acc_operand" "")
(unspec:SI [(match_operand:DI 1 "even_acc_operand" "")]
UNSPEC_MADDACC))
(set (match_operand:QI 2 "accg_operand" "")
(unspec:QI [(match_operand:HI 3 "accg_operand" "")
(match_dup 4)]
UNSPEC_MADDACC))])]
"TARGET_MEDIA_REV2"
"operands[4] = GEN_INT (FRV_BUILTIN_MADDACCS);")
 
(define_expand "msubaccs"
[(parallel [(set (match_operand:SI 0 "acc_operand" "")
(unspec:SI [(match_operand:DI 1 "even_acc_operand" "")]
UNSPEC_MADDACC))
(set (match_operand:QI 2 "accg_operand" "")
(unspec:QI [(match_operand:HI 3 "accg_operand" "")
(match_dup 4)]
UNSPEC_MADDACC))])]
"TARGET_MEDIA_REV2"
"operands[4] = GEN_INT (FRV_BUILTIN_MSUBACCS);")
 
(define_insn "masaccs"
[(set (match_operand:DI 0 "even_acc_operand" "=b")
(unspec:DI [(match_operand:DI 1 "even_acc_operand" "b")]
UNSPEC_MASACCS))
(set (match_operand:HI 2 "accg_operand" "=B")
(unspec:HI [(match_operand:HI 3 "accg_operand" "B")]
UNSPEC_MASACCS))]
"TARGET_MEDIA_REV2"
"masaccs %1, %0"
[(set_attr "length" "4")
(set_attr "type" "maddacc")])
 
(define_insn "*maddacc"
[(set (match_operand:SI 0 "acc_operand" "=a")
(unspec:SI [(match_operand:DI 1 "even_acc_operand" "b")]
UNSPEC_MADDACC))
(set (match_operand:QI 2 "accg_operand" "=B")
(unspec:QI [(match_operand:HI 3 "accg_operand" "B")
(match_operand:SI 4 "const_int_operand" "n")]
UNSPEC_MADDACC))]
"TARGET_MEDIA_REV2"
"*
{
switch (INTVAL (operands[4]))
{
default: break;
case FRV_BUILTIN_MADDACCS: return \"maddaccs %1, %0\";
case FRV_BUILTIN_MSUBACCS: return \"msubaccs %1, %0\";
}
 
fatal_insn (\"Bad media insn, maddacc\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "maddacc")])
 
;; Dual accumulator addition/subtraction: type "mdaddacc"
 
(define_expand "mdaddaccs"
[(parallel [(set (match_operand:DI 0 "even_acc_operand" "")
(unspec:DI [(match_operand:V4SI 1 "quad_acc_operand" "")]
UNSPEC_MDADDACC))
(set (match_operand:HI 2 "accg_operand" "")
(unspec:HI [(match_operand:V4QI 3 "accg_operand" "")
(match_dup 4)]
UNSPEC_MDADDACC))])]
"TARGET_MEDIA_REV2"
"operands[4] = GEN_INT (FRV_BUILTIN_MDADDACCS);")
 
(define_expand "mdsubaccs"
[(parallel [(set (match_operand:DI 0 "even_acc_operand" "")
(unspec:DI [(match_operand:V4SI 1 "quad_acc_operand" "")]
UNSPEC_MDADDACC))
(set (match_operand:HI 2 "accg_operand" "")
(unspec:HI [(match_operand:V4QI 3 "accg_operand" "")
(match_dup 4)]
UNSPEC_MDADDACC))])]
"TARGET_MEDIA_REV2"
"operands[4] = GEN_INT (FRV_BUILTIN_MDSUBACCS);")
 
(define_insn "mdasaccs"
[(set (match_operand:V4SI 0 "quad_acc_operand" "=A")
(unspec:V4SI [(match_operand:V4SI 1 "quad_acc_operand" "A")]
UNSPEC_MDASACCS))
(set (match_operand:V4QI 2 "accg_operand" "=B")
(unspec:V4QI [(match_operand:V4QI 3 "accg_operand" "B")]
UNSPEC_MDASACCS))]
"TARGET_MEDIA_REV2"
"mdasaccs %1, %0"
[(set_attr "length" "4")
(set_attr "type" "mdaddacc")])
 
(define_insn "*mdaddacc"
[(set (match_operand:DI 0 "even_acc_operand" "=b")
(unspec:DI [(match_operand:V4SI 1 "quad_acc_operand" "A")]
UNSPEC_MDADDACC))
(set (match_operand:HI 2 "accg_operand" "=B")
(unspec:HI [(match_operand:V4QI 3 "accg_operand" "B")
(match_operand:SI 4 "const_int_operand" "n")]
UNSPEC_MDADDACC))]
"TARGET_MEDIA_REV2"
"*
{
switch (INTVAL (operands[4]))
{
default: break;
case FRV_BUILTIN_MDADDACCS: return \"mdaddaccs %1, %0\";
case FRV_BUILTIN_MDSUBACCS: return \"mdsubaccs %1, %0\";
}
 
fatal_insn (\"Bad media insn, mdaddacc\", insn);
}"
[(set_attr "length" "4")
(set_attr "type" "mdaddacc")])
 
;; Dual absolute (halfword): type "mabsh"
 
(define_insn "mabshs"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")] UNSPEC_MABSHS))]
"TARGET_MEDIA_REV2"
"mabshs %1, %0"
[(set_attr "length" "4")
(set_attr "type" "mabsh")])
 
;; Dual rotate: type "mdrot"
 
(define_insn "mdrotli"
[(set (match_operand:DI 0 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:SI 2 "uint5_operand" "I")]
UNSPEC_MDROTLI))]
"TARGET_MEDIA_REV2"
"mdrotli %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mdrot")])
 
;; Dual coupling (concatenation): type "mcpl"
 
(define_insn "mcplhi"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:DI 1 "fpr_operand" "h")
(match_operand:SI 2 "uint4_operand" "I")]
UNSPEC_MCPLHI))]
"TARGET_MEDIA_REV2"
"mcplhi %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mcpl")])
 
(define_insn "mcpli"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:DI 1 "fpr_operand" "h")
(match_operand:SI 2 "uint5_operand" "I")]
UNSPEC_MCPLI))]
"TARGET_MEDIA_REV2"
"mcpli %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mcpl")])
 
;; Dual cut: type "mdcut"
 
(define_insn "mdcutssi"
[(set (match_operand:DI 0 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:DI 1 "even_acc_operand" "b")
(match_operand:SI 2 "int6_operand" "I")
(match_operand:HI 3 "accg_operand" "B")]
UNSPEC_MDCUTSSI))]
"TARGET_MEDIA_REV2"
"mdcutssi %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mdcut")])
 
;; Quad saturate (halfword): type "mqsath"
 
(define_insn "mqsaths"
[(set (match_operand:DI 0 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")]
UNSPEC_MQSATHS))]
"TARGET_MEDIA_REV2"
"mqsaths %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mqsath")])
 
;; Quad limit instructions: type "mqlimh"
 
(define_insn "mqlclrhs"
[(set (match_operand:DI 0 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")]
UNSPEC_MQLCLRHS))]
"TARGET_MEDIA_FR450"
"mqlclrhs %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mqlimh")])
 
(define_insn "mqlmths"
[(set (match_operand:DI 0 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:DI 2 "even_fpr_operand" "h")]
UNSPEC_MQLMTHS))]
"TARGET_MEDIA_FR450"
"mqlmths %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mqlimh")])
 
(define_insn "mqsllhi"
[(set (match_operand:DI 0 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:SI 2 "int6_operand" "I")]
UNSPEC_MQSLLHI))]
"TARGET_MEDIA_FR450"
"mqsllhi %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mqshift")])
 
(define_insn "mqsrahi"
[(set (match_operand:DI 0 "even_fpr_operand" "=h")
(unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
(match_operand:SI 2 "int6_operand" "I")]
UNSPEC_MQSRAHI))]
"TARGET_MEDIA_FR450"
"mqsrahi %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mqshift")])
 
;; Set hi/lo instructions: type "mset"
 
(define_insn "mhsetlos"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "0")
(match_operand:SI 2 "int12_operand" "NOP")]
UNSPEC_MHSETLOS))]
"TARGET_MEDIA_REV2"
"mhsetlos %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mset")])
 
(define_insn "mhsetloh"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "0")
(match_operand:SI 2 "int5_operand" "I")]
UNSPEC_MHSETLOH))]
"TARGET_MEDIA_REV2"
"mhsetloh %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mset")])
 
(define_insn "mhsethis"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "0")
(match_operand:SI 2 "int12_operand" "NOP")]
UNSPEC_MHSETHIS))]
"TARGET_MEDIA_REV2"
"mhsethis %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mset")])
 
(define_insn "mhsethih"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "0")
(match_operand:SI 2 "int5_operand" "I")]
UNSPEC_MHSETHIH))]
"TARGET_MEDIA_REV2"
"mhsethih %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mset")])
 
(define_insn "mhdsets"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "int12_operand" "NOP")]
UNSPEC_MHDSETS))]
"TARGET_MEDIA_REV2"
"mhdsets %1, %0"
[(set_attr "length" "4")
(set_attr "type" "mset")])
 
(define_insn "mhdseth"
[(set (match_operand:SI 0 "fpr_operand" "=f")
(unspec:SI [(match_operand:SI 1 "fpr_operand" "0")
(match_operand:SI 2 "int5_operand" "I")]
UNSPEC_MHDSETH))]
"TARGET_MEDIA_REV2"
"mhdseth %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mset")])
 
;;-----------------------------------------------------------------------------
 
(define_expand "symGOT2reg"
[(match_operand:SI 0 "" "")
(match_operand:SI 1 "" "")
(match_operand:SI 2 "" "")
(match_operand:SI 3 "" "")]
""
"
{
rtx insn;
 
insn = emit_insn (gen_symGOT2reg_i (operands[0], operands[1], operands[2], operands[3]));
 
MEM_READONLY_P (SET_SRC (PATTERN (insn))) = 1;
 
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, operands[1],
REG_NOTES (insn));
 
DONE;
}")
 
(define_expand "symGOT2reg_i"
[(set (match_operand:SI 0 "" "")
(mem:SI (plus:SI (match_operand:SI 2 "" "")
(const:SI (unspec:SI [(match_operand:SI 1 "" "")
(match_operand:SI 3 "" "")]
UNSPEC_GOT)))))]
""
"")
 
(define_expand "symGOT2reg_hilo"
[(set (match_dup 6)
(high:SI (const:SI (unspec:SI [(match_operand:SI 1 "" "")
(match_dup 4)] UNSPEC_GOT))))
(set (match_dup 5)
(lo_sum:SI (match_dup 6)
(const:SI (unspec:SI [(match_dup 1)
(match_operand:SI 3 "" "")]
UNSPEC_GOT))))
(set (match_operand:SI 0 "" "")
(mem:SI (plus:SI (match_dup 5)
(match_operand:SI 2 "" ""))))
]
""
"
{
if (no_new_pseudos)
operands[6] = operands[5] = operands[0];
else
{
operands[6] = gen_reg_rtx (SImode);
operands[5] = gen_reg_rtx (SImode);
}
 
operands[4] = GEN_INT (INTVAL (operands[3]) + 1);
operands[3] = GEN_INT (INTVAL (operands[3]) + 2);
}")
 
(define_expand "symGOTOFF2reg_hilo"
[(set (match_dup 6)
(high:SI (const:SI (unspec:SI [(match_operand:SI 1 "" "")
(match_dup 4)] UNSPEC_GOT))))
(set (match_dup 5)
(lo_sum:SI (match_dup 6)
(const:SI (unspec:SI [(match_dup 1)
(match_operand:SI 3 "" "")]
UNSPEC_GOT))))
(set (match_operand:SI 0 "" "")
(plus:SI (match_dup 5)
(match_operand:SI 2 "" "")))
]
""
"
{
if (no_new_pseudos)
operands[6] = operands[5] = operands[0];
else
{
operands[6] = gen_reg_rtx (SImode);
operands[5] = gen_reg_rtx (SImode);
}
 
operands[4] = GEN_INT (INTVAL (operands[3]) + 1);
operands[3] = GEN_INT (INTVAL (operands[3]) + 2);
}")
 
(define_expand "symGOTOFF2reg"
[(match_operand:SI 0 "" "")
(match_operand:SI 1 "" "")
(match_operand:SI 2 "" "")
(match_operand:SI 3 "" "")]
""
"
{
rtx insn = emit_insn (gen_symGOTOFF2reg_i (operands[0], operands[1], operands[2], operands[3]));
 
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, operands[1],
REG_NOTES (insn));
 
DONE;
}")
 
(define_expand "symGOTOFF2reg_i"
[(set (match_operand:SI 0 "" "")
(plus:SI (match_operand:SI 2 "" "")
(const:SI
(unspec:SI [(match_operand:SI 1 "" "")
(match_operand:SI 3 "" "")]
UNSPEC_GOT))))]
""
"")
 
(define_expand "symGPREL2reg"
[(match_operand:SI 0 "" "")
(match_operand:SI 1 "" "")
(match_operand:SI 2 "" "")
(match_operand:SI 3 "" "")
(match_dup 4)]
""
"
{
rtx insn;
 
if (no_new_pseudos)
operands[4] = operands[0];
else
operands[4] = gen_reg_rtx (SImode);
 
emit_insn (frv_gen_GPsym2reg (operands[4], operands[2]));
 
insn = emit_insn (gen_symGOTOFF2reg_i (operands[0], operands[1],
operands[4], operands[3]));
 
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, operands[1],
REG_NOTES (insn));
 
DONE;
}")
 
(define_expand "symGPREL2reg_hilo"
[(match_operand:SI 0 "" "")
(match_operand:SI 1 "" "")
(match_operand:SI 2 "" "")
(match_operand:SI 3 "" "")
(match_dup 4)]
""
"
{
rtx insn;
 
if (no_new_pseudos)
{
emit_insn (gen_symGOT2reg (operands[0], operands[1], operands[2],
GEN_INT (R_FRV_GOT12)));
DONE;
}
 
operands[4] = gen_reg_rtx (SImode);
 
emit_insn (frv_gen_GPsym2reg (operands[4], operands[2]));
 
insn = emit_insn (gen_symGOTOFF2reg_hilo (operands[0], operands[1],
operands[4], operands[3]));
 
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, operands[1],
REG_NOTES (insn));
 
DONE;
}")
(define_constants
[
(UNSPEC_SMUL 154)
(UNSPEC_UMUL 155)
(UNSPEC_SMU 156)
(UNSPEC_ADDSS 157)
(UNSPEC_SUBSS 158)
(UNSPEC_SLASS 159)
(UNSPEC_SCAN 160)
(UNSPEC_INTSS 161)
(UNSPEC_SCUTSS 162)
(UNSPEC_PREFETCH0 163)
(UNSPEC_PREFETCH 164)
(UNSPEC_IACCreadll 165)
(UNSPEC_IACCreadl 166)
(UNSPEC_IACCsetll 167)
(UNSPEC_IACCsetl 168)
(UNSPEC_SMASS 169)
(UNSPEC_SMSSS 170)
(UNSPEC_IMUL 171)
 
(IACC0_REG 171)
])
 
(define_insn "smul"
[(set (match_operand:DI 0 "integer_register_operand" "=d")
(unspec:DI [(match_operand:SI 1 "integer_register_operand" "d")
(match_operand:SI 2 "integer_register_operand" "d")]
UNSPEC_SMUL))]
""
"smul %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mul")])
 
(define_insn "umul"
[(set (match_operand:DI 0 "integer_register_operand" "=d")
(unspec:DI [(match_operand:SI 1 "integer_register_operand" "d")
(match_operand:SI 2 "integer_register_operand" "d")]
UNSPEC_UMUL))]
""
"umul %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "mul")])
 
(define_insn "smass"
[(set (reg:DI IACC0_REG)
(unspec:DI [(match_operand:SI 0 "integer_register_operand" "d")
(match_operand:SI 1 "integer_register_operand" "d")
(reg:DI IACC0_REG)]
UNSPEC_SMASS))]
"TARGET_FR405_BUILTINS"
"smass %1, %0"
[(set_attr "length" "4")
(set_attr "type" "macc")])
 
(define_insn "smsss"
[(set (reg:DI IACC0_REG)
(unspec:DI [(match_operand:SI 0 "integer_register_operand" "d")
(match_operand:SI 1 "integer_register_operand" "d")
(reg:DI IACC0_REG)]
UNSPEC_SMSSS))]
"TARGET_FR405_BUILTINS"
"smsss %1, %0"
[(set_attr "length" "4")
(set_attr "type" "macc")])
 
(define_insn "smu"
[(set (reg:DI IACC0_REG)
(unspec:DI [(match_operand:SI 0 "integer_register_operand" "d")
(match_operand:SI 1 "integer_register_operand" "d")]
UNSPEC_SMU))]
"TARGET_FR405_BUILTINS"
"smu %1, %0"
[(set_attr "length" "4")
(set_attr "type" "macc")])
 
(define_insn "addss"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(unspec:SI [(match_operand:SI 1 "integer_register_operand" "d")
(match_operand:SI 2 "integer_register_operand" "d")]
UNSPEC_ADDSS))]
"TARGET_FR405_BUILTINS"
"addss %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_insn "subss"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(unspec:SI [(match_operand:SI 1 "integer_register_operand" "d")
(match_operand:SI 2 "integer_register_operand" "d")]
UNSPEC_SUBSS))]
"TARGET_FR405_BUILTINS"
"subss %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_insn "slass"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(unspec:SI [(match_operand:SI 1 "integer_register_operand" "d")
(match_operand:SI 2 "integer_register_operand" "d")]
UNSPEC_SLASS))]
"TARGET_FR405_BUILTINS"
"slass %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "int")])
 
(define_insn "scan"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(unspec:SI [(match_operand:SI 1 "integer_register_operand" "d")
(match_operand:SI 2 "integer_register_operand" "d")]
UNSPEC_SCAN))]
""
"scan %1, %2, %0"
[(set_attr "length" "4")
(set_attr "type" "scan")])
 
(define_insn "scutss"
[(set (match_operand:SI 0 "integer_register_operand" "=d")
(unspec:SI [(match_operand:SI 1 "integer_register_operand" "d")
(reg:DI IACC0_REG)]
UNSPEC_SCUTSS))]
"TARGET_FR405_BUILTINS"
"scutss %1,%0"
[(set_attr "length" "4")
(set_attr "type" "cut")])
 
(define_insn "frv_prefetch0"
[(prefetch (unspec:SI [(match_operand:SI 0 "register_operand" "r")]
UNSPEC_PREFETCH0)
(const_int 0)
(const_int 0))]
""
"dcpl %0, gr0, #0"
[(set_attr "length" "4")])
 
(define_insn "frv_prefetch"
[(prefetch (unspec:SI [(match_operand:SI 0 "register_operand" "r")]
UNSPEC_PREFETCH)
(const_int 0)
(const_int 0))]
"TARGET_FR500_FR550_BUILTINS"
"nop.p\\n\\tnldub @(%0, gr0), gr0"
[(set_attr "length" "8")])
 
;; TLS patterns
 
(define_insn "call_gettlsoff"
[(set (match_operand:SI 0 "register_operand" "=D09")
(unspec:SI
[(match_operand:SI 1 "symbolic_operand" "")]
UNSPEC_GETTLSOFF))
(clobber (reg:SI GR8_REG))
(clobber (reg:SI LRREG))
(use (match_operand:SI 2 "register_operand" "D15"))]
"HAVE_AS_TLS"
"call #gettlsoff(%a1)"
[(set_attr "length" "4")
(set_attr "type" "load_or_call")])
 
;; We have to expand this like a libcall (it sort of actually is)
;; because otherwise sched may move, for example, an insn that sets up
;; GR8 for a subsequence call before the *tls_indirect_call insn, and
;; then reload won't be able to fix things up.
(define_expand "tls_indirect_call"
[(set (reg:DI GR8_REG)
(match_operand:DI 2 "register_operand" ""))
(parallel
[(set (reg:SI GR9_REG)
(unspec:SI
[(match_operand:SI 1 "symbolic_operand" "")
(reg:DI GR8_REG)]
UNSPEC_TLS_INDIRECT_CALL))
(clobber (reg:SI GR8_REG))
(clobber (reg:SI LRREG))
(use (match_operand:SI 3 "register_operand" ""))])
(set (match_operand:SI 0 "register_operand" "")
(reg:SI GR9_REG))]
"HAVE_AS_TLS")
 
(define_insn "*tls_indirect_call"
[(set (reg:SI GR9_REG)
(unspec:SI
[(match_operand:SI 0 "symbolic_operand" "")
(reg:DI GR8_REG)]
UNSPEC_TLS_INDIRECT_CALL))
(clobber (reg:SI GR8_REG))
(clobber (reg:SI LRREG))
;; If there was a way to represent the fact that we don't need GR9
;; or GR15 to be set before this instruction (it could be in
;; parallel), we could use it here. This change wouldn't apply to
;; call_gettlsoff, thought, since the linker may turn the latter
;; into ldi @(gr15,offset),gr9.
(use (match_operand:SI 1 "register_operand" "D15"))]
"HAVE_AS_TLS"
"calll #gettlsoff(%a0)@(gr8,gr0)"
[(set_attr "length" "4")
(set_attr "type" "jumpl")])
 
(define_insn "tls_load_gottlsoff12"
[(set (match_operand:SI 0 "register_operand" "=r")
(unspec:SI
[(match_operand:SI 1 "symbolic_operand" "")
(match_operand:SI 2 "register_operand" "r")]
UNSPEC_TLS_LOAD_GOTTLSOFF12))]
"HAVE_AS_TLS"
"ldi @(%2, #gottlsoff12(%1)), %0"
[(set_attr "length" "4")])
 
(define_expand "tlsoff_hilo"
[(set (match_operand:SI 0 "register_operand" "=r")
(high:SI (const:SI (unspec:SI
[(match_operand:SI 1 "symbolic_operand" "")
(match_operand:SI 2 "immediate_operand" "n")]
UNSPEC_GOT))))
(set (match_dup 0)
(lo_sum:SI (match_dup 0)
(const:SI (unspec:SI [(match_dup 1)
(match_dup 3)] UNSPEC_GOT))))]
""
"
{
operands[3] = GEN_INT (INTVAL (operands[2]) + 1);
}")
 
;; Just like movdi_ldd, but with relaxation annotations.
(define_insn "tls_tlsdesc_ldd"
[(set (match_operand:DI 0 "register_operand" "=r")
(unspec:DI [(mem:DI (unspec:SI
[(match_operand:SI 1 "register_operand" "r")
(match_operand:SI 2 "register_operand" "r")
(match_operand:SI 3 "symbolic_operand" "")]
UNSPEC_TLS_TLSDESC_LDD_AUX))]
UNSPEC_TLS_TLSDESC_LDD))]
""
"ldd #tlsdesc(%a3)@(%1,%2), %0"
[(set_attr "length" "4")
(set_attr "type" "gload")])
 
(define_insn "tls_tlsoff_ld"
[(set (match_operand:SI 0 "register_operand" "=r")
(mem:SI (unspec:SI
[(match_operand:SI 1 "register_operand" "r")
(match_operand:SI 2 "register_operand" "r")
(match_operand:SI 3 "symbolic_operand" "")]
UNSPEC_TLS_TLSOFF_LD)))]
""
"ld #tlsoff(%a3)@(%1,%2), %0"
[(set_attr "length" "4")
(set_attr "type" "gload")])
 
(define_insn "tls_lddi"
[(set (match_operand:DI 0 "register_operand" "=r")
(unspec:DI [(match_operand:SI 1 "symbolic_operand" "")
(match_operand:SI 2 "register_operand" "d")]
UNSPEC_TLS_LDDI))]
""
"lddi @(%2, #gottlsdesc12(%a1)), %0"
[(set_attr "length" "4")
(set_attr "type" "gload")])
/cmovh.c
0,0 → 1,50
/* Move half-word library function.
Copyright (C) 2000, 2003 Free Software Foundation, Inc.
Contributed by Red Hat, Inc.
This file is part of GCC.
GCC 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 Software Foundation * either version 2, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
 
/* As a special exception, if you link this library with other files,
some of which are compiled with GCC, to produce an executable,
this library does not by itself cause the resulting executable
to be covered by the GNU General Public License.
This exception does not however invalidate any other reasons why
the executable file might be covered by the GNU General Public License. */
 
void
__cmovh (short *dest, const short *src, unsigned len)
{
unsigned i;
unsigned num = len >> 1;
char *dest_byte = (char *)dest;
const char *src_byte = (const char *)src;
 
if (dest_byte < src_byte || dest_byte > src_byte+len)
{
for (i = 0; i < num; i++)
dest[i] = src[i];
 
if ((len & 1) != 0)
dest_byte[len-1] = src_byte[len-1];
}
else
{
while (len-- > 0)
dest_byte[len] = src_byte[len];
}
}
/frv.opt
0,0 → 1,199
; Options for the FR-V port of the compiler.
 
; Copyright (C) 2005, 2007 Free Software Foundation, Inc.
;
; This file is part of GCC.
;
; GCC 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
; Software Foundation; either version 3, or (at your option) any later
; version.
;
; GCC is distributed in the hope that it will be useful, but WITHOUT ANY
; WARRANTY; without even the implied warranty of MERCHANTABILITY or
; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
; for more details.
;
; You should have received a copy of the GNU General Public License
; along with GCC; see the file COPYING3. If not see
; <http://www.gnu.org/licenses/>.
 
macc-4
Target Report RejectNegative Mask(ACC_4)
Use 4 media accumulators
 
macc-8
Target Report RejectNegative InverseMask(ACC_4, ACC_8)
Use 8 media accumulators
 
malign-labels
Target Report Mask(ALIGN_LABELS)
Enable label alignment optimizations
 
malloc-cc
Target Report RejectNegative Mask(ALLOC_CC)
Dynamically allocate cc registers
 
; We used to default the branch cost to 2, but it was changed it to 1 to avoid
; generating SCC instructions and or/and-ing them together, and then doing the
; branch on the result, which collectively generate much worse code.
mbranch-cost=
Target RejectNegative Joined UInteger Var(frv_branch_cost_int) Init(1)
Set the cost of branches
 
mcond-exec
Target Report Mask(COND_EXEC)
Enable conditional execution other than moves/scc
 
mcond-exec-insns=
Target RejectNegative Joined UInteger Var(frv_condexec_insns) Init(8)
Change the maximum length of conditionally-executed sequences
 
mcond-exec-temps=
Target RejectNegative Joined UInteger Var(frv_condexec_temps) Init(4)
Change the number of temporary registers that are available to conditionally-executed sequences
 
mcond-move
Target Report Mask(COND_MOVE)
Enable conditional moves
 
mcpu=
Target RejectNegative Joined
Set the target CPU type
 
mdebug
Target Undocumented Var(TARGET_DEBUG)
 
mdebug-arg
Target Undocumented Var(TARGET_DEBUG_ARG)
 
mdebug-addr
Target Undocumented Var(TARGET_DEBUG_ADDR)
 
mdebug-cond-exec
Target Undocumented Var(TARGET_DEBUG_COND_EXEC)
 
mdebug-loc
Target Undocumented Var(TARGET_DEBUG_LOC)
 
mdebug-stack
Target Undocumented Var(TARGET_DEBUG_STACK)
 
mdouble
Target Report Mask(DOUBLE)
Use fp double instructions
 
mdword
Target Report Mask(DWORD)
Change the ABI to allow double word insns
 
mfdpic
Target Report Mask(FDPIC)
Enable Function Descriptor PIC mode
 
mfixed-cc
Target Report RejectNegative InverseMask(ALLOC_CC, FIXED_CC)
Just use icc0/fcc0
 
mfpr-32
Target Report RejectNegative Mask(FPR_32)
Only use 32 FPRs
 
mfpr-64
Target Report RejectNegative InverseMask(FPR_32, FPR_64)
Use 64 FPRs
 
mgpr-32
Target Report RejectNegative Mask(GPR_32)
Only use 32 GPRs
 
mgpr-64
Target Report RejectNegative InverseMask(GPR_32, GPR_64)
Use 64 GPRs
 
mgprel-ro
Target Report Mask(GPREL_RO)
Enable use of GPREL for read-only data in FDPIC
 
mhard-float
Target Report RejectNegative InverseMask(SOFT_FLOAT, HARD_FLOAT)
Use hardware floating point
 
minline-plt
Target Report Mask(INLINE_PLT)
Enable inlining of PLT in function calls
 
mlibrary-pic
Target Report Mask(LIBPIC)
Enable PIC support for building libraries
 
mlinked-fp
Target Report Mask(LINKED_FP)
Follow the EABI linkage requirements
 
mlong-calls
Target Report Mask(LONG_CALLS)
Disallow direct calls to global functions
 
mmedia
Target Report Mask(MEDIA)
Use media instructions
 
mmuladd
Target Report Mask(MULADD)
Use multiply add/subtract instructions
 
mmulti-cond-exec
Target Report Mask(MULTI_CE)
Enable optimizing &&/|| in conditional execution
 
mnested-cond-exec
Target Report Mask(NESTED_CE)
Enable nested conditional execution optimizations
 
; Not used by the compiler proper.
mno-eflags
Target RejectNegative
Do not mark ABI switches in e_flags
 
moptimize-membar
Target Report Mask(OPTIMIZE_MEMBAR)
Remove redundant membars
 
mpack
Target Report Mask(PACK)
Pack VLIW instructions
 
mscc
Target Report Mask(SCC)
Enable setting GPRs to the result of comparisons
 
msched-lookahead=
Target RejectNegative Joined UInteger Var(frv_sched_lookahead) Init(4)
Change the amount of scheduler lookahead
 
msoft-float
Target Report RejectNegative Mask(SOFT_FLOAT)
Use software floating point
 
mTLS
Target Report RejectNegative Mask(BIG_TLS)
Assume a large TLS segment
 
mtls
Target Report RejectNegative InverseMask(BIG_TLS)
Do not assume a large TLS segment
 
; Not used by the compiler proper.
mtomcat-stats
Target
Cause gas to print tomcat statistics
 
; Not used by the compiler proper.
multilib-library-pic
Target RejectNegative
Link with the library-pic libraries
 
mvliw-branch
Target Report Mask(VLIW_BRANCH)
Allow branches to be packed with other instructions
/t-frv
0,0 → 1,95
# Name of assembly file containing libgcc1 functions.
# This entry must be present, but it can be empty if the target does
# not need any assembler functions to support its code generation.
#
# Alternatively if assembler functions *are* needed then define the
# entries below:
CROSS_LIBGCC1 = libgcc1-asm.a
LIB1ASMSRC = frv/lib1funcs.asm
LIB1ASMFUNCS = _cmpll _cmpf _cmpd _addll _subll _andll _orll _xorll _notll _cmov
LIB2FUNCS_EXTRA = cmovh.c cmovw.c cmovd.c modi.c umodi.c uitof.c uitod.c ulltof.c ulltod.c
 
# We want fine grained libraries, so use the new code to build the
# floating point emulation libraries.
FPBIT = fp-bit.c
DPBIT = dp-bit.c
 
# If any special flags are necessary when building libgcc2 put them here.
TARGET_LIBGCC2_CFLAGS =
 
fp-bit.c: $(srcdir)/config/fp-bit.c
echo '#define FLOAT' > fp-bit.c
echo '#include "config/frv/frv-abi.h"' >> fp-bit.c
cat $(srcdir)/config/fp-bit.c >> fp-bit.c
 
dp-bit.c: $(srcdir)/config/fp-bit.c
echo '#include "config/frv/frv-abi.h"' > dp-bit.c
cat $(srcdir)/config/fp-bit.c >> dp-bit.c
 
cmovh.c: $(srcdir)/config/frv/cmovh.c
$(LN_S) $(srcdir)/config/frv/cmovh.c .
 
cmovw.c: $(srcdir)/config/frv/cmovw.c
$(LN_S) $(srcdir)/config/frv/cmovw.c .
 
cmovd.c: $(srcdir)/config/frv/cmovd.c
$(LN_S) $(srcdir)/config/frv/cmovd.c .
 
modi.c: $(srcdir)/config/frv/modi.c
$(LN_S) $(srcdir)/config/frv/modi.c .
 
umodi.c: $(srcdir)/config/frv/umodi.c
$(LN_S) $(srcdir)/config/frv/umodi.c .
 
uitof.c: $(srcdir)/config/frv/uitof.c
$(LN_S) $(srcdir)/config/frv/uitof.c .
 
uitod.c: $(srcdir)/config/frv/uitod.c
$(LN_S) $(srcdir)/config/frv/uitod.c .
 
ulltof.c: $(srcdir)/config/frv/ulltof.c
$(LN_S) $(srcdir)/config/frv/ulltof.c .
 
ulltod.c: $(srcdir)/config/frv/ulltod.c
$(LN_S) $(srcdir)/config/frv/ulltod.c .
 
# Build frvbegin.o and frvend.o
EXTRA_MULTILIB_PARTS=frvbegin.o frvend.o
 
# Compile two additional files that are linked with every program
# linked using GCC on systems using COFF or ELF, for the sake of C++
# constructors.
 
FRVSTUFF_CFLAGS = $(TARGET_LIBGCC2_CFLAGS)
 
$(T)frvbegin$(objext): $(srcdir)/config/frv/frvbegin.c $(GCC_PASSES) \
$(CONFIG_H) defaults.h unwind-dw2-fde.h gbl-ctors.h
$(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) $(FRVSTUFF_CFLAGS) \
-c $(srcdir)/config/frv/frvbegin.c -o $(T)frvbegin$(objext)
 
$(T)frvend$(objext): $(srcdir)/config/frv/frvend.c $(GCC_PASSES) \
$(CONFIG_H) defaults.h unwind-dw2-fde.h gbl-ctors.h
$(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) $(FRVSTUFF_CFLAGS) \
-c $(srcdir)/config/frv/frvend.c -o $(T)frvend$(objext)
 
# Enable the following if multilibs are needed.
# See gcc/genmultilib, gcc/gcc.texi and gcc/tm.texi for a
# description of the options and their values.
#
#MULTILIB_OPTIONS = mcpu=fr500/mcpu=tomcat/mcpu=simple/mcpu=frv msoft-float mdword/mno-dword
#MULTILIB_DIRNAMES = fr500 tomcat simple frv nof dw no-dw
#MULTILIB_MATCHES = mcpu?simple=mcpu?fr300 mno-double=mcpu?fr500 mcpu?frv=mdouble
#MULTILIB_EXCEPTIONS = *mcpu=simple/*msoft-float* *mcpu=frv/*msoft-float*
#MULTILIB_EXTRA_OPTS = mlibrary-pic
 
MULTILIB_OPTIONS = mcpu=fr400/mcpu=fr550 mno-pack mlibrary-pic/mfdpic
MULTILIB_DIRNAMES = fr400 fr550 unpacked pic fdpic
MULTILIB_MATCHES = mcpu?simple=mcpu?fr300 \
mlibrary-pic=multilib-library-pic \
mcpu?fr400=mcpu?fr405 mcpu?fr400=mcpu?fr450
MULTILIB_EXCEPTIONS = mcpu=frv/mno-pack* mcpu=simple/mno-pack*
 
LIBGCC = stmp-multilib
INSTALL_LIBGCC = install-multilib
 
EXTRA_HEADERS = $(srcdir)/config/frv/frv-asm.h
/umodi.c
0,0 → 1,4
unsigned int __umodi (unsigned int a, unsigned int b)
{
return a % b;
}
/frv-modes.def
0,0 → 1,34
/* Definitions of target machine for GNU compiler for FRV.
Copyright (C) 2002, 2004, 2007 Free Software Foundation, Inc.
 
This file is part of GCC.
 
GCC 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 Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
/* On the FRV, the CC modes used are:
 
CCmode set ICCs from comparing signed integers
CC_UNSmode set ICCs from comparing unsigned integers
CC_NZmode set ICCs for comparisons that just need the Z and N flags
CC_FPmode set FCCs from comparing floating point
CC_CCRmode set CCRs to do conditional execution */
 
CC_MODE (CC_UNS);
CC_MODE (CC_NZ);
CC_MODE (CC_FP);
CC_MODE (CC_CCR);
 
VECTOR_MODE (INT, QI, 4); /* V4QI */
VECTOR_MODE (INT, SI, 4); /* V4SI */
/t-linux
0,0 → 1,12
# We don't want multilibs.
MULTILIB_OPTIONS=
MULTILIB_DIRNAMES=
MULTILIB_MATCHES=
MULTILIB_EXCEPTIONS=
MULTILIB_EXTRA_OPTS=
 
CRTSTUFF_T_CFLAGS = -fPIC
TARGET_LIBGCC2_CFLAGS = -fPIC
 
SHLIB_MAPFILES = $(srcdir)/libgcc-std.ver \
$(srcdir)/config/frv/libgcc-frv.ver
/frv-asm.h
0,0 → 1,48
/* Assembler Support.
Copyright (C) 2000, 2007 Free Software Foundation, Inc.
Contributed by Red Hat, Inc.
 
This file is part of GCC.
 
GCC 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 Software Foundation * either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
/* P(INSN): Emit INSN.P for VLIW machines, otherwise emit plain INSN.
P2(INSN): Emit INSN.P on the FR500 and above, otherwise emit plain INSN. */
#ifdef __FRV_VLIW__
#ifdef __STDC__
#define P(A) A.p
#else
#define P(A) A/**/.p
#endif
#if __FRV_VLIW__ > 2
#define P2(A) P(A)
#else
#define P2(A) A
#endif
#else
#define P(A) A
#define P2(A) A
#endif
 
/* Add underscore if necessary to external name. */
#ifdef __FRV_UNDERSCORE__
#ifdef __STDC__
#define EXT(NAME) _##NAME
#else
#define EXT(NAME) _/**/NAME
#endif
#else
#define EXT(NAME) NAME
#endif
/frvend.c
0,0 → 1,73
/* Frv initialization file linked after all user modules
Copyright (C) 1999, 2000, 2003, 2004 Free Software Foundation, Inc.
Contributed by Red Hat, Inc.
This file is part of GCC.
GCC 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 Software Foundation * either version 2, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
 
/* As a special exception, if you link this library with other files,
some of which are compiled with GCC, to produce an executable,
this library does not by itself cause the resulting executable
to be covered by the GNU General Public License.
This exception does not however invalidate any other reasons why
the executable file might be covered by the GNU General Public License. */
 
#include "defaults.h"
#include <stddef.h>
#include "unwind-dw2-fde.h"
 
#ifdef __FRV_UNDERSCORE__
#define UNDERSCORE "_"
#else
#define UNDERSCORE ""
#endif
 
#define FINI_SECTION_ZERO(SECTION, FLAGS, NAME) \
__asm__ (".section " SECTION "," FLAGS "\n\t" \
".globl " UNDERSCORE NAME "\n\t" \
".type " UNDERSCORE NAME ",@object\n\t" \
".p2align 2\n" \
UNDERSCORE NAME ":\n\t" \
".word 0\n\t" \
".previous")
 
#define FINI_SECTION(SECTION, FLAGS, NAME) \
__asm__ (".section " SECTION "," FLAGS "\n\t" \
".globl " UNDERSCORE NAME "\n\t" \
".type " UNDERSCORE NAME ",@object\n\t" \
".p2align 2\n" \
UNDERSCORE NAME ":\n\t" \
".previous")
 
/* End of .ctor/.dtor sections that provides a list of constructors and
destructors to run. */
 
FINI_SECTION_ZERO (".ctors", "\"aw\"", "__CTOR_END__");
FINI_SECTION_ZERO (".dtors", "\"aw\"", "__DTOR_END__");
 
/* End of .eh_frame section that provides all of the exception handling
tables. */
 
FINI_SECTION_ZERO (".eh_frame", "\"aw\"", "__FRAME_END__");
 
#if ! __FRV_FDPIC__
/* In FDPIC, the linker itself generates this. */
/* End of .rofixup section that provides a list of pointers that we
need to adjust. */
 
FINI_SECTION (".rofixup", "\"a\"", "__ROFIXUP_END__");
#endif /* __FRV_FDPIC__ */
/lib1funcs.asm
0,0 → 1,282
/* Library functions.
Copyright (C) 2000, 2003 Free Software Foundation, Inc.
Contributed by Red Hat, Inc.
This file is part of GCC.
GCC 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 Software Foundation * either version 2, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
 
/* As a special exception, if you link this library with other files,
some of which are compiled with GCC, to produce an executable,
this library does not by itself cause the resulting executable
to be covered by the GNU General Public License.
This exception does not however invalidate any other reasons why
the executable file might be covered by the GNU General Public License. */
 
#include <frv-asm.h>
 
#ifdef L_cmpll
/* icc0 = __cmpll (long long a, long long b) */
 
.file "_cmpll.s"
.globl EXT(__cmpll)
.type EXT(__cmpll),@function
.text
.p2align 4
EXT(__cmpll):
cmp gr8, gr10, icc0
ckeq icc0, cc4
P(ccmp) gr9, gr11, cc4, 1
ret
.Lend:
.size EXT(__cmpll),.Lend-EXT(__cmpll)
#endif /* L_cmpll */
#ifdef L_cmpf
/* icc0 = __cmpf (float a, float b) */
/* Note, because this function returns the result in ICC0, it means it can't
handle NaNs. */
 
.file "_cmpf.s"
.globl EXT(__cmpf)
.type EXT(__cmpf),@function
.text
.p2align 4
EXT(__cmpf):
#ifdef __FRV_HARD_FLOAT__ /* floating point instructions available */
movgf gr8, fr0
P(movgf) gr9, fr1
setlos #1, gr8
fcmps fr0, fr1, fcc0
P(fcklt) fcc0, cc0
fckeq fcc0, cc1
csub gr0, gr8, gr8, cc0, 1
cmov gr0, gr8, cc1, 1
cmpi gr8, 0, icc0
ret
#else /* no floating point instructions available */
movsg lr, gr4
addi sp, #-16, sp
sti gr4, @(sp, 8)
st fp, @(sp, gr0)
mov sp, fp
call EXT(__cmpsf2)
cmpi gr8, #0, icc0
ldi @(sp, 8), gr4
movgs gr4, lr
ld @(sp,gr0), fp
addi sp, #16, sp
ret
#endif
.Lend:
.size EXT(__cmpf),.Lend-EXT(__cmpf)
#endif
#ifdef L_cmpd
/* icc0 = __cmpd (double a, double b) */
/* Note, because this function returns the result in ICC0, it means it can't
handle NaNs. */
 
.file "_cmpd.s"
.globl EXT(__cmpd)
.type EXT(__cmpd),@function
.text
.p2align 4
EXT(__cmpd):
movsg lr, gr4
addi sp, #-16, sp
sti gr4, @(sp, 8)
st fp, @(sp, gr0)
mov sp, fp
call EXT(__cmpdf2)
cmpi gr8, #0, icc0
ldi @(sp, 8), gr4
movgs gr4, lr
ld @(sp,gr0), fp
addi sp, #16, sp
ret
.Lend:
.size EXT(__cmpd),.Lend-EXT(__cmpd)
#endif
#ifdef L_addll
/* gr8,gr9 = __addll (long long a, long long b) */
/* Note, gcc will never call this function, but it is present in case an
ABI program calls it. */
 
.file "_addll.s"
.globl EXT(__addll)
.type EXT(__addll),@function
.text
.p2align
EXT(__addll):
addcc gr9, gr11, gr9, icc0
addx gr8, gr10, gr8, icc0
ret
.Lend:
.size EXT(__addll),.Lend-EXT(__addll)
#endif
#ifdef L_subll
/* gr8,gr9 = __subll (long long a, long long b) */
/* Note, gcc will never call this function, but it is present in case an
ABI program calls it. */
 
.file "_subll.s"
.globl EXT(__subll)
.type EXT(__subll),@function
.text
.p2align 4
EXT(__subll):
subcc gr9, gr11, gr9, icc0
subx gr8, gr10, gr8, icc0
ret
.Lend:
.size EXT(__subll),.Lend-EXT(__subll)
#endif
#ifdef L_andll
/* gr8,gr9 = __andll (long long a, long long b) */
/* Note, gcc will never call this function, but it is present in case an
ABI program calls it. */
 
.file "_andll.s"
.globl EXT(__andll)
.type EXT(__andll),@function
.text
.p2align 4
EXT(__andll):
P(and) gr9, gr11, gr9
P2(and) gr8, gr10, gr8
ret
.Lend:
.size EXT(__andll),.Lend-EXT(__andll)
#endif
#ifdef L_orll
/* gr8,gr9 = __orll (long long a, long long b) */
/* Note, gcc will never call this function, but it is present in case an
ABI program calls it. */
 
.file "_orll.s"
.globl EXT(__orll)
.type EXT(__orll),@function
.text
.p2align 4
EXT(__orll):
P(or) gr9, gr11, gr9
P2(or) gr8, gr10, gr8
ret
.Lend:
.size EXT(__orll),.Lend-EXT(__orll)
#endif
#ifdef L_xorll
/* gr8,gr9 = __xorll (long long a, long long b) */
/* Note, gcc will never call this function, but it is present in case an
ABI program calls it. */
 
.file "_xorll.s"
.globl EXT(__xorll)
.type EXT(__xorll),@function
.text
.p2align 4
EXT(__xorll):
P(xor) gr9, gr11, gr9
P2(xor) gr8, gr10, gr8
ret
.Lend:
.size EXT(__xorll),.Lend-EXT(__xorll)
#endif
#ifdef L_notll
/* gr8,gr9 = __notll (long long a) */
/* Note, gcc will never call this function, but it is present in case an
ABI program calls it. */
 
.file "_notll.s"
.globl EXT(__notll)
.type EXT(__notll),@function
.text
.p2align 4
EXT(__notll):
P(not) gr9, gr9
P2(not) gr8, gr8
ret
.Lend:
.size EXT(__notll),.Lend-EXT(__notll)
#endif
#ifdef L_cmov
/* (void) __cmov (char *dest, const char *src, size_t len) */
/*
* void __cmov (char *dest, const char *src, size_t len)
* {
* size_t i;
*
* if (dest < src || dest > src+len)
* {
* for (i = 0; i < len; i++)
* dest[i] = src[i];
* }
* else
* {
* while (len-- > 0)
* dest[len] = src[len];
* }
* }
*/
 
.file "_cmov.s"
.globl EXT(__cmov)
.type EXT(__cmov),@function
.text
.p2align 4
EXT(__cmov):
P(cmp) gr8, gr9, icc0
add gr9, gr10, gr4
P(cmp) gr8, gr4, icc1
bc icc0, 0, .Lfwd
bls icc1, 0, .Lback
.Lfwd:
/* move bytes in a forward direction */
P(setlos) #0, gr5
cmp gr0, gr10, icc0
P(subi) gr9, #1, gr9
P2(subi) gr8, #1, gr8
bnc icc0, 0, .Lret
.Lfloop:
/* forward byte move loop */
addi gr5, #1, gr5
P(ldsb) @(gr9, gr5), gr4
cmp gr5, gr10, icc0
P(stb) gr4, @(gr8, gr5)
bc icc0, 0, .Lfloop
ret
.Lbloop:
/* backward byte move loop body */
ldsb @(gr9,gr10),gr4
stb gr4,@(gr8,gr10)
.Lback:
P(cmpi) gr10, #0, icc0
addi gr10, #-1, gr10
bne icc0, 0, .Lbloop
.Lret:
ret
.Lend:
.size EXT(__cmov),.Lend-EXT(__cmov)
#endif
/uitod.c
0,0 → 1,4
double __uitod (unsigned int a)
{
return a;
}
/uitof.c
0,0 → 1,4
float __uitof (unsigned int a)
{
return a;
}
/frv-protos.h
0,0 → 1,215
/* Frv prototypes.
Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2007 Free Software Foundation,
Inc.
Contributed by Red Hat, Inc.
 
This file is part of GCC.
 
GCC 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 Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
/* CPU type. This must be identical to the cpu enumeration in frv.md. */
typedef enum frv_cpu
{
FRV_CPU_GENERIC,
FRV_CPU_FR550,
FRV_CPU_FR500,
FRV_CPU_FR450,
FRV_CPU_FR405,
FRV_CPU_FR400,
FRV_CPU_FR300,
FRV_CPU_SIMPLE,
FRV_CPU_TOMCAT
} frv_cpu_t;
 
extern frv_cpu_t frv_cpu_type; /* value of -mcpu= */
 
/* Define functions defined in frv.c */
extern void frv_expand_prologue (void);
extern void frv_expand_epilogue (bool);
extern void frv_override_options (void);
extern void frv_optimization_options (int, int);
extern void frv_conditional_register_usage (void);
extern frv_stack_t *frv_stack_info (void);
extern void frv_debug_stack (frv_stack_t *);
extern int frv_frame_pointer_required (void);
extern int frv_initial_elimination_offset (int, int);
 
#ifdef RTX_CODE
extern int frv_legitimate_address_p (enum machine_mode, rtx,
int, int, int);
extern rtx frv_legitimize_address (rtx, rtx, enum machine_mode);
extern rtx frv_find_base_term (rtx);
 
#ifdef TREE_CODE
extern void frv_init_cumulative_args (CUMULATIVE_ARGS *, tree,
rtx, tree, int);
 
extern int frv_function_arg_boundary (enum machine_mode, tree);
extern rtx frv_function_arg (CUMULATIVE_ARGS *,
enum machine_mode,
tree, int, int);
 
extern void frv_function_arg_advance (CUMULATIVE_ARGS *,
enum machine_mode,
tree, int);
 
extern void frv_expand_builtin_va_start (tree, rtx);
#endif /* TREE_CODE */
 
extern int frv_expand_block_move (rtx *);
extern int frv_expand_block_clear (rtx *);
extern rtx frv_dynamic_chain_address (rtx);
extern rtx frv_return_addr_rtx (int, rtx);
extern rtx frv_index_memory (rtx, enum machine_mode, int);
extern const char *frv_asm_output_opcode
(FILE *, const char *);
extern void frv_final_prescan_insn (rtx, rtx *, int);
extern void frv_print_operand (FILE *, rtx, int);
extern void frv_print_operand_address (FILE *, rtx);
extern void frv_emit_move (enum machine_mode, rtx, rtx);
extern int frv_emit_movsi (rtx, rtx);
extern const char *output_move_single (rtx *, rtx);
extern const char *output_move_double (rtx *, rtx);
extern const char *output_condmove_single
(rtx *, rtx);
extern int frv_emit_cond_branch (enum rtx_code, rtx);
extern int frv_emit_scc (enum rtx_code, rtx);
extern rtx frv_split_scc (rtx, rtx, rtx, rtx, HOST_WIDE_INT);
extern int frv_emit_cond_move (rtx, rtx, rtx, rtx);
extern rtx frv_split_cond_move (rtx *);
extern rtx frv_split_minmax (rtx *);
extern rtx frv_split_abs (rtx *);
extern void frv_split_double_load (rtx, rtx);
extern void frv_split_double_store (rtx, rtx);
#ifdef BB_HEAD
extern void frv_ifcvt_init_extra_fields (ce_if_block_t *);
extern void frv_ifcvt_modify_tests (ce_if_block_t *, rtx *, rtx *);
extern void frv_ifcvt_modify_multiple_tests
(ce_if_block_t *, basic_block,
rtx *, rtx *);
extern rtx frv_ifcvt_modify_insn (ce_if_block_t *, rtx, rtx);
extern void frv_ifcvt_modify_final (ce_if_block_t *);
extern void frv_ifcvt_modify_cancel (ce_if_block_t *);
#endif
extern int frv_trampoline_size (void);
extern void frv_initialize_trampoline (rtx, rtx, rtx);
extern enum reg_class frv_secondary_reload_class
(enum reg_class class,
enum machine_mode mode,
rtx x, int);
extern int frv_class_likely_spilled_p (enum reg_class class);
extern int frv_hard_regno_mode_ok (int, enum machine_mode);
extern int frv_hard_regno_nregs (int, enum machine_mode);
extern int frv_class_max_nregs (enum reg_class class,
enum machine_mode mode);
extern int frv_legitimate_constant_p (rtx);
extern enum machine_mode frv_select_cc_mode (enum rtx_code, rtx, rtx);
#endif /* RTX_CODE */
 
extern int direct_return_p (void);
extern int frv_register_move_cost (enum reg_class, enum reg_class);
extern int frv_issue_rate (void);
extern int frv_acc_group (rtx);
 
#ifdef TREE_CODE
extern int frv_adjust_field_align (tree, int);
#endif
 
#ifdef RTX_CODE
extern int integer_register_operand (rtx, enum machine_mode);
extern int frv_load_operand (rtx, enum machine_mode);
extern int gpr_or_fpr_operand (rtx, enum machine_mode);
extern int gpr_no_subreg_operand (rtx, enum machine_mode);
extern int gpr_or_int6_operand (rtx, enum machine_mode);
extern int fpr_or_int6_operand (rtx, enum machine_mode);
extern int gpr_or_int_operand (rtx, enum machine_mode);
extern int gpr_or_int12_operand (rtx, enum machine_mode);
extern int gpr_fpr_or_int12_operand (rtx, enum machine_mode);
extern int gpr_or_int10_operand (rtx, enum machine_mode);
extern int move_source_operand (rtx, enum machine_mode);
extern int move_destination_operand (rtx, enum machine_mode);
extern int condexec_source_operand (rtx, enum machine_mode);
extern int condexec_dest_operand (rtx, enum machine_mode);
extern int lr_operand (rtx, enum machine_mode);
extern int gpr_or_memory_operand (rtx, enum machine_mode);
extern int fpr_or_memory_operand (rtx, enum machine_mode);
extern int reg_or_0_operand (rtx, enum machine_mode);
extern int fcc_operand (rtx, enum machine_mode);
extern int icc_operand (rtx, enum machine_mode);
extern int cc_operand (rtx, enum machine_mode);
extern int fcr_operand (rtx, enum machine_mode);
extern int icr_operand (rtx, enum machine_mode);
extern int cr_operand (rtx, enum machine_mode);
extern int call_operand (rtx, enum machine_mode);
extern int fpr_operand (rtx, enum machine_mode);
extern int even_reg_operand (rtx, enum machine_mode);
extern int odd_reg_operand (rtx, enum machine_mode);
extern int even_gpr_operand (rtx, enum machine_mode);
extern int odd_gpr_operand (rtx, enum machine_mode);
extern int quad_fpr_operand (rtx, enum machine_mode);
extern int even_fpr_operand (rtx, enum machine_mode);
extern int odd_fpr_operand (rtx, enum machine_mode);
extern int dbl_memory_one_insn_operand (rtx, enum machine_mode);
extern int dbl_memory_two_insn_operand (rtx, enum machine_mode);
extern int int12_operand (rtx, enum machine_mode);
extern int int6_operand (rtx, enum machine_mode);
extern int int5_operand (rtx, enum machine_mode);
extern int uint5_operand (rtx, enum machine_mode);
extern int uint4_operand (rtx, enum machine_mode);
extern int uint1_operand (rtx, enum machine_mode);
extern int int_2word_operand (rtx, enum machine_mode);
extern int pic_register_operand (rtx, enum machine_mode);
extern int pic_symbolic_operand (rtx, enum machine_mode);
extern int small_data_register_operand (rtx, enum machine_mode);
extern int small_data_symbolic_operand (rtx, enum machine_mode);
extern int upper_int16_operand (rtx, enum machine_mode);
extern int uint16_operand (rtx, enum machine_mode);
extern int symbolic_operand (rtx, enum machine_mode);
extern int relational_operator (rtx, enum machine_mode);
extern int signed_relational_operator (rtx, enum machine_mode);
extern int unsigned_relational_operator (rtx, enum machine_mode);
extern int float_relational_operator (rtx, enum machine_mode);
extern int ccr_eqne_operator (rtx, enum machine_mode);
extern int minmax_operator (rtx, enum machine_mode);
extern int condexec_si_binary_operator (rtx, enum machine_mode);
extern int condexec_si_media_operator (rtx, enum machine_mode);
extern int condexec_si_divide_operator (rtx, enum machine_mode);
extern int condexec_si_unary_operator (rtx, enum machine_mode);
extern int condexec_sf_conv_operator (rtx, enum machine_mode);
extern int condexec_sf_add_operator (rtx, enum machine_mode);
extern int condexec_memory_operand (rtx, enum machine_mode);
extern int intop_compare_operator (rtx, enum machine_mode);
extern int acc_operand (rtx, enum machine_mode);
extern int even_acc_operand (rtx, enum machine_mode);
extern int quad_acc_operand (rtx, enum machine_mode);
extern int accg_operand (rtx, enum machine_mode);
extern rtx frv_matching_accg_for_acc (rtx);
extern void frv_expand_fdpic_call (rtx *, bool, bool);
extern rtx frv_gen_GPsym2reg (rtx, rtx);
extern int frv_legitimate_memory_operand (rtx, enum machine_mode, int);
 
/* Information about a relocation unspec. SYMBOL is the relocation symbol
(a SYMBOL_REF or LABEL_REF), RELOC is the type of relocation and OFFSET
is the constant addend. */
struct frv_unspec {
rtx symbol;
int reloc;
HOST_WIDE_INT offset;
};
 
extern bool frv_const_unspec_p (rtx, struct frv_unspec *);
 
#endif
 
/modi.c
0,0 → 1,4
int __modi (int a, int b)
{
return a % b;
}
/libgcc-frv.ver
0,0 → 1,55
GCC_3.4 {
# frv abi symbol names
__ftod
__ftoi
__ftoui
__dtoi
__ftoui
__dtoui
__ftoll
__dtoll
__ftoull
__dtoull
__itof
__lltof
__dtof
__itod
__lltof
__lltod
__addd
__subd
__muld
__divd
__addf
__subf
__mulf
__divf
__sllll
__srlll
__srall
__addll
__subll
__mulll
__umulll
__divll
__udivll
__modll
__umodll
__cmpll
__cmpf
__cmpd
__andll
__orll
__xorll
__notll
__cmov
__cmovd
__cmovh
__cmovw
__modi
__uitod
__uitof
__ulltod
__ulltof
__umodi
}
/cmovw.c
0,0 → 1,54
/* Move word library function.
Copyright (C) 2000, 2003 Free Software Foundation, Inc.
Contributed by Red Hat, Inc.
This file is part of GCC.
GCC 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 Software Foundation * either version 2, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
 
/* As a special exception, if you link this library with other files,
some of which are compiled with GCC, to produce an executable,
this library does not by itself cause the resulting executable
to be covered by the GNU General Public License.
This exception does not however invalidate any other reasons why
the executable file might be covered by the GNU General Public License. */
 
void
__cmovw (int *dest, const int *src, unsigned len)
{
unsigned i;
unsigned num = len >> 2;
unsigned xlen = len & ~3;
char *dest_byte = (char *)dest;
const char *src_byte = (const char *)src;
 
if (dest_byte < src_byte || dest_byte > src_byte+len)
{
for (i = 0; i < num; i++)
dest[i] = src[i];
 
while (len > xlen)
{
dest_byte[xlen] = src_byte[xlen];
xlen++;
}
}
else
{
while (len-- > 0)
dest_byte[len] = src_byte[len];
}
}
/frv-abi.h
0,0 → 1,181
/* Frv map GCC names to FR-V ABI.
Copyright (C) 2000, 2003, 2004, 2007 Free Software Foundation, Inc.
Contributed by Red Hat, Inc.
 
This file is part of GCC.
 
GCC 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 Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
/* For each of the functions in the library that has a corresponding name in
the ABI, add an equivalence between the GCC name and the ABI name. This is
in a separate file from frv.h so that fp-bit.c can be made to include it. */
 
#ifdef __GNUC__
#ifdef __FRV_UNDERSCORE__
#define RENAME_LIBRARY(OLD,NEW) \
__asm__ (".globl\t_" #NEW "\n" \
"_" #NEW "=_" #OLD "\n" \
"\t.type\t_" #NEW ",@function\n");
 
#else
#define RENAME_LIBRARY(OLD,NEW) \
__asm__ (".globl\t" #NEW "\n" \
#NEW "=" #OLD "\n" \
"\t.type\t" #NEW ",@function\n");
#endif
 
#define CREATE_DOUBLE_SHIFT(OLD,NEW) \
__asm__ (".text\n" \
"\t.globl\t" #NEW "\n" \
"\t.type\t" #NEW ",@function\n" \
#NEW ":\n" \
"\tor\tgr11, gr0, gr10\n" \
".L" #OLD " = " #OLD "\n" \
"\tbra\t.L" #OLD "\n");
 
#ifdef L_sf_to_df
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__extendsfdf2,__ftod)
#endif
 
#ifdef L_sf_to_si
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__fixsfsi,__ftoi)
#endif
 
#ifdef L_sf_to_usi
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__fixunssfsi,__ftoui)
#endif
 
#ifdef L_df_to_si
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__fixdfsi,__dtoi)
#endif
 
#ifdef L_fixunssfsi
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__fixunssfsi,__ftoui)
#endif
 
#ifdef L_fixunsdfsi
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__fixunsdfsi,__dtoui)
#endif
 
#ifdef L_fixsfdi
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__fixsfdi,__ftoll)
#endif
 
#ifdef L_fixdfdi
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__fixdfdi,__dtoll)
#endif
 
#ifdef L_fixunssfdi
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__fixunssfdi,__ftoull)
#endif
 
#ifdef L_fixunsdfdi
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__fixunsdfdi,__dtoull)
#endif
 
#ifdef L_si_to_sf
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__floatsisf,__itof)
#endif
 
#ifdef L_di_to_sf
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__floatdisf,__lltof)
#endif
 
#ifdef L_df_to_sf
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__truncdfsf2,__dtof)
#endif
 
#ifdef L_si_to_df
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__floatsidf,__itod)
#endif
 
#ifdef L_floatdisf
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__floatdisf,__lltof)
#endif
 
#ifdef L_floatdidf
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__floatdidf,__lltod)
#endif
 
#ifdef L_addsub_df
#define DECLARE_LIBRARY_RENAMES \
RENAME_LIBRARY(__adddf3,__addd)
RENAME_LIBRARY(__subdf3,__subd)
#endif
 
#ifdef L_mul_df
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__muldf3,__muld)
#endif
 
#ifdef L_div_df
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__divdf3,__divd)
#endif
 
#ifdef L_addsub_sf
#define DECLARE_LIBRARY_RENAMES \
RENAME_LIBRARY(__addsf3,__addf) \
RENAME_LIBRARY(__subsf3,__subf)
#endif
 
#ifdef L_mul_sf
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__mulsf3,__mulf)
#endif
 
#ifdef L_div_sf
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__divsf3,__divf)
#endif
 
#ifdef L_ashldi3
#define DECLARE_LIBRARY_RENAMES CREATE_DOUBLE_SHIFT (__ashldi3,__sllll)
#endif
 
#ifdef L_lshrdi3
#define DECLARE_LIBRARY_RENAMES CREATE_DOUBLE_SHIFT (__lshrdi3,__srlll)
#endif
 
#ifdef L_ashrdi3
#define DECLARE_LIBRARY_RENAMES CREATE_DOUBLE_SHIFT (__ashrdi3,__srall)
#endif
 
#ifdef L_adddi3
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__adddi3,__addll)
#endif
 
#ifdef L_subdi3
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__subdi3,__subll)
#endif
 
#ifdef L_muldi3
#define DECLARE_LIBRARY_RENAMES \
RENAME_LIBRARY(__muldi3,__mulll)
RENAME_LIBRARY(__muldi3,__umulll)
#endif
 
#ifdef L_divdi3
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__divdi3,__divll)
#endif
 
#ifdef L_udivdi3
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__udivdi3,__udivll)
#endif
 
#ifdef L_moddi3
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__moddi3,__modll)
#endif
 
#ifdef L_umoddi3
#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY(__umoddi3,__umodll)
#endif
#endif /* __GNUC__ */
/frv.c
0,0 → 1,9596
/* Copyright (C) 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2007
Free Software Foundation, Inc.
Contributed by Red Hat, Inc.
 
This file is part of GCC.
 
GCC 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 Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "tree.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "insn-flags.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
#include "recog.h"
#include "reload.h"
#include "expr.h"
#include "obstack.h"
#include "except.h"
#include "function.h"
#include "optabs.h"
#include "toplev.h"
#include "basic-block.h"
#include "tm_p.h"
#include "ggc.h"
#include <ctype.h>
#include "target.h"
#include "target-def.h"
#include "targhooks.h"
#include "integrate.h"
#include "langhooks.h"
 
#ifndef FRV_INLINE
#define FRV_INLINE inline
#endif
 
/* The maximum number of distinct NOP patterns. There are three:
nop, fnop and mnop. */
#define NUM_NOP_PATTERNS 3
 
/* Classification of instructions and units: integer, floating-point/media,
branch and control. */
enum frv_insn_group { GROUP_I, GROUP_FM, GROUP_B, GROUP_C, NUM_GROUPS };
 
/* The DFA names of the units, in packet order. */
static const char *const frv_unit_names[] =
{
"c",
"i0", "f0",
"i1", "f1",
"i2", "f2",
"i3", "f3",
"b0", "b1"
};
 
/* The classification of each unit in frv_unit_names[]. */
static const enum frv_insn_group frv_unit_groups[ARRAY_SIZE (frv_unit_names)] =
{
GROUP_C,
GROUP_I, GROUP_FM,
GROUP_I, GROUP_FM,
GROUP_I, GROUP_FM,
GROUP_I, GROUP_FM,
GROUP_B, GROUP_B
};
 
/* Return the DFA unit code associated with the Nth unit of integer
or floating-point group GROUP, */
#define NTH_UNIT(GROUP, N) frv_unit_codes[(GROUP) + (N) * 2 + 1]
 
/* Return the number of integer or floating-point unit UNIT
(1 for I1, 2 for F2, etc.). */
#define UNIT_NUMBER(UNIT) (((UNIT) - 1) / 2)
 
/* The DFA unit number for each unit in frv_unit_names[]. */
static int frv_unit_codes[ARRAY_SIZE (frv_unit_names)];
 
/* FRV_TYPE_TO_UNIT[T] is the last unit in frv_unit_names[] that can issue
an instruction of type T. The value is ARRAY_SIZE (frv_unit_names) if
no instruction of type T has been seen. */
static unsigned int frv_type_to_unit[TYPE_UNKNOWN + 1];
 
/* An array of dummy nop INSNs, one for each type of nop that the
target supports. */
static GTY(()) rtx frv_nops[NUM_NOP_PATTERNS];
 
/* The number of nop instructions in frv_nops[]. */
static unsigned int frv_num_nops;
 
/* Information about one __builtin_read or __builtin_write access, or
the combination of several such accesses. The most general value
is all-zeros (an unknown access to an unknown address). */
struct frv_io {
/* The type of access. FRV_IO_UNKNOWN means the access can be either
a read or a write. */
enum { FRV_IO_UNKNOWN, FRV_IO_READ, FRV_IO_WRITE } type;
 
/* The constant address being accessed, or zero if not known. */
HOST_WIDE_INT const_address;
 
/* The run-time address, as used in operand 0 of the membar pattern. */
rtx var_address;
};
 
/* Return true if instruction INSN should be packed with the following
instruction. */
#define PACKING_FLAG_P(INSN) (GET_MODE (INSN) == TImode)
 
/* Set the value of PACKING_FLAG_P(INSN). */
#define SET_PACKING_FLAG(INSN) PUT_MODE (INSN, TImode)
#define CLEAR_PACKING_FLAG(INSN) PUT_MODE (INSN, VOIDmode)
 
/* Loop with REG set to each hard register in rtx X. */
#define FOR_EACH_REGNO(REG, X) \
for (REG = REGNO (X); \
REG < REGNO (X) + HARD_REGNO_NREGS (REGNO (X), GET_MODE (X)); \
REG++)
 
/* This structure contains machine specific function data. */
struct machine_function GTY(())
{
/* True if we have created an rtx that relies on the stack frame. */
int frame_needed;
 
/* True if this function contains at least one __builtin_{read,write}*. */
bool has_membar_p;
};
 
/* Temporary register allocation support structure. */
typedef struct frv_tmp_reg_struct
{
HARD_REG_SET regs; /* possible registers to allocate */
int next_reg[N_REG_CLASSES]; /* next register to allocate per class */
}
frv_tmp_reg_t;
 
/* Register state information for VLIW re-packing phase. */
#define REGSTATE_CC_MASK 0x07 /* Mask to isolate CCn for cond exec */
#define REGSTATE_MODIFIED 0x08 /* reg modified in current VLIW insn */
#define REGSTATE_IF_TRUE 0x10 /* reg modified in cond exec true */
#define REGSTATE_IF_FALSE 0x20 /* reg modified in cond exec false */
 
#define REGSTATE_IF_EITHER (REGSTATE_IF_TRUE | REGSTATE_IF_FALSE)
 
typedef unsigned char regstate_t;
 
/* Used in frv_frame_accessor_t to indicate the direction of a register-to-
memory move. */
enum frv_stack_op
{
FRV_LOAD,
FRV_STORE
};
 
/* Information required by frv_frame_access. */
typedef struct
{
/* This field is FRV_LOAD if registers are to be loaded from the stack and
FRV_STORE if they should be stored onto the stack. FRV_STORE implies
the move is being done by the prologue code while FRV_LOAD implies it
is being done by the epilogue. */
enum frv_stack_op op;
 
/* The base register to use when accessing the stack. This may be the
frame pointer, stack pointer, or a temporary. The choice of register
depends on which part of the frame is being accessed and how big the
frame is. */
rtx base;
 
/* The offset of BASE from the bottom of the current frame, in bytes. */
int base_offset;
} frv_frame_accessor_t;
 
/* Define the information needed to generate branch and scc insns. This is
stored from the compare operation. */
rtx frv_compare_op0;
rtx frv_compare_op1;
 
/* Conditional execution support gathered together in one structure. */
typedef struct
{
/* Linked list of insns to add if the conditional execution conversion was
successful. Each link points to an EXPR_LIST which points to the pattern
of the insn to add, and the insn to be inserted before. */
rtx added_insns_list;
 
/* Identify which registers are safe to allocate for if conversions to
conditional execution. We keep the last allocated register in the
register classes between COND_EXEC statements. This will mean we allocate
different registers for each different COND_EXEC group if we can. This
might allow the scheduler to intermix two different COND_EXEC sections. */
frv_tmp_reg_t tmp_reg;
 
/* For nested IFs, identify which CC registers are used outside of setting
via a compare isnsn, and using via a check insn. This will allow us to
know if we can rewrite the register to use a different register that will
be paired with the CR register controlling the nested IF-THEN blocks. */
HARD_REG_SET nested_cc_ok_rewrite;
 
/* Temporary registers allocated to hold constants during conditional
execution. */
rtx scratch_regs[FIRST_PSEUDO_REGISTER];
 
/* Current number of temp registers available. */
int cur_scratch_regs;
 
/* Number of nested conditional execution blocks. */
int num_nested_cond_exec;
 
/* Map of insns that set up constants in scratch registers. */
bitmap scratch_insns_bitmap;
 
/* Conditional execution test register (CC0..CC7). */
rtx cr_reg;
 
/* Conditional execution compare register that is paired with cr_reg, so that
nested compares can be done. The csubcc and caddcc instructions don't
have enough bits to specify both a CC register to be set and a CR register
to do the test on, so the same bit number is used for both. Needless to
say, this is rather inconvenient for GCC. */
rtx nested_cc_reg;
 
/* Extra CR registers used for &&, ||. */
rtx extra_int_cr;
rtx extra_fp_cr;
 
/* Previous CR used in nested if, to make sure we are dealing with the same
nested if as the previous statement. */
rtx last_nested_if_cr;
}
frv_ifcvt_t;
 
static /* GTY(()) */ frv_ifcvt_t frv_ifcvt;
 
/* Map register number to smallest register class. */
enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER];
 
/* Map class letter into register class. */
enum reg_class reg_class_from_letter[256];
 
/* Cached value of frv_stack_info. */
static frv_stack_t *frv_stack_cache = (frv_stack_t *)0;
 
/* -mcpu= support */
frv_cpu_t frv_cpu_type = CPU_TYPE; /* value of -mcpu= */
 
/* Forward references */
 
static bool frv_handle_option (size_t, const char *, int);
static int frv_default_flags_for_cpu (void);
static int frv_string_begins_with (tree, const char *);
static FRV_INLINE bool frv_small_data_reloc_p (rtx, int);
static void frv_print_operand_memory_reference_reg
(FILE *, rtx);
static void frv_print_operand_memory_reference (FILE *, rtx, int);
static int frv_print_operand_jump_hint (rtx);
static const char *comparison_string (enum rtx_code, rtx);
static FRV_INLINE int frv_regno_ok_for_base_p (int, int);
static rtx single_set_pattern (rtx);
static int frv_function_contains_far_jump (void);
static rtx frv_alloc_temp_reg (frv_tmp_reg_t *,
enum reg_class,
enum machine_mode,
int, int);
static rtx frv_frame_offset_rtx (int);
static rtx frv_frame_mem (enum machine_mode, rtx, int);
static rtx frv_dwarf_store (rtx, int);
static void frv_frame_insn (rtx, rtx);
static void frv_frame_access (frv_frame_accessor_t*,
rtx, int);
static void frv_frame_access_multi (frv_frame_accessor_t*,
frv_stack_t *, int);
static void frv_frame_access_standard_regs (enum frv_stack_op,
frv_stack_t *);
static struct machine_function *frv_init_machine_status (void);
static rtx frv_int_to_acc (enum insn_code, int, rtx);
static enum machine_mode frv_matching_accg_mode (enum machine_mode);
static rtx frv_read_argument (tree *);
static rtx frv_read_iacc_argument (enum machine_mode, tree *);
static int frv_check_constant_argument (enum insn_code, int, rtx);
static rtx frv_legitimize_target (enum insn_code, rtx);
static rtx frv_legitimize_argument (enum insn_code, int, rtx);
static rtx frv_legitimize_tls_address (rtx, enum tls_model);
static rtx frv_expand_set_builtin (enum insn_code, tree, rtx);
static rtx frv_expand_unop_builtin (enum insn_code, tree, rtx);
static rtx frv_expand_binop_builtin (enum insn_code, tree, rtx);
static rtx frv_expand_cut_builtin (enum insn_code, tree, rtx);
static rtx frv_expand_binopimm_builtin (enum insn_code, tree, rtx);
static rtx frv_expand_voidbinop_builtin (enum insn_code, tree);
static rtx frv_expand_int_void2arg (enum insn_code, tree);
static rtx frv_expand_prefetches (enum insn_code, tree);
static rtx frv_expand_voidtriop_builtin (enum insn_code, tree);
static rtx frv_expand_voidaccop_builtin (enum insn_code, tree);
static rtx frv_expand_mclracc_builtin (tree);
static rtx frv_expand_mrdacc_builtin (enum insn_code, tree);
static rtx frv_expand_mwtacc_builtin (enum insn_code, tree);
static rtx frv_expand_noargs_builtin (enum insn_code);
static void frv_split_iacc_move (rtx, rtx);
static rtx frv_emit_comparison (enum rtx_code, rtx, rtx);
static int frv_clear_registers_used (rtx *, void *);
static void frv_ifcvt_add_insn (rtx, rtx, int);
static rtx frv_ifcvt_rewrite_mem (rtx, enum machine_mode, rtx);
static rtx frv_ifcvt_load_value (rtx, rtx);
static int frv_acc_group_1 (rtx *, void *);
static unsigned int frv_insn_unit (rtx);
static bool frv_issues_to_branch_unit_p (rtx);
static int frv_cond_flags (rtx);
static bool frv_regstate_conflict_p (regstate_t, regstate_t);
static int frv_registers_conflict_p_1 (rtx *, void *);
static bool frv_registers_conflict_p (rtx);
static void frv_registers_update_1 (rtx, rtx, void *);
static void frv_registers_update (rtx);
static void frv_start_packet (void);
static void frv_start_packet_block (void);
static void frv_finish_packet (void (*) (void));
static bool frv_pack_insn_p (rtx);
static void frv_add_insn_to_packet (rtx);
static void frv_insert_nop_in_packet (rtx);
static bool frv_for_each_packet (void (*) (void));
static bool frv_sort_insn_group_1 (enum frv_insn_group,
unsigned int, unsigned int,
unsigned int, unsigned int,
state_t);
static int frv_compare_insns (const void *, const void *);
static void frv_sort_insn_group (enum frv_insn_group);
static void frv_reorder_packet (void);
static void frv_fill_unused_units (enum frv_insn_group);
static void frv_align_label (void);
static void frv_reorg_packet (void);
static void frv_register_nop (rtx);
static void frv_reorg (void);
static void frv_pack_insns (void);
static void frv_function_prologue (FILE *, HOST_WIDE_INT);
static void frv_function_epilogue (FILE *, HOST_WIDE_INT);
static bool frv_assemble_integer (rtx, unsigned, int);
static void frv_init_builtins (void);
static rtx frv_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
static void frv_init_libfuncs (void);
static bool frv_in_small_data_p (tree);
static void frv_asm_output_mi_thunk
(FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree);
static void frv_setup_incoming_varargs (CUMULATIVE_ARGS *,
enum machine_mode,
tree, int *, int);
static rtx frv_expand_builtin_saveregs (void);
static bool frv_rtx_costs (rtx, int, int, int*);
static void frv_asm_out_constructor (rtx, int);
static void frv_asm_out_destructor (rtx, int);
static bool frv_function_symbol_referenced_p (rtx);
static bool frv_cannot_force_const_mem (rtx);
static const char *unspec_got_name (int);
static void frv_output_const_unspec (FILE *,
const struct frv_unspec *);
static bool frv_function_ok_for_sibcall (tree, tree);
static rtx frv_struct_value_rtx (tree, int);
static bool frv_must_pass_in_stack (enum machine_mode mode, tree type);
static int frv_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
tree, bool);
static void frv_output_dwarf_dtprel (FILE *, int, rtx)
ATTRIBUTE_UNUSED;
/* Allow us to easily change the default for -malloc-cc. */
#ifndef DEFAULT_NO_ALLOC_CC
#define MASK_DEFAULT_ALLOC_CC MASK_ALLOC_CC
#else
#define MASK_DEFAULT_ALLOC_CC 0
#endif
/* Initialize the GCC target structure. */
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE frv_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE frv_function_epilogue
#undef TARGET_ASM_INTEGER
#define TARGET_ASM_INTEGER frv_assemble_integer
#undef TARGET_DEFAULT_TARGET_FLAGS
#define TARGET_DEFAULT_TARGET_FLAGS \
(MASK_DEFAULT_ALLOC_CC \
| MASK_COND_MOVE \
| MASK_SCC \
| MASK_COND_EXEC \
| MASK_VLIW_BRANCH \
| MASK_MULTI_CE \
| MASK_NESTED_CE)
#undef TARGET_HANDLE_OPTION
#define TARGET_HANDLE_OPTION frv_handle_option
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS frv_init_builtins
#undef TARGET_EXPAND_BUILTIN
#define TARGET_EXPAND_BUILTIN frv_expand_builtin
#undef TARGET_INIT_LIBFUNCS
#define TARGET_INIT_LIBFUNCS frv_init_libfuncs
#undef TARGET_IN_SMALL_DATA_P
#define TARGET_IN_SMALL_DATA_P frv_in_small_data_p
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS frv_rtx_costs
#undef TARGET_ASM_CONSTRUCTOR
#define TARGET_ASM_CONSTRUCTOR frv_asm_out_constructor
#undef TARGET_ASM_DESTRUCTOR
#define TARGET_ASM_DESTRUCTOR frv_asm_out_destructor
 
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK frv_asm_output_mi_thunk
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
 
#undef TARGET_SCHED_ISSUE_RATE
#define TARGET_SCHED_ISSUE_RATE frv_issue_rate
 
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
#define TARGET_FUNCTION_OK_FOR_SIBCALL frv_function_ok_for_sibcall
#undef TARGET_CANNOT_FORCE_CONST_MEM
#define TARGET_CANNOT_FORCE_CONST_MEM frv_cannot_force_const_mem
 
#undef TARGET_HAVE_TLS
#define TARGET_HAVE_TLS HAVE_AS_TLS
 
#undef TARGET_STRUCT_VALUE_RTX
#define TARGET_STRUCT_VALUE_RTX frv_struct_value_rtx
#undef TARGET_MUST_PASS_IN_STACK
#define TARGET_MUST_PASS_IN_STACK frv_must_pass_in_stack
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE hook_pass_by_reference_must_pass_in_stack
#undef TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES frv_arg_partial_bytes
 
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
#define TARGET_EXPAND_BUILTIN_SAVEREGS frv_expand_builtin_saveregs
#undef TARGET_SETUP_INCOMING_VARARGS
#define TARGET_SETUP_INCOMING_VARARGS frv_setup_incoming_varargs
#undef TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG frv_reorg
 
#if HAVE_AS_TLS
#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
#define TARGET_ASM_OUTPUT_DWARF_DTPREL frv_output_dwarf_dtprel
#endif
 
struct gcc_target targetm = TARGET_INITIALIZER;
 
#define FRV_SYMBOL_REF_TLS_P(RTX) \
(GET_CODE (RTX) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (RTX) != 0)
 
/* Any function call that satisfies the machine-independent
requirements is eligible on FR-V. */
 
static bool
frv_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED,
tree exp ATTRIBUTE_UNUSED)
{
return true;
}
 
/* Return true if SYMBOL is a small data symbol and relocation RELOC
can be used to access it directly in a load or store. */
 
static FRV_INLINE bool
frv_small_data_reloc_p (rtx symbol, int reloc)
{
return (GET_CODE (symbol) == SYMBOL_REF
&& SYMBOL_REF_SMALL_P (symbol)
&& (!TARGET_FDPIC || flag_pic == 1)
&& (reloc == R_FRV_GOTOFF12 || reloc == R_FRV_GPREL12));
}
 
/* Return true if X is a valid relocation unspec. If it is, fill in UNSPEC
appropriately. */
 
bool
frv_const_unspec_p (rtx x, struct frv_unspec *unspec)
{
if (GET_CODE (x) == CONST)
{
unspec->offset = 0;
x = XEXP (x, 0);
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
{
unspec->offset += INTVAL (XEXP (x, 1));
x = XEXP (x, 0);
}
if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_GOT)
{
unspec->symbol = XVECEXP (x, 0, 0);
unspec->reloc = INTVAL (XVECEXP (x, 0, 1));
 
if (unspec->offset == 0)
return true;
 
if (frv_small_data_reloc_p (unspec->symbol, unspec->reloc)
&& unspec->offset > 0
&& (unsigned HOST_WIDE_INT) unspec->offset < g_switch_value)
return true;
}
}
return false;
}
 
/* Decide whether we can force certain constants to memory. If we
decide we can't, the caller should be able to cope with it in
another way.
 
We never allow constants to be forced into memory for TARGET_FDPIC.
This is necessary for several reasons:
 
1. Since LEGITIMATE_CONSTANT_P rejects constant pool addresses, the
target-independent code will try to force them into the constant
pool, thus leading to infinite recursion.
 
2. We can never introduce new constant pool references during reload.
Any such reference would require use of the pseudo FDPIC register.
 
3. We can't represent a constant added to a function pointer (which is
not the same as a pointer to a function+constant).
 
4. In many cases, it's more efficient to calculate the constant in-line. */
 
static bool
frv_cannot_force_const_mem (rtx x ATTRIBUTE_UNUSED)
{
return TARGET_FDPIC;
}
/* Implement TARGET_HANDLE_OPTION. */
 
static bool
frv_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
{
switch (code)
{
case OPT_mcpu_:
if (strcmp (arg, "simple") == 0)
frv_cpu_type = FRV_CPU_SIMPLE;
else if (strcmp (arg, "tomcat") == 0)
frv_cpu_type = FRV_CPU_TOMCAT;
else if (strcmp (arg, "fr550") == 0)
frv_cpu_type = FRV_CPU_FR550;
else if (strcmp (arg, "fr500") == 0)
frv_cpu_type = FRV_CPU_FR500;
else if (strcmp (arg, "fr450") == 0)
frv_cpu_type = FRV_CPU_FR450;
else if (strcmp (arg, "fr405") == 0)
frv_cpu_type = FRV_CPU_FR405;
else if (strcmp (arg, "fr400") == 0)
frv_cpu_type = FRV_CPU_FR400;
else if (strcmp (arg, "fr300") == 0)
frv_cpu_type = FRV_CPU_FR300;
else if (strcmp (arg, "frv") == 0)
frv_cpu_type = FRV_CPU_GENERIC;
else
return false;
return true;
 
default:
return true;
}
}
 
static int
frv_default_flags_for_cpu (void)
{
switch (frv_cpu_type)
{
case FRV_CPU_GENERIC:
return MASK_DEFAULT_FRV;
 
case FRV_CPU_FR550:
return MASK_DEFAULT_FR550;
 
case FRV_CPU_FR500:
case FRV_CPU_TOMCAT:
return MASK_DEFAULT_FR500;
 
case FRV_CPU_FR450:
return MASK_DEFAULT_FR450;
 
case FRV_CPU_FR405:
case FRV_CPU_FR400:
return MASK_DEFAULT_FR400;
 
case FRV_CPU_FR300:
case FRV_CPU_SIMPLE:
return MASK_DEFAULT_SIMPLE;
 
default:
gcc_unreachable ();
}
}
 
/* Sometimes certain combinations of command options do not make
sense on a particular target machine. You can define a macro
`OVERRIDE_OPTIONS' to take account of this. This macro, if
defined, is executed once just after all the command options have
been parsed.
 
Don't use this macro to turn on various extra optimizations for
`-O'. That is what `OPTIMIZATION_OPTIONS' is for. */
 
void
frv_override_options (void)
{
int regno;
unsigned int i;
 
target_flags |= (frv_default_flags_for_cpu () & ~target_flags_explicit);
 
/* -mlibrary-pic sets -fPIC and -G0 and also suppresses warnings from the
linker about linking pic and non-pic code. */
if (TARGET_LIBPIC)
{
if (!flag_pic) /* -fPIC */
flag_pic = 2;
 
if (! g_switch_set) /* -G0 */
{
g_switch_set = 1;
g_switch_value = 0;
}
}
 
/* A C expression whose value is a register class containing hard
register REGNO. In general there is more than one such class;
choose a class which is "minimal", meaning that no smaller class
also contains the register. */
 
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
enum reg_class class;
 
if (GPR_P (regno))
{
int gpr_reg = regno - GPR_FIRST;
 
if (gpr_reg == GR8_REG)
class = GR8_REGS;
 
else if (gpr_reg == GR9_REG)
class = GR9_REGS;
 
else if (gpr_reg == GR14_REG)
class = FDPIC_FPTR_REGS;
 
else if (gpr_reg == FDPIC_REGNO)
class = FDPIC_REGS;
 
else if ((gpr_reg & 3) == 0)
class = QUAD_REGS;
 
else if ((gpr_reg & 1) == 0)
class = EVEN_REGS;
 
else
class = GPR_REGS;
}
 
else if (FPR_P (regno))
{
int fpr_reg = regno - GPR_FIRST;
if ((fpr_reg & 3) == 0)
class = QUAD_FPR_REGS;
 
else if ((fpr_reg & 1) == 0)
class = FEVEN_REGS;
 
else
class = FPR_REGS;
}
 
else if (regno == LR_REGNO)
class = LR_REG;
 
else if (regno == LCR_REGNO)
class = LCR_REG;
 
else if (ICC_P (regno))
class = ICC_REGS;
 
else if (FCC_P (regno))
class = FCC_REGS;
 
else if (ICR_P (regno))
class = ICR_REGS;
 
else if (FCR_P (regno))
class = FCR_REGS;
 
else if (ACC_P (regno))
{
int r = regno - ACC_FIRST;
if ((r & 3) == 0)
class = QUAD_ACC_REGS;
else if ((r & 1) == 0)
class = EVEN_ACC_REGS;
else
class = ACC_REGS;
}
 
else if (ACCG_P (regno))
class = ACCG_REGS;
 
else
class = NO_REGS;
 
regno_reg_class[regno] = class;
}
 
/* Check for small data option */
if (!g_switch_set)
g_switch_value = SDATA_DEFAULT_SIZE;
 
/* A C expression which defines the machine-dependent operand
constraint letters for register classes. If CHAR is such a
letter, the value should be the register class corresponding to
it. Otherwise, the value should be `NO_REGS'. The register
letter `r', corresponding to class `GENERAL_REGS', will not be
passed to this macro; you do not need to handle it.
 
The following letters are unavailable, due to being used as
constraints:
'0'..'9'
'<', '>'
'E', 'F', 'G', 'H'
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'
'Q', 'R', 'S', 'T', 'U'
'V', 'X'
'g', 'i', 'm', 'n', 'o', 'p', 'r', 's' */
 
for (i = 0; i < 256; i++)
reg_class_from_letter[i] = NO_REGS;
 
reg_class_from_letter['a'] = ACC_REGS;
reg_class_from_letter['b'] = EVEN_ACC_REGS;
reg_class_from_letter['c'] = CC_REGS;
reg_class_from_letter['d'] = GPR_REGS;
reg_class_from_letter['e'] = EVEN_REGS;
reg_class_from_letter['f'] = FPR_REGS;
reg_class_from_letter['h'] = FEVEN_REGS;
reg_class_from_letter['l'] = LR_REG;
reg_class_from_letter['q'] = QUAD_REGS;
reg_class_from_letter['t'] = ICC_REGS;
reg_class_from_letter['u'] = FCC_REGS;
reg_class_from_letter['v'] = ICR_REGS;
reg_class_from_letter['w'] = FCR_REGS;
reg_class_from_letter['x'] = QUAD_FPR_REGS;
reg_class_from_letter['y'] = LCR_REG;
reg_class_from_letter['z'] = SPR_REGS;
reg_class_from_letter['A'] = QUAD_ACC_REGS;
reg_class_from_letter['B'] = ACCG_REGS;
reg_class_from_letter['C'] = CR_REGS;
reg_class_from_letter['W'] = FDPIC_CALL_REGS; /* gp14+15 */
reg_class_from_letter['Z'] = FDPIC_REGS; /* gp15 */
 
/* There is no single unaligned SI op for PIC code. Sometimes we
need to use ".4byte" and sometimes we need to use ".picptr".
See frv_assemble_integer for details. */
if (flag_pic || TARGET_FDPIC)
targetm.asm_out.unaligned_op.si = 0;
 
if ((target_flags_explicit & MASK_LINKED_FP) == 0)
target_flags |= MASK_LINKED_FP;
 
if ((target_flags_explicit & MASK_OPTIMIZE_MEMBAR) == 0)
target_flags |= MASK_OPTIMIZE_MEMBAR;
 
for (i = 0; i < ARRAY_SIZE (frv_unit_names); i++)
frv_unit_codes[i] = get_cpu_unit_code (frv_unit_names[i]);
 
for (i = 0; i < ARRAY_SIZE (frv_type_to_unit); i++)
frv_type_to_unit[i] = ARRAY_SIZE (frv_unit_codes);
 
init_machine_status = frv_init_machine_status;
}
 
/* Some machines may desire to change what optimizations are performed for
various optimization levels. This macro, if defined, is executed once just
after the optimization level is determined and before the remainder of the
command options have been parsed. Values set in this macro are used as the
default values for the other command line options.
 
LEVEL is the optimization level specified; 2 if `-O2' is specified, 1 if
`-O' is specified, and 0 if neither is specified.
 
SIZE is nonzero if `-Os' is specified, 0 otherwise.
 
You should not use this macro to change options that are not
machine-specific. These should uniformly selected by the same optimization
level on all supported machines. Use this macro to enable machine-specific
optimizations.
 
*Do not examine `write_symbols' in this macro!* The debugging options are
*not supposed to alter the generated code. */
 
/* On the FRV, possibly disable VLIW packing which is done by the 2nd
scheduling pass at the current time. */
void
frv_optimization_options (int level, int size ATTRIBUTE_UNUSED)
{
if (level >= 2)
{
#ifdef DISABLE_SCHED2
flag_schedule_insns_after_reload = 0;
#endif
#ifdef ENABLE_RCSP
flag_rcsp = 1;
#endif
}
}
 
/* Return true if NAME (a STRING_CST node) begins with PREFIX. */
 
static int
frv_string_begins_with (tree name, const char *prefix)
{
int prefix_len = strlen (prefix);
 
/* Remember: NAME's length includes the null terminator. */
return (TREE_STRING_LENGTH (name) > prefix_len
&& strncmp (TREE_STRING_POINTER (name), prefix, prefix_len) == 0);
}
/* Zero or more C statements that may conditionally modify two variables
`fixed_regs' and `call_used_regs' (both of type `char []') after they have
been initialized from the two preceding macros.
 
This is necessary in case the fixed or call-clobbered registers depend on
target flags.
 
You need not define this macro if it has no work to do.
 
If the usage of an entire class of registers depends on the target flags,
you may indicate this to GCC by using this macro to modify `fixed_regs' and
`call_used_regs' to 1 for each of the registers in the classes which should
not be used by GCC. Also define the macro `REG_CLASS_FROM_LETTER' to return
`NO_REGS' if it is called with a letter for a class that shouldn't be used.
 
(However, if this class is not included in `GENERAL_REGS' and all of the
insn patterns whose constraints permit this class are controlled by target
switches, then GCC will automatically avoid using these registers when the
target switches are opposed to them.) */
 
void
frv_conditional_register_usage (void)
{
int i;
 
for (i = GPR_FIRST + NUM_GPRS; i <= GPR_LAST; i++)
fixed_regs[i] = call_used_regs[i] = 1;
 
for (i = FPR_FIRST + NUM_FPRS; i <= FPR_LAST; i++)
fixed_regs[i] = call_used_regs[i] = 1;
 
/* Reserve the registers used for conditional execution. At present, we need
1 ICC and 1 ICR register. */
fixed_regs[ICC_TEMP] = call_used_regs[ICC_TEMP] = 1;
fixed_regs[ICR_TEMP] = call_used_regs[ICR_TEMP] = 1;
 
if (TARGET_FIXED_CC)
{
fixed_regs[ICC_FIRST] = call_used_regs[ICC_FIRST] = 1;
fixed_regs[FCC_FIRST] = call_used_regs[FCC_FIRST] = 1;
fixed_regs[ICR_FIRST] = call_used_regs[ICR_FIRST] = 1;
fixed_regs[FCR_FIRST] = call_used_regs[FCR_FIRST] = 1;
}
 
if (TARGET_FDPIC)
fixed_regs[GPR_FIRST + 16] = fixed_regs[GPR_FIRST + 17] =
call_used_regs[GPR_FIRST + 16] = call_used_regs[GPR_FIRST + 17] = 0;
 
#if 0
/* If -fpic, SDA_BASE_REG is the PIC register. */
if (g_switch_value == 0 && !flag_pic)
fixed_regs[SDA_BASE_REG] = call_used_regs[SDA_BASE_REG] = 0;
 
if (!flag_pic)
fixed_regs[PIC_REGNO] = call_used_regs[PIC_REGNO] = 0;
#endif
}
 
/*
* Compute the stack frame layout
*
* Register setup:
* +---------------+-----------------------+-----------------------+
* |Register |type |caller-save/callee-save|
* +---------------+-----------------------+-----------------------+
* |GR0 |Zero register | - |
* |GR1 |Stack pointer(SP) | - |
* |GR2 |Frame pointer(FP) | - |
* |GR3 |Hidden parameter | caller save |
* |GR4-GR7 | - | caller save |
* |GR8-GR13 |Argument register | caller save |
* |GR14-GR15 | - | caller save |
* |GR16-GR31 | - | callee save |
* |GR32-GR47 | - | caller save |
* |GR48-GR63 | - | callee save |
* |FR0-FR15 | - | caller save |
* |FR16-FR31 | - | callee save |
* |FR32-FR47 | - | caller save |
* |FR48-FR63 | - | callee save |
* +---------------+-----------------------+-----------------------+
*
* Stack frame setup:
* Low
* SP-> |-----------------------------------|
* | Argument area |
* |-----------------------------------|
* | Register save area |
* |-----------------------------------|
* | Local variable save area |
* FP-> |-----------------------------------|
* | Old FP |
* |-----------------------------------|
* | Hidden parameter save area |
* |-----------------------------------|
* | Return address(LR) storage area |
* |-----------------------------------|
* | Padding for alignment |
* |-----------------------------------|
* | Register argument area |
* OLD SP-> |-----------------------------------|
* | Parameter area |
* |-----------------------------------|
* High
*
* Argument area/Parameter area:
*
* When a function is called, this area is used for argument transfer. When
* the argument is set up by the caller function, this area is referred to as
* the argument area. When the argument is referenced by the callee function,
* this area is referred to as the parameter area. The area is allocated when
* all arguments cannot be placed on the argument register at the time of
* argument transfer.
*
* Register save area:
*
* This is a register save area that must be guaranteed for the caller
* function. This area is not secured when the register save operation is not
* needed.
*
* Local variable save area:
*
* This is the area for local variables and temporary variables.
*
* Old FP:
*
* This area stores the FP value of the caller function.
*
* Hidden parameter save area:
*
* This area stores the start address of the return value storage
* area for a struct/union return function.
* When a struct/union is used as the return value, the caller
* function stores the return value storage area start address in
* register GR3 and passes it to the caller function.
* The callee function interprets the address stored in the GR3
* as the return value storage area start address.
* When register GR3 needs to be saved into memory, the callee
* function saves it in the hidden parameter save area. This
* area is not secured when the save operation is not needed.
*
* Return address(LR) storage area:
*
* This area saves the LR. The LR stores the address of a return to the caller
* function for the purpose of function calling.
*
* Argument register area:
*
* This area saves the argument register. This area is not secured when the
* save operation is not needed.
*
* Argument:
*
* Arguments, the count of which equals the count of argument registers (6
* words), are positioned in registers GR8 to GR13 and delivered to the callee
* function. When a struct/union return function is called, the return value
* area address is stored in register GR3. Arguments not placed in the
* argument registers will be stored in the stack argument area for transfer
* purposes. When an 8-byte type argument is to be delivered using registers,
* it is divided into two and placed in two registers for transfer. When
* argument registers must be saved to memory, the callee function secures an
* argument register save area in the stack. In this case, a continuous
* argument register save area must be established in the parameter area. The
* argument register save area must be allocated as needed to cover the size of
* the argument register to be saved. If the function has a variable count of
* arguments, it saves all argument registers in the argument register save
* area.
*
* Argument Extension Format:
*
* When an argument is to be stored in the stack, its type is converted to an
* extended type in accordance with the individual argument type. The argument
* is freed by the caller function after the return from the callee function is
* made.
*
* +-----------------------+---------------+------------------------+
* | Argument Type |Extended Type |Stack Storage Size(byte)|
* +-----------------------+---------------+------------------------+
* |char |int | 4 |
* |signed char |int | 4 |
* |unsigned char |int | 4 |
* |[signed] short int |int | 4 |
* |unsigned short int |int | 4 |
* |[signed] int |No extension | 4 |
* |unsigned int |No extension | 4 |
* |[signed] long int |No extension | 4 |
* |unsigned long int |No extension | 4 |
* |[signed] long long int |No extension | 8 |
* |unsigned long long int |No extension | 8 |
* |float |double | 8 |
* |double |No extension | 8 |
* |long double |No extension | 8 |
* |pointer |No extension | 4 |
* |struct/union |- | 4 (*1) |
* +-----------------------+---------------+------------------------+
*
* When a struct/union is to be delivered as an argument, the caller copies it
* to the local variable area and delivers the address of that area.
*
* Return Value:
*
* +-------------------------------+----------------------+
* |Return Value Type |Return Value Interface|
* +-------------------------------+----------------------+
* |void |None |
* |[signed|unsigned] char |GR8 |
* |[signed|unsigned] short int |GR8 |
* |[signed|unsigned] int |GR8 |
* |[signed|unsigned] long int |GR8 |
* |pointer |GR8 |
* |[signed|unsigned] long long int|GR8 & GR9 |
* |float |GR8 |
* |double |GR8 & GR9 |
* |long double |GR8 & GR9 |
* |struct/union |(*1) |
* +-------------------------------+----------------------+
*
* When a struct/union is used as the return value, the caller function stores
* the start address of the return value storage area into GR3 and then passes
* it to the callee function. The callee function interprets GR3 as the start
* address of the return value storage area. When this address needs to be
* saved in memory, the callee function secures the hidden parameter save area
* and saves the address in that area.
*/
 
frv_stack_t *
frv_stack_info (void)
{
static frv_stack_t info, zero_info;
frv_stack_t *info_ptr = &info;
tree fndecl = current_function_decl;
int varargs_p = 0;
tree cur_arg;
tree next_arg;
int range;
int alignment;
int offset;
 
/* If we've already calculated the values and reload is complete,
just return now. */
if (frv_stack_cache)
return frv_stack_cache;
 
/* Zero all fields. */
info = zero_info;
 
/* Set up the register range information. */
info_ptr->regs[STACK_REGS_GPR].name = "gpr";
info_ptr->regs[STACK_REGS_GPR].first = LAST_ARG_REGNUM + 1;
info_ptr->regs[STACK_REGS_GPR].last = GPR_LAST;
info_ptr->regs[STACK_REGS_GPR].dword_p = TRUE;
 
info_ptr->regs[STACK_REGS_FPR].name = "fpr";
info_ptr->regs[STACK_REGS_FPR].first = FPR_FIRST;
info_ptr->regs[STACK_REGS_FPR].last = FPR_LAST;
info_ptr->regs[STACK_REGS_FPR].dword_p = TRUE;
 
info_ptr->regs[STACK_REGS_LR].name = "lr";
info_ptr->regs[STACK_REGS_LR].first = LR_REGNO;
info_ptr->regs[STACK_REGS_LR].last = LR_REGNO;
info_ptr->regs[STACK_REGS_LR].special_p = 1;
 
info_ptr->regs[STACK_REGS_CC].name = "cc";
info_ptr->regs[STACK_REGS_CC].first = CC_FIRST;
info_ptr->regs[STACK_REGS_CC].last = CC_LAST;
info_ptr->regs[STACK_REGS_CC].field_p = TRUE;
 
info_ptr->regs[STACK_REGS_LCR].name = "lcr";
info_ptr->regs[STACK_REGS_LCR].first = LCR_REGNO;
info_ptr->regs[STACK_REGS_LCR].last = LCR_REGNO;
 
info_ptr->regs[STACK_REGS_STDARG].name = "stdarg";
info_ptr->regs[STACK_REGS_STDARG].first = FIRST_ARG_REGNUM;
info_ptr->regs[STACK_REGS_STDARG].last = LAST_ARG_REGNUM;
info_ptr->regs[STACK_REGS_STDARG].dword_p = 1;
info_ptr->regs[STACK_REGS_STDARG].special_p = 1;
 
info_ptr->regs[STACK_REGS_STRUCT].name = "struct";
info_ptr->regs[STACK_REGS_STRUCT].first = FRV_STRUCT_VALUE_REGNUM;
info_ptr->regs[STACK_REGS_STRUCT].last = FRV_STRUCT_VALUE_REGNUM;
info_ptr->regs[STACK_REGS_STRUCT].special_p = 1;
 
info_ptr->regs[STACK_REGS_FP].name = "fp";
info_ptr->regs[STACK_REGS_FP].first = FRAME_POINTER_REGNUM;
info_ptr->regs[STACK_REGS_FP].last = FRAME_POINTER_REGNUM;
info_ptr->regs[STACK_REGS_FP].special_p = 1;
 
/* Determine if this is a stdarg function. If so, allocate space to store
the 6 arguments. */
if (cfun->stdarg)
varargs_p = 1;
 
else
{
/* Find the last argument, and see if it is __builtin_va_alist. */
for (cur_arg = DECL_ARGUMENTS (fndecl); cur_arg != (tree)0; cur_arg = next_arg)
{
next_arg = TREE_CHAIN (cur_arg);
if (next_arg == (tree)0)
{
if (DECL_NAME (cur_arg)
&& !strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)), "__builtin_va_alist"))
varargs_p = 1;
 
break;
}
}
}
 
/* Iterate over all of the register ranges. */
for (range = 0; range < STACK_REGS_MAX; range++)
{
frv_stack_regs_t *reg_ptr = &(info_ptr->regs[range]);
int first = reg_ptr->first;
int last = reg_ptr->last;
int size_1word = 0;
int size_2words = 0;
int regno;
 
/* Calculate which registers need to be saved & save area size. */
switch (range)
{
default:
for (regno = first; regno <= last; regno++)
{
if ((regs_ever_live[regno] && !call_used_regs[regno])
|| (current_function_calls_eh_return
&& (regno >= FIRST_EH_REGNUM && regno <= LAST_EH_REGNUM))
|| (!TARGET_FDPIC && flag_pic
&& cfun->uses_pic_offset_table && regno == PIC_REGNO))
{
info_ptr->save_p[regno] = REG_SAVE_1WORD;
size_1word += UNITS_PER_WORD;
}
}
break;
 
/* Calculate whether we need to create a frame after everything else
has been processed. */
case STACK_REGS_FP:
break;
 
case STACK_REGS_LR:
if (regs_ever_live[LR_REGNO]
|| profile_flag
/* This is set for __builtin_return_address, etc. */
|| cfun->machine->frame_needed
|| (TARGET_LINKED_FP && frame_pointer_needed)
|| (!TARGET_FDPIC && flag_pic
&& cfun->uses_pic_offset_table))
{
info_ptr->save_p[LR_REGNO] = REG_SAVE_1WORD;
size_1word += UNITS_PER_WORD;
}
break;
 
case STACK_REGS_STDARG:
if (varargs_p)
{
/* If this is a stdarg function with a non varardic
argument split between registers and the stack,
adjust the saved registers downward. */
last -= (ADDR_ALIGN (cfun->pretend_args_size, UNITS_PER_WORD)
/ UNITS_PER_WORD);
 
for (regno = first; regno <= last; regno++)
{
info_ptr->save_p[regno] = REG_SAVE_1WORD;
size_1word += UNITS_PER_WORD;
}
 
info_ptr->stdarg_size = size_1word;
}
break;
 
case STACK_REGS_STRUCT:
if (cfun->returns_struct)
{
info_ptr->save_p[FRV_STRUCT_VALUE_REGNUM] = REG_SAVE_1WORD;
size_1word += UNITS_PER_WORD;
}
break;
}
 
 
if (size_1word)
{
/* If this is a field, it only takes one word. */
if (reg_ptr->field_p)
size_1word = UNITS_PER_WORD;
 
/* Determine which register pairs can be saved together. */
else if (reg_ptr->dword_p && TARGET_DWORD)
{
for (regno = first; regno < last; regno += 2)
{
if (info_ptr->save_p[regno] && info_ptr->save_p[regno+1])
{
size_2words += 2 * UNITS_PER_WORD;
size_1word -= 2 * UNITS_PER_WORD;
info_ptr->save_p[regno] = REG_SAVE_2WORDS;
info_ptr->save_p[regno+1] = REG_SAVE_NO_SAVE;
}
}
}
 
reg_ptr->size_1word = size_1word;
reg_ptr->size_2words = size_2words;
 
if (! reg_ptr->special_p)
{
info_ptr->regs_size_1word += size_1word;
info_ptr->regs_size_2words += size_2words;
}
}
}
 
/* Set up the sizes of each each field in the frame body, making the sizes
of each be divisible by the size of a dword if dword operations might
be used, or the size of a word otherwise. */
alignment = (TARGET_DWORD? 2 * UNITS_PER_WORD : UNITS_PER_WORD);
 
info_ptr->parameter_size = ADDR_ALIGN (cfun->outgoing_args_size, alignment);
info_ptr->regs_size = ADDR_ALIGN (info_ptr->regs_size_2words
+ info_ptr->regs_size_1word,
alignment);
info_ptr->vars_size = ADDR_ALIGN (get_frame_size (), alignment);
 
info_ptr->pretend_size = cfun->pretend_args_size;
 
/* Work out the size of the frame, excluding the header. Both the frame
body and register parameter area will be dword-aligned. */
info_ptr->total_size
= (ADDR_ALIGN (info_ptr->parameter_size
+ info_ptr->regs_size
+ info_ptr->vars_size,
2 * UNITS_PER_WORD)
+ ADDR_ALIGN (info_ptr->pretend_size
+ info_ptr->stdarg_size,
2 * UNITS_PER_WORD));
 
/* See if we need to create a frame at all, if so add header area. */
if (info_ptr->total_size > 0
|| frame_pointer_needed
|| info_ptr->regs[STACK_REGS_LR].size_1word > 0
|| info_ptr->regs[STACK_REGS_STRUCT].size_1word > 0)
{
offset = info_ptr->parameter_size;
info_ptr->header_size = 4 * UNITS_PER_WORD;
info_ptr->total_size += 4 * UNITS_PER_WORD;
 
/* Calculate the offsets to save normal register pairs. */
for (range = 0; range < STACK_REGS_MAX; range++)
{
frv_stack_regs_t *reg_ptr = &(info_ptr->regs[range]);
if (! reg_ptr->special_p)
{
int first = reg_ptr->first;
int last = reg_ptr->last;
int regno;
 
for (regno = first; regno <= last; regno++)
if (info_ptr->save_p[regno] == REG_SAVE_2WORDS
&& regno != FRAME_POINTER_REGNUM
&& (regno < FIRST_ARG_REGNUM
|| regno > LAST_ARG_REGNUM))
{
info_ptr->reg_offset[regno] = offset;
offset += 2 * UNITS_PER_WORD;
}
}
}
 
/* Calculate the offsets to save normal single registers. */
for (range = 0; range < STACK_REGS_MAX; range++)
{
frv_stack_regs_t *reg_ptr = &(info_ptr->regs[range]);
if (! reg_ptr->special_p)
{
int first = reg_ptr->first;
int last = reg_ptr->last;
int regno;
 
for (regno = first; regno <= last; regno++)
if (info_ptr->save_p[regno] == REG_SAVE_1WORD
&& regno != FRAME_POINTER_REGNUM
&& (regno < FIRST_ARG_REGNUM
|| regno > LAST_ARG_REGNUM))
{
info_ptr->reg_offset[regno] = offset;
offset += UNITS_PER_WORD;
}
}
}
 
/* Calculate the offset to save the local variables at. */
offset = ADDR_ALIGN (offset, alignment);
if (info_ptr->vars_size)
{
info_ptr->vars_offset = offset;
offset += info_ptr->vars_size;
}
 
/* Align header to a dword-boundary. */
offset = ADDR_ALIGN (offset, 2 * UNITS_PER_WORD);
 
/* Calculate the offsets in the fixed frame. */
info_ptr->save_p[FRAME_POINTER_REGNUM] = REG_SAVE_1WORD;
info_ptr->reg_offset[FRAME_POINTER_REGNUM] = offset;
info_ptr->regs[STACK_REGS_FP].size_1word = UNITS_PER_WORD;
 
info_ptr->save_p[LR_REGNO] = REG_SAVE_1WORD;
info_ptr->reg_offset[LR_REGNO] = offset + 2*UNITS_PER_WORD;
info_ptr->regs[STACK_REGS_LR].size_1word = UNITS_PER_WORD;
 
if (cfun->returns_struct)
{
info_ptr->save_p[FRV_STRUCT_VALUE_REGNUM] = REG_SAVE_1WORD;
info_ptr->reg_offset[FRV_STRUCT_VALUE_REGNUM] = offset + UNITS_PER_WORD;
info_ptr->regs[STACK_REGS_STRUCT].size_1word = UNITS_PER_WORD;
}
 
/* Calculate the offsets to store the arguments passed in registers
for stdarg functions. The register pairs are first and the single
register if any is last. The register save area starts on a
dword-boundary. */
if (info_ptr->stdarg_size)
{
int first = info_ptr->regs[STACK_REGS_STDARG].first;
int last = info_ptr->regs[STACK_REGS_STDARG].last;
int regno;
 
/* Skip the header. */
offset += 4 * UNITS_PER_WORD;
for (regno = first; regno <= last; regno++)
{
if (info_ptr->save_p[regno] == REG_SAVE_2WORDS)
{
info_ptr->reg_offset[regno] = offset;
offset += 2 * UNITS_PER_WORD;
}
else if (info_ptr->save_p[regno] == REG_SAVE_1WORD)
{
info_ptr->reg_offset[regno] = offset;
offset += UNITS_PER_WORD;
}
}
}
}
 
if (reload_completed)
frv_stack_cache = info_ptr;
 
return info_ptr;
}
 
/* Print the information about the frv stack offsets, etc. when debugging. */
 
void
frv_debug_stack (frv_stack_t *info)
{
int range;
 
if (!info)
info = frv_stack_info ();
 
fprintf (stderr, "\nStack information for function %s:\n",
((current_function_decl && DECL_NAME (current_function_decl))
? IDENTIFIER_POINTER (DECL_NAME (current_function_decl))
: "<unknown>"));
 
fprintf (stderr, "\ttotal_size\t= %6d\n", info->total_size);
fprintf (stderr, "\tvars_size\t= %6d\n", info->vars_size);
fprintf (stderr, "\tparam_size\t= %6d\n", info->parameter_size);
fprintf (stderr, "\tregs_size\t= %6d, 1w = %3d, 2w = %3d\n",
info->regs_size, info->regs_size_1word, info->regs_size_2words);
 
fprintf (stderr, "\theader_size\t= %6d\n", info->header_size);
fprintf (stderr, "\tpretend_size\t= %6d\n", info->pretend_size);
fprintf (stderr, "\tvars_offset\t= %6d\n", info->vars_offset);
fprintf (stderr, "\tregs_offset\t= %6d\n", info->regs_offset);
 
for (range = 0; range < STACK_REGS_MAX; range++)
{
frv_stack_regs_t *regs = &(info->regs[range]);
if ((regs->size_1word + regs->size_2words) > 0)
{
int first = regs->first;
int last = regs->last;
int regno;
 
fprintf (stderr, "\t%s\tsize\t= %6d, 1w = %3d, 2w = %3d, save =",
regs->name, regs->size_1word + regs->size_2words,
regs->size_1word, regs->size_2words);
 
for (regno = first; regno <= last; regno++)
{
if (info->save_p[regno] == REG_SAVE_1WORD)
fprintf (stderr, " %s (%d)", reg_names[regno],
info->reg_offset[regno]);
 
else if (info->save_p[regno] == REG_SAVE_2WORDS)
fprintf (stderr, " %s-%s (%d)", reg_names[regno],
reg_names[regno+1], info->reg_offset[regno]);
}
 
fputc ('\n', stderr);
}
}
 
fflush (stderr);
}
 
 
 
/* Used during final to control the packing of insns. The value is
1 if the current instruction should be packed with the next one,
0 if it shouldn't or -1 if packing is disabled altogether. */
 
static int frv_insn_packing_flag;
 
/* True if the current function contains a far jump. */
 
static int
frv_function_contains_far_jump (void)
{
rtx insn = get_insns ();
while (insn != NULL
&& !(GET_CODE (insn) == JUMP_INSN
/* Ignore tablejump patterns. */
&& GET_CODE (PATTERN (insn)) != ADDR_VEC
&& GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC
&& get_attr_far_jump (insn) == FAR_JUMP_YES))
insn = NEXT_INSN (insn);
return (insn != NULL);
}
 
/* For the FRV, this function makes sure that a function with far jumps
will return correctly. It also does the VLIW packing. */
 
static void
frv_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
/* If no frame was created, check whether the function uses a call
instruction to implement a far jump. If so, save the link in gr3 and
replace all returns to LR with returns to GR3. GR3 is used because it
is call-clobbered, because is not available to the register allocator,
and because all functions that take a hidden argument pointer will have
a stack frame. */
if (frv_stack_info ()->total_size == 0 && frv_function_contains_far_jump ())
{
rtx insn;
 
/* Just to check that the above comment is true. */
gcc_assert (!regs_ever_live[GPR_FIRST + 3]);
 
/* Generate the instruction that saves the link register. */
fprintf (file, "\tmovsg lr,gr3\n");
 
/* Replace the LR with GR3 in *return_internal patterns. The insn
will now return using jmpl @(gr3,0) rather than bralr. We cannot
simply emit a different assembly directive because bralr and jmpl
execute in different units. */
for (insn = get_insns(); insn != NULL; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == JUMP_INSN)
{
rtx pattern = PATTERN (insn);
if (GET_CODE (pattern) == PARALLEL
&& XVECLEN (pattern, 0) >= 2
&& GET_CODE (XVECEXP (pattern, 0, 0)) == RETURN
&& GET_CODE (XVECEXP (pattern, 0, 1)) == USE)
{
rtx address = XEXP (XVECEXP (pattern, 0, 1), 0);
if (GET_CODE (address) == REG && REGNO (address) == LR_REGNO)
REGNO (address) = GPR_FIRST + 3;
}
}
}
 
frv_pack_insns ();
 
/* Allow the garbage collector to free the nops created by frv_reorg. */
memset (frv_nops, 0, sizeof (frv_nops));
}
 
/* Return the next available temporary register in a given class. */
 
static rtx
frv_alloc_temp_reg (
frv_tmp_reg_t *info, /* which registers are available */
enum reg_class class, /* register class desired */
enum machine_mode mode, /* mode to allocate register with */
int mark_as_used, /* register not available after allocation */
int no_abort) /* return NULL instead of aborting */
{
int regno = info->next_reg[ (int)class ];
int orig_regno = regno;
HARD_REG_SET *reg_in_class = &reg_class_contents[ (int)class ];
int i, nr;
 
for (;;)
{
if (TEST_HARD_REG_BIT (*reg_in_class, regno)
&& TEST_HARD_REG_BIT (info->regs, regno))
break;
 
if (++regno >= FIRST_PSEUDO_REGISTER)
regno = 0;
if (regno == orig_regno)
{
gcc_assert (no_abort);
return NULL_RTX;
}
}
 
nr = HARD_REGNO_NREGS (regno, mode);
info->next_reg[ (int)class ] = regno + nr;
 
if (mark_as_used)
for (i = 0; i < nr; i++)
CLEAR_HARD_REG_BIT (info->regs, regno+i);
 
return gen_rtx_REG (mode, regno);
}
 
/* Return an rtx with the value OFFSET, which will either be a register or a
signed 12-bit integer. It can be used as the second operand in an "add"
instruction, or as the index in a load or store.
 
The function returns a constant rtx if OFFSET is small enough, otherwise
it loads the constant into register OFFSET_REGNO and returns that. */
static rtx
frv_frame_offset_rtx (int offset)
{
rtx offset_rtx = GEN_INT (offset);
if (IN_RANGE_P (offset, -2048, 2047))
return offset_rtx;
else
{
rtx reg_rtx = gen_rtx_REG (SImode, OFFSET_REGNO);
if (IN_RANGE_P (offset, -32768, 32767))
emit_insn (gen_movsi (reg_rtx, offset_rtx));
else
{
emit_insn (gen_movsi_high (reg_rtx, offset_rtx));
emit_insn (gen_movsi_lo_sum (reg_rtx, offset_rtx));
}
return reg_rtx;
}
}
 
/* Generate (mem:MODE (plus:Pmode BASE (frv_frame_offset OFFSET)))). The
prologue and epilogue uses such expressions to access the stack. */
static rtx
frv_frame_mem (enum machine_mode mode, rtx base, int offset)
{
return gen_rtx_MEM (mode, gen_rtx_PLUS (Pmode,
base,
frv_frame_offset_rtx (offset)));
}
 
/* Generate a frame-related expression:
 
(set REG (mem (plus (sp) (const_int OFFSET)))).
 
Such expressions are used in FRAME_RELATED_EXPR notes for more complex
instructions. Marking the expressions as frame-related is superfluous if
the note contains just a single set. But if the note contains a PARALLEL
or SEQUENCE that has several sets, each set must be individually marked
as frame-related. */
static rtx
frv_dwarf_store (rtx reg, int offset)
{
rtx set = gen_rtx_SET (VOIDmode,
gen_rtx_MEM (GET_MODE (reg),
plus_constant (stack_pointer_rtx,
offset)),
reg);
RTX_FRAME_RELATED_P (set) = 1;
return set;
}
 
/* Emit a frame-related instruction whose pattern is PATTERN. The
instruction is the last in a sequence that cumulatively performs the
operation described by DWARF_PATTERN. The instruction is marked as
frame-related and has a REG_FRAME_RELATED_EXPR note containing
DWARF_PATTERN. */
static void
frv_frame_insn (rtx pattern, rtx dwarf_pattern)
{
rtx insn = emit_insn (pattern);
RTX_FRAME_RELATED_P (insn) = 1;
REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
dwarf_pattern,
REG_NOTES (insn));
}
 
/* Emit instructions that transfer REG to or from the memory location (sp +
STACK_OFFSET). The register is stored in memory if ACCESSOR->OP is
FRV_STORE and loaded if it is FRV_LOAD. Only the prologue uses this
function to store registers and only the epilogue uses it to load them.
 
The caller sets up ACCESSOR so that BASE is equal to (sp + BASE_OFFSET).
The generated instruction will use BASE as its base register. BASE may
simply be the stack pointer, but if several accesses are being made to a
region far away from the stack pointer, it may be more efficient to set
up a temporary instead.
 
Store instructions will be frame-related and will be annotated with the
overall effect of the store. Load instructions will be followed by a
(use) to prevent later optimizations from zapping them.
 
The function takes care of the moves to and from SPRs, using TEMP_REGNO
as a temporary in such cases. */
static void
frv_frame_access (frv_frame_accessor_t *accessor, rtx reg, int stack_offset)
{
enum machine_mode mode = GET_MODE (reg);
rtx mem = frv_frame_mem (mode,
accessor->base,
stack_offset - accessor->base_offset);
 
if (accessor->op == FRV_LOAD)
{
if (SPR_P (REGNO (reg)))
{
rtx temp = gen_rtx_REG (mode, TEMP_REGNO);
emit_insn (gen_rtx_SET (VOIDmode, temp, mem));
emit_insn (gen_rtx_SET (VOIDmode, reg, temp));
}
else
emit_insn (gen_rtx_SET (VOIDmode, reg, mem));
emit_insn (gen_rtx_USE (VOIDmode, reg));
}
else
{
if (SPR_P (REGNO (reg)))
{
rtx temp = gen_rtx_REG (mode, TEMP_REGNO);
emit_insn (gen_rtx_SET (VOIDmode, temp, reg));
frv_frame_insn (gen_rtx_SET (Pmode, mem, temp),
frv_dwarf_store (reg, stack_offset));
}
else if (GET_MODE (reg) == DImode)
{
/* For DImode saves, the dwarf2 version needs to be a SEQUENCE
with a separate save for each register. */
rtx reg1 = gen_rtx_REG (SImode, REGNO (reg));
rtx reg2 = gen_rtx_REG (SImode, REGNO (reg) + 1);
rtx set1 = frv_dwarf_store (reg1, stack_offset);
rtx set2 = frv_dwarf_store (reg2, stack_offset + 4);
frv_frame_insn (gen_rtx_SET (Pmode, mem, reg),
gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (2, set1, set2)));
}
else
frv_frame_insn (gen_rtx_SET (Pmode, mem, reg),
frv_dwarf_store (reg, stack_offset));
}
}
 
/* A function that uses frv_frame_access to transfer a group of registers to
or from the stack. ACCESSOR is passed directly to frv_frame_access, INFO
is the stack information generated by frv_stack_info, and REG_SET is the
number of the register set to transfer. */
static void
frv_frame_access_multi (frv_frame_accessor_t *accessor,
frv_stack_t *info,
int reg_set)
{
frv_stack_regs_t *regs_info;
int regno;
 
regs_info = &info->regs[reg_set];
for (regno = regs_info->first; regno <= regs_info->last; regno++)
if (info->save_p[regno])
frv_frame_access (accessor,
info->save_p[regno] == REG_SAVE_2WORDS
? gen_rtx_REG (DImode, regno)
: gen_rtx_REG (SImode, regno),
info->reg_offset[regno]);
}
 
/* Save or restore callee-saved registers that are kept outside the frame
header. The function saves the registers if OP is FRV_STORE and restores
them if OP is FRV_LOAD. INFO is the stack information generated by
frv_stack_info. */
static void
frv_frame_access_standard_regs (enum frv_stack_op op, frv_stack_t *info)
{
frv_frame_accessor_t accessor;
 
accessor.op = op;
accessor.base = stack_pointer_rtx;
accessor.base_offset = 0;
frv_frame_access_multi (&accessor, info, STACK_REGS_GPR);
frv_frame_access_multi (&accessor, info, STACK_REGS_FPR);
frv_frame_access_multi (&accessor, info, STACK_REGS_LCR);
}
 
 
/* Called after register allocation to add any instructions needed for the
prologue. Using a prologue insn is favored compared to putting all of the
instructions in the TARGET_ASM_FUNCTION_PROLOGUE target hook, since
it allows the scheduler 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 insn to prevent such
scheduling.
 
Also any insns generated here should have RTX_FRAME_RELATED_P(insn) = 1
so that the debug info generation code can handle them properly. */
void
frv_expand_prologue (void)
{
frv_stack_t *info = frv_stack_info ();
rtx sp = stack_pointer_rtx;
rtx fp = frame_pointer_rtx;
frv_frame_accessor_t accessor;
 
if (TARGET_DEBUG_STACK)
frv_debug_stack (info);
 
if (info->total_size == 0)
return;
 
/* We're interested in three areas of the frame here:
 
A: the register save area
B: the old FP
C: the header after B
 
If the frame pointer isn't used, we'll have to set up A, B and C
using the stack pointer. If the frame pointer is used, we'll access
them as follows:
 
A: set up using sp
B: set up using sp or a temporary (see below)
C: set up using fp
 
We set up B using the stack pointer if the frame is small enough.
Otherwise, it's more efficient to copy the old stack pointer into a
temporary and use that.
 
Note that it's important to make sure the prologue and epilogue use the
same registers to access A and C, since doing otherwise will confuse
the aliasing code. */
 
/* Set up ACCESSOR for accessing region B above. If the frame pointer
isn't used, the same method will serve for C. */
accessor.op = FRV_STORE;
if (frame_pointer_needed && info->total_size > 2048)
{
rtx insn;
 
accessor.base = gen_rtx_REG (Pmode, OLD_SP_REGNO);
accessor.base_offset = info->total_size;
insn = emit_insn (gen_movsi (accessor.base, sp));
}
else
{
accessor.base = stack_pointer_rtx;
accessor.base_offset = 0;
}
 
/* Allocate the stack space. */
{
rtx asm_offset = frv_frame_offset_rtx (-info->total_size);
rtx dwarf_offset = GEN_INT (-info->total_size);
 
frv_frame_insn (gen_stack_adjust (sp, sp, asm_offset),
gen_rtx_SET (Pmode,
sp,
gen_rtx_PLUS (Pmode, sp, dwarf_offset)));
}
 
/* If the frame pointer is needed, store the old one at (sp + FP_OFFSET)
and point the new one to that location. */
if (frame_pointer_needed)
{
int fp_offset = info->reg_offset[FRAME_POINTER_REGNUM];
 
/* ASM_SRC and DWARF_SRC both point to the frame header. ASM_SRC is
based on ACCESSOR.BASE but DWARF_SRC is always based on the stack
pointer. */
rtx asm_src = plus_constant (accessor.base,
fp_offset - accessor.base_offset);
rtx dwarf_src = plus_constant (sp, fp_offset);
 
/* Store the old frame pointer at (sp + FP_OFFSET). */
frv_frame_access (&accessor, fp, fp_offset);
 
/* Set up the new frame pointer. */
frv_frame_insn (gen_rtx_SET (VOIDmode, fp, asm_src),
gen_rtx_SET (VOIDmode, fp, dwarf_src));
 
/* Access region C from the frame pointer. */
accessor.base = fp;
accessor.base_offset = fp_offset;
}
 
/* Set up region C. */
frv_frame_access_multi (&accessor, info, STACK_REGS_STRUCT);
frv_frame_access_multi (&accessor, info, STACK_REGS_LR);
frv_frame_access_multi (&accessor, info, STACK_REGS_STDARG);
 
/* Set up region A. */
frv_frame_access_standard_regs (FRV_STORE, info);
 
/* If this is a varargs/stdarg function, issue a blockage to prevent the
scheduler from moving loads before the stores saving the registers. */
if (info->stdarg_size > 0)
emit_insn (gen_blockage ());
 
/* Set up pic register/small data register for this function. */
if (!TARGET_FDPIC && flag_pic && cfun->uses_pic_offset_table)
emit_insn (gen_pic_prologue (gen_rtx_REG (Pmode, PIC_REGNO),
gen_rtx_REG (Pmode, LR_REGNO),
gen_rtx_REG (SImode, OFFSET_REGNO)));
}
 
/* Under frv, all of the work is done via frv_expand_epilogue, but
this function provides a convenient place to do cleanup. */
 
static void
frv_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
frv_stack_cache = (frv_stack_t *)0;
 
/* Zap last used registers for conditional execution. */
memset (&frv_ifcvt.tmp_reg, 0, sizeof (frv_ifcvt.tmp_reg));
 
/* Release the bitmap of created insns. */
BITMAP_FREE (frv_ifcvt.scratch_insns_bitmap);
}
 
/* Called after register allocation to add any instructions needed for the
epilogue. Using an epilogue insn is favored compared to putting all of the
instructions in the TARGET_ASM_FUNCTION_PROLOGUE target hook, since
it allows the scheduler 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 insn to prevent such
scheduling. */
 
void
frv_expand_epilogue (bool emit_return)
{
frv_stack_t *info = frv_stack_info ();
rtx fp = frame_pointer_rtx;
rtx sp = stack_pointer_rtx;
rtx return_addr;
int fp_offset;
 
fp_offset = info->reg_offset[FRAME_POINTER_REGNUM];
 
/* Restore the stack pointer to its original value if alloca or the like
is used. */
if (! current_function_sp_is_unchanging)
emit_insn (gen_addsi3 (sp, fp, frv_frame_offset_rtx (-fp_offset)));
 
/* Restore the callee-saved registers that were used in this function. */
frv_frame_access_standard_regs (FRV_LOAD, info);
 
/* Set RETURN_ADDR to the address we should return to. Set it to NULL if
no return instruction should be emitted. */
if (info->save_p[LR_REGNO])
{
int lr_offset;
rtx mem;
 
/* Use the same method to access the link register's slot as we did in
the prologue. In other words, use the frame pointer if available,
otherwise use the stack pointer.
 
LR_OFFSET is the offset of the link register's slot from the start
of the frame and MEM is a memory rtx for it. */
lr_offset = info->reg_offset[LR_REGNO];
if (frame_pointer_needed)
mem = frv_frame_mem (Pmode, fp, lr_offset - fp_offset);
else
mem = frv_frame_mem (Pmode, sp, lr_offset);
 
/* Load the old link register into a GPR. */
return_addr = gen_rtx_REG (Pmode, TEMP_REGNO);
emit_insn (gen_rtx_SET (VOIDmode, return_addr, mem));
}
else
return_addr = gen_rtx_REG (Pmode, LR_REGNO);
 
/* Restore the old frame pointer. Emit a USE afterwards to make sure
the load is preserved. */
if (frame_pointer_needed)
{
emit_insn (gen_rtx_SET (VOIDmode, fp, gen_rtx_MEM (Pmode, fp)));
emit_insn (gen_rtx_USE (VOIDmode, fp));
}
 
/* Deallocate the stack frame. */
if (info->total_size != 0)
{
rtx offset = frv_frame_offset_rtx (info->total_size);
emit_insn (gen_stack_adjust (sp, sp, offset));
}
 
/* If this function uses eh_return, add the final stack adjustment now. */
if (current_function_calls_eh_return)
emit_insn (gen_stack_adjust (sp, sp, EH_RETURN_STACKADJ_RTX));
 
if (emit_return)
emit_jump_insn (gen_epilogue_return (return_addr));
else
{
rtx lr = return_addr;
 
if (REGNO (return_addr) != LR_REGNO)
{
lr = gen_rtx_REG (Pmode, LR_REGNO);
emit_move_insn (lr, return_addr);
}
 
emit_insn (gen_rtx_USE (VOIDmode, lr));
}
}
 
/* Worker function for TARGET_ASM_OUTPUT_MI_THUNK. */
 
static void
frv_asm_output_mi_thunk (FILE *file,
tree thunk_fndecl ATTRIBUTE_UNUSED,
HOST_WIDE_INT delta,
HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
tree function)
{
const char *name_func = XSTR (XEXP (DECL_RTL (function), 0), 0);
const char *name_arg0 = reg_names[FIRST_ARG_REGNUM];
const char *name_jmp = reg_names[JUMP_REGNO];
const char *parallel = (frv_issue_rate () > 1 ? ".p" : "");
 
/* Do the add using an addi if possible. */
if (IN_RANGE_P (delta, -2048, 2047))
fprintf (file, "\taddi %s,#%d,%s\n", name_arg0, (int) delta, name_arg0);
else
{
const char *const name_add = reg_names[TEMP_REGNO];
fprintf (file, "\tsethi%s #hi(" HOST_WIDE_INT_PRINT_DEC "),%s\n",
parallel, delta, name_add);
fprintf (file, "\tsetlo #lo(" HOST_WIDE_INT_PRINT_DEC "),%s\n",
delta, name_add);
fprintf (file, "\tadd %s,%s,%s\n", name_add, name_arg0, name_arg0);
}
 
if (TARGET_FDPIC)
{
const char *name_pic = reg_names[FDPIC_REGNO];
name_jmp = reg_names[FDPIC_FPTR_REGNO];
 
if (flag_pic != 1)
{
fprintf (file, "\tsethi%s #gotofffuncdeschi(", parallel);
assemble_name (file, name_func);
fprintf (file, "),%s\n", name_jmp);
 
fprintf (file, "\tsetlo #gotofffuncdesclo(");
assemble_name (file, name_func);
fprintf (file, "),%s\n", name_jmp);
 
fprintf (file, "\tldd @(%s,%s), %s\n", name_jmp, name_pic, name_jmp);
}
else
{
fprintf (file, "\tlddo @(%s,#gotofffuncdesc12(", name_pic);
assemble_name (file, name_func);
fprintf (file, "\t)), %s\n", name_jmp);
}
}
else if (!flag_pic)
{
fprintf (file, "\tsethi%s #hi(", parallel);
assemble_name (file, name_func);
fprintf (file, "),%s\n", name_jmp);
 
fprintf (file, "\tsetlo #lo(");
assemble_name (file, name_func);
fprintf (file, "),%s\n", name_jmp);
}
else
{
/* Use JUMP_REGNO as a temporary PIC register. */
const char *name_lr = reg_names[LR_REGNO];
const char *name_gppic = name_jmp;
const char *name_tmp = reg_names[TEMP_REGNO];
 
fprintf (file, "\tmovsg %s,%s\n", name_lr, name_tmp);
fprintf (file, "\tcall 1f\n");
fprintf (file, "1:\tmovsg %s,%s\n", name_lr, name_gppic);
fprintf (file, "\tmovgs %s,%s\n", name_tmp, name_lr);
fprintf (file, "\tsethi%s #gprelhi(1b),%s\n", parallel, name_tmp);
fprintf (file, "\tsetlo #gprello(1b),%s\n", name_tmp);
fprintf (file, "\tsub %s,%s,%s\n", name_gppic, name_tmp, name_gppic);
 
fprintf (file, "\tsethi%s #gprelhi(", parallel);
assemble_name (file, name_func);
fprintf (file, "),%s\n", name_tmp);
 
fprintf (file, "\tsetlo #gprello(");
assemble_name (file, name_func);
fprintf (file, "),%s\n", name_tmp);
 
fprintf (file, "\tadd %s,%s,%s\n", name_gppic, name_tmp, name_jmp);
}
 
/* Jump to the function address. */
fprintf (file, "\tjmpl @(%s,%s)\n", name_jmp, reg_names[GPR_FIRST+0]);
}
 
/* A C expression which is nonzero if a function must have and use a frame
pointer. This expression is evaluated in the reload pass. If its value is
nonzero the function will have a frame pointer.
 
The expression can in principle examine the current function and decide
according to the facts, but on most machines the constant 0 or the constant
1 suffices. Use 0 when the machine allows code to be generated with no
frame pointer, and doing so saves some time or space. Use 1 when there is
no possible advantage to avoiding a frame pointer.
 
In certain cases, the compiler does not know how to produce valid code
without a frame pointer. The compiler recognizes those cases and
automatically gives the function a frame pointer regardless of what
`FRAME_POINTER_REQUIRED' says. You don't need to worry about them.
 
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. See `FIXED_REGISTERS' for more information. */
 
/* On frv, create a frame whenever we need to create stack. */
 
int
frv_frame_pointer_required (void)
{
/* If we forgoing the usual linkage requirements, we only need
a frame pointer if the stack pointer might change. */
if (!TARGET_LINKED_FP)
return !current_function_sp_is_unchanging;
 
if (! current_function_is_leaf)
return TRUE;
 
if (get_frame_size () != 0)
return TRUE;
 
if (cfun->stdarg)
return TRUE;
 
if (!current_function_sp_is_unchanging)
return TRUE;
 
if (!TARGET_FDPIC && flag_pic && cfun->uses_pic_offset_table)
return TRUE;
 
if (profile_flag)
return TRUE;
 
if (cfun->machine->frame_needed)
return TRUE;
 
return FALSE;
}
 
/* This macro is similar to `INITIAL_FRAME_POINTER_OFFSET'. It specifies the
initial difference between the specified pair of registers. This macro must
be defined if `ELIMINABLE_REGS' is defined. */
 
/* See frv_stack_info for more details on the frv stack frame. */
 
int
frv_initial_elimination_offset (int from, int to)
{
frv_stack_t *info = frv_stack_info ();
int ret = 0;
 
if (to == STACK_POINTER_REGNUM && from == ARG_POINTER_REGNUM)
ret = info->total_size - info->pretend_size;
 
else if (to == STACK_POINTER_REGNUM && from == FRAME_POINTER_REGNUM)
ret = info->reg_offset[FRAME_POINTER_REGNUM];
 
else if (to == FRAME_POINTER_REGNUM && from == ARG_POINTER_REGNUM)
ret = (info->total_size
- info->reg_offset[FRAME_POINTER_REGNUM]
- info->pretend_size);
 
else
gcc_unreachable ();
 
if (TARGET_DEBUG_STACK)
fprintf (stderr, "Eliminate %s to %s by adding %d\n",
reg_names [from], reg_names[to], ret);
 
return ret;
}
 
/* Worker function for TARGET_SETUP_INCOMING_VARARGS. */
 
static void
frv_setup_incoming_varargs (CUMULATIVE_ARGS *cum,
enum machine_mode mode,
tree type ATTRIBUTE_UNUSED,
int *pretend_size,
int second_time)
{
if (TARGET_DEBUG_ARG)
fprintf (stderr,
"setup_vararg: words = %2d, mode = %4s, pretend_size = %d, second_time = %d\n",
*cum, GET_MODE_NAME (mode), *pretend_size, second_time);
}
 
/* Worker function for TARGET_EXPAND_BUILTIN_SAVEREGS. */
 
static rtx
frv_expand_builtin_saveregs (void)
{
int offset = UNITS_PER_WORD * FRV_NUM_ARG_REGS;
 
if (TARGET_DEBUG_ARG)
fprintf (stderr, "expand_builtin_saveregs: offset from ap = %d\n",
offset);
 
return gen_rtx_PLUS (Pmode, virtual_incoming_args_rtx, GEN_INT (- offset));
}
 
/* Expand __builtin_va_start to do the va_start macro. */
 
void
frv_expand_builtin_va_start (tree valist, rtx nextarg)
{
tree t;
int num = cfun->args_info - FIRST_ARG_REGNUM - FRV_NUM_ARG_REGS;
 
nextarg = gen_rtx_PLUS (Pmode, virtual_incoming_args_rtx,
GEN_INT (UNITS_PER_WORD * num));
 
if (TARGET_DEBUG_ARG)
{
fprintf (stderr, "va_start: args_info = %d, num = %d\n",
cfun->args_info, num);
 
debug_rtx (nextarg);
}
 
t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist,
make_tree (ptr_type_node, nextarg));
TREE_SIDE_EFFECTS (t) = 1;
 
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
 
/* Expand a block move operation, and return 1 if successful. Return 0
if we should let the compiler generate normal code.
 
operands[0] is the destination
operands[1] is the source
operands[2] is the length
operands[3] is the alignment */
 
/* Maximum number of loads to do before doing the stores */
#ifndef MAX_MOVE_REG
#define MAX_MOVE_REG 4
#endif
 
/* Maximum number of total loads to do. */
#ifndef TOTAL_MOVE_REG
#define TOTAL_MOVE_REG 8
#endif
 
int
frv_expand_block_move (rtx operands[])
{
rtx orig_dest = operands[0];
rtx orig_src = operands[1];
rtx bytes_rtx = operands[2];
rtx align_rtx = operands[3];
int constp = (GET_CODE (bytes_rtx) == CONST_INT);
int align;
int bytes;
int offset;
int num_reg;
int i;
rtx src_reg;
rtx dest_reg;
rtx src_addr;
rtx dest_addr;
rtx src_mem;
rtx dest_mem;
rtx tmp_reg;
rtx stores[MAX_MOVE_REG];
int move_bytes;
enum machine_mode mode;
 
/* If this is not a fixed size move, just call memcpy. */
if (! constp)
return FALSE;
 
/* This should be a fixed size alignment. */
gcc_assert (GET_CODE (align_rtx) == CONST_INT);
 
align = INTVAL (align_rtx);
 
/* Anything to move? */
bytes = INTVAL (bytes_rtx);
if (bytes <= 0)
return TRUE;
 
/* Don't support real large moves. */
if (bytes > TOTAL_MOVE_REG*align)
return FALSE;
 
/* Move the address into scratch registers. */
dest_reg = copy_addr_to_reg (XEXP (orig_dest, 0));
src_reg = copy_addr_to_reg (XEXP (orig_src, 0));
 
num_reg = offset = 0;
for ( ; bytes > 0; (bytes -= move_bytes), (offset += move_bytes))
{
/* Calculate the correct offset for src/dest. */
if (offset == 0)
{
src_addr = src_reg;
dest_addr = dest_reg;
}
else
{
src_addr = plus_constant (src_reg, offset);
dest_addr = plus_constant (dest_reg, offset);
}
 
/* Generate the appropriate load and store, saving the stores
for later. */
if (bytes >= 4 && align >= 4)
mode = SImode;
else if (bytes >= 2 && align >= 2)
mode = HImode;
else
mode = QImode;
 
move_bytes = GET_MODE_SIZE (mode);
tmp_reg = gen_reg_rtx (mode);
src_mem = change_address (orig_src, mode, src_addr);
dest_mem = change_address (orig_dest, mode, dest_addr);
emit_insn (gen_rtx_SET (VOIDmode, tmp_reg, src_mem));
stores[num_reg++] = gen_rtx_SET (VOIDmode, dest_mem, tmp_reg);
 
if (num_reg >= MAX_MOVE_REG)
{
for (i = 0; i < num_reg; i++)
emit_insn (stores[i]);
num_reg = 0;
}
}
 
for (i = 0; i < num_reg; i++)
emit_insn (stores[i]);
 
return TRUE;
}
 
/* Expand a block clear operation, and return 1 if successful. Return 0
if we should let the compiler generate normal code.
 
operands[0] is the destination
operands[1] is the length
operands[3] is the alignment */
 
int
frv_expand_block_clear (rtx operands[])
{
rtx orig_dest = operands[0];
rtx bytes_rtx = operands[1];
rtx align_rtx = operands[3];
int constp = (GET_CODE (bytes_rtx) == CONST_INT);
int align;
int bytes;
int offset;
int num_reg;
rtx dest_reg;
rtx dest_addr;
rtx dest_mem;
int clear_bytes;
enum machine_mode mode;
 
/* If this is not a fixed size move, just call memcpy. */
if (! constp)
return FALSE;
 
/* This should be a fixed size alignment. */
gcc_assert (GET_CODE (align_rtx) == CONST_INT);
 
align = INTVAL (align_rtx);
 
/* Anything to move? */
bytes = INTVAL (bytes_rtx);
if (bytes <= 0)
return TRUE;
 
/* Don't support real large clears. */
if (bytes > TOTAL_MOVE_REG*align)
return FALSE;
 
/* Move the address into a scratch register. */
dest_reg = copy_addr_to_reg (XEXP (orig_dest, 0));
 
num_reg = offset = 0;
for ( ; bytes > 0; (bytes -= clear_bytes), (offset += clear_bytes))
{
/* Calculate the correct offset for src/dest. */
dest_addr = ((offset == 0)
? dest_reg
: plus_constant (dest_reg, offset));
 
/* Generate the appropriate store of gr0. */
if (bytes >= 4 && align >= 4)
mode = SImode;
else if (bytes >= 2 && align >= 2)
mode = HImode;
else
mode = QImode;
 
clear_bytes = GET_MODE_SIZE (mode);
dest_mem = change_address (orig_dest, mode, dest_addr);
emit_insn (gen_rtx_SET (VOIDmode, dest_mem, const0_rtx));
}
 
return TRUE;
}
 
/* The following variable is used to output modifiers of assembler
code of the current output insn. */
 
static rtx *frv_insn_operands;
 
/* The following function is used to add assembler insn code suffix .p
if it is necessary. */
 
const char *
frv_asm_output_opcode (FILE *f, const char *ptr)
{
int c;
 
if (frv_insn_packing_flag <= 0)
return ptr;
 
for (; *ptr && *ptr != ' ' && *ptr != '\t';)
{
c = *ptr++;
if (c == '%' && ((*ptr >= 'a' && *ptr <= 'z')
|| (*ptr >= 'A' && *ptr <= 'Z')))
{
int letter = *ptr++;
 
c = atoi (ptr);
frv_print_operand (f, frv_insn_operands [c], letter);
while ((c = *ptr) >= '0' && c <= '9')
ptr++;
}
else
fputc (c, f);
}
 
fprintf (f, ".p");
 
return ptr;
}
 
/* Set up the packing bit for the current output insn. Note that this
function is not called for asm insns. */
 
void
frv_final_prescan_insn (rtx insn, rtx *opvec,
int noperands ATTRIBUTE_UNUSED)
{
if (INSN_P (insn))
{
if (frv_insn_packing_flag >= 0)
{
frv_insn_operands = opvec;
frv_insn_packing_flag = PACKING_FLAG_P (insn);
}
else if (recog_memoized (insn) >= 0
&& get_attr_acc_group (insn) == ACC_GROUP_ODD)
/* Packing optimizations have been disabled, but INSN can only
be issued in M1. Insert an mnop in M0. */
fprintf (asm_out_file, "\tmnop.p\n");
}
}
 
 
/* A C expression whose value is RTL representing the address in a stack frame
where the pointer to the caller's frame is stored. Assume that FRAMEADDR is
an RTL expression for the address of the stack frame itself.
 
If you don't define this macro, the default is to return the value of
FRAMEADDR--that is, the stack frame address is also the address of the stack
word that points to the previous frame. */
 
/* The default is correct, but we need to make sure the frame gets created. */
rtx
frv_dynamic_chain_address (rtx frame)
{
cfun->machine->frame_needed = 1;
return frame;
}
 
 
/* A C expression whose value is RTL representing the value of the return
address for the frame COUNT steps up from the current frame, after the
prologue. FRAMEADDR is the frame pointer of the COUNT frame, or the frame
pointer of the COUNT - 1 frame if `RETURN_ADDR_IN_PREVIOUS_FRAME' is
defined.
 
The value of the expression must always be the correct address when COUNT is
zero, but may be `NULL_RTX' if there is not way to determine the return
address of other frames. */
 
rtx
frv_return_addr_rtx (int count, rtx frame)
{
if (count != 0)
return const0_rtx;
cfun->machine->frame_needed = 1;
return gen_rtx_MEM (Pmode, plus_constant (frame, 8));
}
 
/* Given a memory reference MEMREF, interpret the referenced memory as
an array of MODE values, and return a reference to the element
specified by INDEX. Assume that any pre-modification implicit in
MEMREF has already happened.
 
MEMREF must be a legitimate operand for modes larger than SImode.
GO_IF_LEGITIMATE_ADDRESS forbids register+register addresses, which
this function cannot handle. */
rtx
frv_index_memory (rtx memref, enum machine_mode mode, int index)
{
rtx base = XEXP (memref, 0);
if (GET_CODE (base) == PRE_MODIFY)
base = XEXP (base, 0);
return change_address (memref, mode,
plus_constant (base, index * GET_MODE_SIZE (mode)));
}
 
/* Print a memory address as an operand to reference that memory location. */
void
frv_print_operand_address (FILE * stream, rtx x)
{
if (GET_CODE (x) == MEM)
x = XEXP (x, 0);
 
switch (GET_CODE (x))
{
case REG:
fputs (reg_names [ REGNO (x)], stream);
return;
 
case CONST_INT:
fprintf (stream, "%ld", (long) INTVAL (x));
return;
 
case SYMBOL_REF:
assemble_name (stream, XSTR (x, 0));
return;
 
case LABEL_REF:
case CONST:
output_addr_const (stream, x);
return;
 
default:
break;
}
 
fatal_insn ("bad insn to frv_print_operand_address:", x);
}
 
static void
frv_print_operand_memory_reference_reg (FILE * stream, rtx x)
{
int regno = true_regnum (x);
if (GPR_P (regno))
fputs (reg_names[regno], stream);
else
fatal_insn ("bad register to frv_print_operand_memory_reference_reg:", x);
}
 
/* Print a memory reference suitable for the ld/st instructions. */
 
static void
frv_print_operand_memory_reference (FILE * stream, rtx x, int addr_offset)
{
struct frv_unspec unspec;
rtx x0 = NULL_RTX;
rtx x1 = NULL_RTX;
 
switch (GET_CODE (x))
{
case SUBREG:
case REG:
x0 = x;
break;
 
case PRE_MODIFY: /* (pre_modify (reg) (plus (reg) (reg))) */
x0 = XEXP (x, 0);
x1 = XEXP (XEXP (x, 1), 1);
break;
 
case CONST_INT:
x1 = x;
break;
 
case PLUS:
x0 = XEXP (x, 0);
x1 = XEXP (x, 1);
if (GET_CODE (x0) == CONST_INT)
{
x0 = XEXP (x, 1);
x1 = XEXP (x, 0);
}
break;
 
default:
fatal_insn ("bad insn to frv_print_operand_memory_reference:", x);
break;
 
}
 
if (addr_offset)
{
if (!x1)
x1 = const0_rtx;
else if (GET_CODE (x1) != CONST_INT)
fatal_insn ("bad insn to frv_print_operand_memory_reference:", x);
}
 
fputs ("@(", stream);
if (!x0)
fputs (reg_names[GPR_R0], stream);
else if (GET_CODE (x0) == REG || GET_CODE (x0) == SUBREG)
frv_print_operand_memory_reference_reg (stream, x0);
else
fatal_insn ("bad insn to frv_print_operand_memory_reference:", x);
 
fputs (",", stream);
if (!x1)
fputs (reg_names [GPR_R0], stream);
 
else
{
switch (GET_CODE (x1))
{
case SUBREG:
case REG:
frv_print_operand_memory_reference_reg (stream, x1);
break;
 
case CONST_INT:
fprintf (stream, "%ld", (long) (INTVAL (x1) + addr_offset));
break;
 
case CONST:
if (!frv_const_unspec_p (x1, &unspec))
fatal_insn ("bad insn to frv_print_operand_memory_reference:", x1);
frv_output_const_unspec (stream, &unspec);
break;
 
default:
fatal_insn ("bad insn to frv_print_operand_memory_reference:", x);
}
}
 
fputs (")", stream);
}
 
/* Return 2 for likely branches and 0 for non-likely branches */
 
#define FRV_JUMP_LIKELY 2
#define FRV_JUMP_NOT_LIKELY 0
 
static int
frv_print_operand_jump_hint (rtx insn)
{
rtx note;
rtx labelref;
int ret;
HOST_WIDE_INT prob = -1;
enum { UNKNOWN, BACKWARD, FORWARD } jump_type = UNKNOWN;
 
gcc_assert (GET_CODE (insn) == JUMP_INSN);
 
/* Assume any non-conditional jump is likely. */
if (! any_condjump_p (insn))
ret = FRV_JUMP_LIKELY;
 
else
{
labelref = condjump_label (insn);
if (labelref)
{
rtx label = XEXP (labelref, 0);
jump_type = (insn_current_address > INSN_ADDRESSES (INSN_UID (label))
? BACKWARD
: FORWARD);
}
 
note = find_reg_note (insn, REG_BR_PROB, 0);
if (!note)
ret = ((jump_type == BACKWARD) ? FRV_JUMP_LIKELY : FRV_JUMP_NOT_LIKELY);
 
else
{
prob = INTVAL (XEXP (note, 0));
ret = ((prob >= (REG_BR_PROB_BASE / 2))
? FRV_JUMP_LIKELY
: FRV_JUMP_NOT_LIKELY);
}
}
 
#if 0
if (TARGET_DEBUG)
{
char *direction;
 
switch (jump_type)
{
default:
case UNKNOWN: direction = "unknown jump direction"; break;
case BACKWARD: direction = "jump backward"; break;
case FORWARD: direction = "jump forward"; break;
}
 
fprintf (stderr,
"%s: uid %ld, %s, probability = %ld, max prob. = %ld, hint = %d\n",
IDENTIFIER_POINTER (DECL_NAME (current_function_decl)),
(long)INSN_UID (insn), direction, (long)prob,
(long)REG_BR_PROB_BASE, ret);
}
#endif
 
return ret;
}
 
/* Return the comparison operator to use for CODE given that the ICC
register is OP0. */
 
static const char *
comparison_string (enum rtx_code code, rtx op0)
{
bool is_nz_p = GET_MODE (op0) == CC_NZmode;
switch (code)
{
default: output_operand_lossage ("bad condition code");
case EQ: return "eq";
case NE: return "ne";
case LT: return is_nz_p ? "n" : "lt";
case LE: return "le";
case GT: return "gt";
case GE: return is_nz_p ? "p" : "ge";
case LTU: return is_nz_p ? "no" : "c";
case LEU: return is_nz_p ? "eq" : "ls";
case GTU: return is_nz_p ? "ne" : "hi";
case GEU: return is_nz_p ? "ra" : "nc";
}
}
 
/* Print an operand to an assembler instruction.
 
`%' followed by a letter and a digit says to output an operand in an
alternate fashion. Four letters have standard, built-in meanings described
below. The machine description macro `PRINT_OPERAND' can define additional
letters with nonstandard meanings.
 
`%cDIGIT' can be used to substitute an operand that is a constant value
without the syntax that normally indicates an immediate operand.
 
`%nDIGIT' is like `%cDIGIT' except that the value of the constant is negated
before printing.
 
`%aDIGIT' can be used to substitute an operand as if it were a memory
reference, with the actual operand treated as the address. This may be
useful when outputting a "load address" instruction, because often the
assembler syntax for such an instruction requires you to write the operand
as if it were a memory reference.
 
`%lDIGIT' is used to substitute a `label_ref' into a jump instruction.
 
`%=' outputs a number which is unique to each instruction in the entire
compilation. This is useful for making local labels to be referred to more
than once in a single template that generates multiple assembler
instructions.
 
`%' followed by a punctuation character specifies a substitution that does
not use an operand. Only one case is standard: `%%' outputs a `%' into the
assembler code. Other nonstandard cases can be defined in the
`PRINT_OPERAND' macro. You must also define which punctuation characters
are valid with the `PRINT_OPERAND_PUNCT_VALID_P' macro. */
 
void
frv_print_operand (FILE * file, rtx x, int code)
{
struct frv_unspec unspec;
HOST_WIDE_INT value;
int offset;
 
if (code != 0 && !isalpha (code))
value = 0;
 
else if (GET_CODE (x) == CONST_INT)
value = INTVAL (x);
 
else if (GET_CODE (x) == CONST_DOUBLE)
{
if (GET_MODE (x) == SFmode)
{
REAL_VALUE_TYPE rv;
long l;
 
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_SINGLE (rv, l);
value = l;
}
 
else if (GET_MODE (x) == VOIDmode)
value = CONST_DOUBLE_LOW (x);
 
else
fatal_insn ("bad insn in frv_print_operand, bad const_double", x);
}
 
else
value = 0;
 
switch (code)
{
 
case '.':
/* Output r0. */
fputs (reg_names[GPR_R0], file);
break;
 
case '#':
fprintf (file, "%d", frv_print_operand_jump_hint (current_output_insn));
break;
 
case '@':
/* Output small data area base register (gr16). */
fputs (reg_names[SDA_BASE_REG], file);
break;
 
case '~':
/* Output pic register (gr17). */
fputs (reg_names[PIC_REGNO], file);
break;
 
case '*':
/* Output the temporary integer CCR register. */
fputs (reg_names[ICR_TEMP], file);
break;
 
case '&':
/* Output the temporary integer CC register. */
fputs (reg_names[ICC_TEMP], file);
break;
 
/* case 'a': print an address. */
 
case 'C':
/* Print appropriate test for integer branch false operation. */
fputs (comparison_string (reverse_condition (GET_CODE (x)),
XEXP (x, 0)), file);
break;
 
case 'c':
/* Print appropriate test for integer branch true operation. */
fputs (comparison_string (GET_CODE (x), XEXP (x, 0)), file);
break;
 
case 'e':
/* Print 1 for a NE and 0 for an EQ to give the final argument
for a conditional instruction. */
if (GET_CODE (x) == NE)
fputs ("1", file);
 
else if (GET_CODE (x) == EQ)
fputs ("0", file);
 
else
fatal_insn ("bad insn to frv_print_operand, 'e' modifier:", x);
break;
 
case 'F':
/* Print appropriate test for floating point branch false operation. */
switch (GET_CODE (x))
{
default:
fatal_insn ("bad insn to frv_print_operand, 'F' modifier:", x);
 
case EQ: fputs ("ne", file); break;
case NE: fputs ("eq", file); break;
case LT: fputs ("uge", file); break;
case LE: fputs ("ug", file); break;
case GT: fputs ("ule", file); break;
case GE: fputs ("ul", file); break;
}
break;
 
case 'f':
/* Print appropriate test for floating point branch true operation. */
switch (GET_CODE (x))
{
default:
fatal_insn ("bad insn to frv_print_operand, 'f' modifier:", x);
 
case EQ: fputs ("eq", file); break;
case NE: fputs ("ne", file); break;
case LT: fputs ("lt", file); break;
case LE: fputs ("le", file); break;
case GT: fputs ("gt", file); break;
case GE: fputs ("ge", file); break;
}
break;
 
case 'g':
/* Print appropriate GOT function. */
if (GET_CODE (x) != CONST_INT)
fatal_insn ("bad insn to frv_print_operand, 'g' modifier:", x);
fputs (unspec_got_name (INTVAL (x)), file);
break;
 
case 'I':
/* Print 'i' if the operand is a constant, or is a memory reference that
adds a constant. */
if (GET_CODE (x) == MEM)
x = ((GET_CODE (XEXP (x, 0)) == PLUS)
? XEXP (XEXP (x, 0), 1)
: XEXP (x, 0));
else if (GET_CODE (x) == PLUS)
x = XEXP (x, 1);
 
switch (GET_CODE (x))
{
default:
break;
 
case CONST_INT:
case SYMBOL_REF:
case CONST:
fputs ("i", file);
break;
}
break;
 
case 'i':
/* For jump instructions, print 'i' if the operand is a constant or
is an expression that adds a constant. */
if (GET_CODE (x) == CONST_INT)
fputs ("i", file);
 
else
{
if (GET_CODE (x) == CONST_INT
|| (GET_CODE (x) == PLUS
&& (GET_CODE (XEXP (x, 1)) == CONST_INT
|| GET_CODE (XEXP (x, 0)) == CONST_INT)))
fputs ("i", file);
}
break;
 
case 'L':
/* Print the lower register of a double word register pair */
if (GET_CODE (x) == REG)
fputs (reg_names[ REGNO (x)+1 ], file);
else
fatal_insn ("bad insn to frv_print_operand, 'L' modifier:", x);
break;
 
/* case 'l': print a LABEL_REF. */
 
case 'M':
case 'N':
/* Print a memory reference for ld/st/jmp, %N prints a memory reference
for the second word of double memory operations. */
offset = (code == 'M') ? 0 : UNITS_PER_WORD;
switch (GET_CODE (x))
{
default:
fatal_insn ("bad insn to frv_print_operand, 'M/N' modifier:", x);
 
case MEM:
frv_print_operand_memory_reference (file, XEXP (x, 0), offset);
break;
 
case REG:
case SUBREG:
case CONST_INT:
case PLUS:
case SYMBOL_REF:
frv_print_operand_memory_reference (file, x, offset);
break;
}
break;
 
case 'O':
/* Print the opcode of a command. */
switch (GET_CODE (x))
{
default:
fatal_insn ("bad insn to frv_print_operand, 'O' modifier:", x);
 
case PLUS: fputs ("add", file); break;
case MINUS: fputs ("sub", file); break;
case AND: fputs ("and", file); break;
case IOR: fputs ("or", file); break;
case XOR: fputs ("xor", file); break;
case ASHIFT: fputs ("sll", file); break;
case ASHIFTRT: fputs ("sra", file); break;
case LSHIFTRT: fputs ("srl", file); break;
}
break;
 
/* case 'n': negate and print a constant int. */
 
case 'P':
/* Print PIC label using operand as the number. */
if (GET_CODE (x) != CONST_INT)
fatal_insn ("bad insn to frv_print_operand, P modifier:", x);
 
fprintf (file, ".LCF%ld", (long)INTVAL (x));
break;
 
case 'U':
/* Print 'u' if the operand is a update load/store. */
if (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == PRE_MODIFY)
fputs ("u", file);
break;
 
case 'z':
/* If value is 0, print gr0, otherwise it must be a register. */
if (GET_CODE (x) == CONST_INT && INTVAL (x) == 0)
fputs (reg_names[GPR_R0], file);
 
else if (GET_CODE (x) == REG)
fputs (reg_names [REGNO (x)], file);
 
else
fatal_insn ("bad insn in frv_print_operand, z case", x);
break;
 
case 'x':
/* Print constant in hex. */
if (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE)
{
fprintf (file, "%s0x%.4lx", IMMEDIATE_PREFIX, (long) value);
break;
}
 
/* Fall through. */
 
case '\0':
if (GET_CODE (x) == REG)
fputs (reg_names [REGNO (x)], file);
 
else if (GET_CODE (x) == CONST_INT
|| GET_CODE (x) == CONST_DOUBLE)
fprintf (file, "%s%ld", IMMEDIATE_PREFIX, (long) value);
 
else if (frv_const_unspec_p (x, &unspec))
frv_output_const_unspec (file, &unspec);
 
else if (GET_CODE (x) == MEM)
frv_print_operand_address (file, XEXP (x, 0));
 
else if (CONSTANT_ADDRESS_P (x))
frv_print_operand_address (file, x);
 
else
fatal_insn ("bad insn in frv_print_operand, 0 case", x);
 
break;
 
default:
fatal_insn ("frv_print_operand: unknown code", x);
break;
}
 
return;
}
 
/* A C statement (sans semicolon) for initializing the variable CUM for the
state at the beginning of the argument list. The variable has type
`CUMULATIVE_ARGS'. The value of FNTYPE is the tree node for the data type
of the function which will receive the args, or 0 if the args are to a
compiler support library function. The value of INDIRECT is nonzero when
processing an indirect call, for example a call through a function pointer.
The value of INDIRECT is zero for a call to an explicitly named function, a
library function call, or when `INIT_CUMULATIVE_ARGS' is used to find
arguments for the function being compiled.
 
When processing a call to a compiler support library function, LIBNAME
identifies which one. It is a `symbol_ref' rtx which contains the name of
the function, as a string. LIBNAME is 0 when an ordinary C function call is
being processed. Thus, each time this macro is called, either LIBNAME or
FNTYPE is nonzero, but never both of them at once. */
 
void
frv_init_cumulative_args (CUMULATIVE_ARGS *cum,
tree fntype,
rtx libname,
tree fndecl,
int incoming)
{
*cum = FIRST_ARG_REGNUM;
 
if (TARGET_DEBUG_ARG)
{
fprintf (stderr, "\ninit_cumulative_args:");
if (!fndecl && fntype)
fputs (" indirect", stderr);
 
if (incoming)
fputs (" incoming", stderr);
 
if (fntype)
{
tree ret_type = TREE_TYPE (fntype);
fprintf (stderr, " return=%s,",
tree_code_name[ (int)TREE_CODE (ret_type) ]);
}
 
if (libname && GET_CODE (libname) == SYMBOL_REF)
fprintf (stderr, " libname=%s", XSTR (libname, 0));
 
if (cfun->returns_struct)
fprintf (stderr, " return-struct");
 
putc ('\n', stderr);
}
}
 
/* Return true if we should pass an argument on the stack rather than
in registers. */
 
static bool
frv_must_pass_in_stack (enum machine_mode mode, tree type)
{
if (mode == BLKmode)
return true;
if (type == NULL)
return false;
return AGGREGATE_TYPE_P (type);
}
 
/* If defined, a C expression that gives the alignment boundary, in bits, of an
argument with the specified mode and type. If it is not defined,
`PARM_BOUNDARY' is used for all arguments. */
 
int
frv_function_arg_boundary (enum machine_mode mode ATTRIBUTE_UNUSED,
tree type ATTRIBUTE_UNUSED)
{
return BITS_PER_WORD;
}
 
rtx
frv_function_arg (CUMULATIVE_ARGS *cum,
enum machine_mode mode,
tree type ATTRIBUTE_UNUSED,
int named,
int incoming ATTRIBUTE_UNUSED)
{
enum machine_mode xmode = (mode == BLKmode) ? SImode : mode;
int arg_num = *cum;
rtx ret;
const char *debstr;
 
/* Return a marker for use in the call instruction. */
if (xmode == VOIDmode)
{
ret = const0_rtx;
debstr = "<0>";
}
 
else if (arg_num <= LAST_ARG_REGNUM)
{
ret = gen_rtx_REG (xmode, arg_num);
debstr = reg_names[arg_num];
}
 
else
{
ret = NULL_RTX;
debstr = "memory";
}
 
if (TARGET_DEBUG_ARG)
fprintf (stderr,
"function_arg: words = %2d, mode = %4s, named = %d, size = %3d, arg = %s\n",
arg_num, GET_MODE_NAME (mode), named, GET_MODE_SIZE (mode), debstr);
 
return ret;
}
 
/* A C statement (sans semicolon) to update the summarizer variable CUM to
advance past an argument in the argument list. The values MODE, TYPE and
NAMED describe that argument. Once this is done, the variable CUM is
suitable for analyzing the *following* argument with `FUNCTION_ARG', etc.
 
This macro need not do anything if the argument in question was passed on
the stack. The compiler knows how to track the amount of stack space used
for arguments without any special help. */
 
void
frv_function_arg_advance (CUMULATIVE_ARGS *cum,
enum machine_mode mode,
tree type ATTRIBUTE_UNUSED,
int named)
{
enum machine_mode xmode = (mode == BLKmode) ? SImode : mode;
int bytes = GET_MODE_SIZE (xmode);
int words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
int arg_num = *cum;
 
*cum = arg_num + words;
 
if (TARGET_DEBUG_ARG)
fprintf (stderr,
"function_adv: words = %2d, mode = %4s, named = %d, size = %3d\n",
arg_num, GET_MODE_NAME (mode), named, words * UNITS_PER_WORD);
}
 
/* A C expression for the number of words, at the beginning of an argument,
must be put in registers. The value must be zero for arguments that are
passed entirely in registers or that are entirely pushed on the stack.
 
On some machines, certain arguments must be passed partially in registers
and partially in memory. On these machines, typically the first N words of
arguments are passed in registers, and the rest on the stack. If a
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 pushed.
This macro tells the compiler when this occurs, and how many of the words
should go in registers.
 
`FUNCTION_ARG' for these arguments should return the first register to be
used by the caller for this argument; likewise `FUNCTION_INCOMING_ARG', for
the called function. */
 
static int
frv_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type ATTRIBUTE_UNUSED, bool named ATTRIBUTE_UNUSED)
{
enum machine_mode xmode = (mode == BLKmode) ? SImode : mode;
int bytes = GET_MODE_SIZE (xmode);
int words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
int arg_num = *cum;
int ret;
 
ret = ((arg_num <= LAST_ARG_REGNUM && arg_num + words > LAST_ARG_REGNUM+1)
? LAST_ARG_REGNUM - arg_num + 1
: 0);
ret *= UNITS_PER_WORD;
 
if (TARGET_DEBUG_ARG && ret)
fprintf (stderr, "frv_arg_partial_bytes: %d\n", ret);
 
return ret;
}
 
/* Return true if a register is ok to use as a base or index register. */
 
static FRV_INLINE int
frv_regno_ok_for_base_p (int regno, int strict_p)
{
if (GPR_P (regno))
return TRUE;
 
if (strict_p)
return (reg_renumber[regno] >= 0 && GPR_P (reg_renumber[regno]));
 
if (regno == ARG_POINTER_REGNUM)
return TRUE;
 
return (regno >= FIRST_PSEUDO_REGISTER);
}
 
/* A C compound statement with a conditional `goto LABEL;' executed if X (an
RTX) is a legitimate memory address on the target machine for a memory
operand of mode MODE.
 
It usually pays to define several simpler macros to serve as subroutines for
this one. Otherwise it may be too complicated to understand.
 
This macro must exist in two variants: a strict variant and a non-strict
one. 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
considered a memory reference. In contexts where some kind of register is
required, a pseudo-register with no hard register must be rejected.
 
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
required.
 
Compiler source files that want to use the strict variant of this macro
define the macro `REG_OK_STRICT'. You should use an `#ifdef REG_OK_STRICT'
conditional to define the strict variant in that case and the non-strict
variant otherwise.
 
Subroutines to check for acceptable registers for various purposes (one for
base registers, one for index registers, and so on) are typically among the
subroutines used to define `GO_IF_LEGITIMATE_ADDRESS'. Then only these
subroutine macros need have two variants; the higher levels of macros may be
the same whether strict or not.
 
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, there is no need to recognize such sums specifically as
legitimate addresses. Normally you would simply recognize any `const' as
legitimate.
 
Usually `PRINT_OPERAND_ADDRESS' is not prepared to handle constant sums that
are not marked with `const'. It assumes that a naked `plus' indicates
indexing. If so, then you *must* reject such naked constant sums as
illegitimate addresses, so that none of them will be given to
`PRINT_OPERAND_ADDRESS'.
 
On some machines, whether a symbolic address is legitimate depends on the
section that the address refers to. On these machines, define the macro
`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 to look
inside it to find the `symbol_ref' in order to determine the section.
 
The best way to modify the name string is by adding text to the beginning,
with suitable punctuation to prevent any ambiguity. Allocate the new name
in `saveable_obstack'. You will have to modify `ASM_OUTPUT_LABELREF' to
remove and decode the added text and output the name accordingly, and define
`(* targetm.strip_name_encoding)' to access the original name string.
 
You can check the information stored here into the `symbol_ref' in the
definitions of the macros `GO_IF_LEGITIMATE_ADDRESS' and
`PRINT_OPERAND_ADDRESS'. */
 
int
frv_legitimate_address_p (enum machine_mode mode,
rtx x,
int strict_p,
int condexec_p,
int allow_double_reg_p)
{
rtx x0, x1;
int ret = 0;
HOST_WIDE_INT value;
unsigned regno0;
 
if (FRV_SYMBOL_REF_TLS_P (x))
return 0;
 
switch (GET_CODE (x))
{
default:
break;
 
case SUBREG:
x = SUBREG_REG (x);
if (GET_CODE (x) != REG)
break;
 
/* Fall through. */
 
case REG:
ret = frv_regno_ok_for_base_p (REGNO (x), strict_p);
break;
 
case PRE_MODIFY:
x0 = XEXP (x, 0);
x1 = XEXP (x, 1);
if (GET_CODE (x0) != REG
|| ! frv_regno_ok_for_base_p (REGNO (x0), strict_p)
|| GET_CODE (x1) != PLUS
|| ! rtx_equal_p (x0, XEXP (x1, 0))
|| GET_CODE (XEXP (x1, 1)) != REG
|| ! frv_regno_ok_for_base_p (REGNO (XEXP (x1, 1)), strict_p))
break;
 
ret = 1;
break;
 
case CONST_INT:
/* 12 bit immediate */
if (condexec_p)
ret = FALSE;
else
{
ret = IN_RANGE_P (INTVAL (x), -2048, 2047);
 
/* If we can't use load/store double operations, make sure we can
address the second word. */
if (ret && GET_MODE_SIZE (mode) > UNITS_PER_WORD)
ret = IN_RANGE_P (INTVAL (x) + GET_MODE_SIZE (mode) - 1,
-2048, 2047);
}
break;
 
case PLUS:
x0 = XEXP (x, 0);
x1 = XEXP (x, 1);
 
if (GET_CODE (x0) == SUBREG)
x0 = SUBREG_REG (x0);
 
if (GET_CODE (x0) != REG)
break;
 
regno0 = REGNO (x0);
if (!frv_regno_ok_for_base_p (regno0, strict_p))
break;
 
switch (GET_CODE (x1))
{
default:
break;
 
case SUBREG:
x1 = SUBREG_REG (x1);
if (GET_CODE (x1) != REG)
break;
 
/* Fall through. */
 
case REG:
/* Do not allow reg+reg addressing for modes > 1 word if we
can't depend on having move double instructions. */
if (!allow_double_reg_p && GET_MODE_SIZE (mode) > UNITS_PER_WORD)
ret = FALSE;
else
ret = frv_regno_ok_for_base_p (REGNO (x1), strict_p);
break;
 
case CONST_INT:
/* 12 bit immediate */
if (condexec_p)
ret = FALSE;
else
{
value = INTVAL (x1);
ret = IN_RANGE_P (value, -2048, 2047);
 
/* If we can't use load/store double operations, make sure we can
address the second word. */
if (ret && GET_MODE_SIZE (mode) > UNITS_PER_WORD)
ret = IN_RANGE_P (value + GET_MODE_SIZE (mode) - 1, -2048, 2047);
}
break;
 
case CONST:
if (!condexec_p && got12_operand (x1, VOIDmode))
ret = TRUE;
break;
 
}
break;
}
 
if (TARGET_DEBUG_ADDR)
{
fprintf (stderr, "\n========== GO_IF_LEGITIMATE_ADDRESS, mode = %s, result = %d, addresses are %sstrict%s\n",
GET_MODE_NAME (mode), ret, (strict_p) ? "" : "not ",
(condexec_p) ? ", inside conditional code" : "");
debug_rtx (x);
}
 
return ret;
}
 
/* Given an ADDR, generate code to inline the PLT. */
static rtx
gen_inlined_tls_plt (rtx addr)
{
rtx retval, dest;
rtx picreg = get_hard_reg_initial_val (Pmode, FDPIC_REG);
 
 
dest = gen_reg_rtx (DImode);
 
if (flag_pic == 1)
{
/*
-fpic version:
 
lddi.p @(gr15, #gottlsdesc12(ADDR)), gr8
calll #gettlsoff(ADDR)@(gr8, gr0)
*/
emit_insn (gen_tls_lddi (dest, addr, picreg));
}
else
{
/*
-fPIC version:
 
sethi.p #gottlsdeschi(ADDR), gr8
setlo #gottlsdesclo(ADDR), gr8
ldd #tlsdesc(ADDR)@(gr15, gr8), gr8
calll #gettlsoff(ADDR)@(gr8, gr0)
*/
rtx reguse = gen_reg_rtx (Pmode);
emit_insn (gen_tlsoff_hilo (reguse, addr, GEN_INT (R_FRV_GOTTLSDESCHI)));
emit_insn (gen_tls_tlsdesc_ldd (dest, picreg, reguse, addr));
}
 
retval = gen_reg_rtx (Pmode);
emit_insn (gen_tls_indirect_call (retval, addr, dest, picreg));
return retval;
}
 
/* Emit a TLSMOFF or TLSMOFF12 offset, depending on -mTLS. Returns
the destination address. */
static rtx
gen_tlsmoff (rtx addr, rtx reg)
{
rtx dest = gen_reg_rtx (Pmode);
 
if (TARGET_BIG_TLS)
{
/* sethi.p #tlsmoffhi(x), grA
setlo #tlsmofflo(x), grA
*/
dest = gen_reg_rtx (Pmode);
emit_insn (gen_tlsoff_hilo (dest, addr,
GEN_INT (R_FRV_TLSMOFFHI)));
dest = gen_rtx_PLUS (Pmode, dest, reg);
}
else
{
/* addi grB, #tlsmoff12(x), grC
-or-
ld/st @(grB, #tlsmoff12(x)), grC
*/
dest = gen_reg_rtx (Pmode);
emit_insn (gen_symGOTOFF2reg_i (dest, addr, reg,
GEN_INT (R_FRV_TLSMOFF12)));
}
return dest;
}
 
/* Generate code for a TLS address. */
static rtx
frv_legitimize_tls_address (rtx addr, enum tls_model model)
{
rtx dest, tp = gen_rtx_REG (Pmode, 29);
rtx picreg = get_hard_reg_initial_val (Pmode, 15);
 
switch (model)
{
case TLS_MODEL_INITIAL_EXEC:
if (flag_pic == 1)
{
/* -fpic version.
ldi @(gr15, #gottlsoff12(x)), gr5
*/
dest = gen_reg_rtx (Pmode);
emit_insn (gen_tls_load_gottlsoff12 (dest, addr, picreg));
dest = gen_rtx_PLUS (Pmode, tp, dest);
}
else
{
/* -fPIC or anything else.
 
sethi.p #gottlsoffhi(x), gr14
setlo #gottlsofflo(x), gr14
ld #tlsoff(x)@(gr15, gr14), gr9
*/
rtx tmp = gen_reg_rtx (Pmode);
dest = gen_reg_rtx (Pmode);
emit_insn (gen_tlsoff_hilo (tmp, addr,
GEN_INT (R_FRV_GOTTLSOFF_HI)));
 
emit_insn (gen_tls_tlsoff_ld (dest, picreg, tmp, addr));
dest = gen_rtx_PLUS (Pmode, tp, dest);
}
break;
case TLS_MODEL_LOCAL_DYNAMIC:
{
rtx reg, retval;
 
if (TARGET_INLINE_PLT)
retval = gen_inlined_tls_plt (GEN_INT (0));
else
{
/* call #gettlsoff(0) */
retval = gen_reg_rtx (Pmode);
emit_insn (gen_call_gettlsoff (retval, GEN_INT (0), picreg));
}
 
reg = gen_reg_rtx (Pmode);
emit_insn (gen_rtx_SET (VOIDmode, reg,
gen_rtx_PLUS (Pmode,
retval, tp)));
 
dest = gen_tlsmoff (addr, reg);
 
/*
dest = gen_reg_rtx (Pmode);
emit_insn (gen_tlsoff_hilo (dest, addr,
GEN_INT (R_FRV_TLSMOFFHI)));
dest = gen_rtx_PLUS (Pmode, dest, reg);
*/
break;
}
case TLS_MODEL_LOCAL_EXEC:
dest = gen_tlsmoff (addr, gen_rtx_REG (Pmode, 29));
break;
case TLS_MODEL_GLOBAL_DYNAMIC:
{
rtx retval;
 
if (TARGET_INLINE_PLT)
retval = gen_inlined_tls_plt (addr);
else
{
/* call #gettlsoff(x) */
retval = gen_reg_rtx (Pmode);
emit_insn (gen_call_gettlsoff (retval, addr, picreg));
}
dest = gen_rtx_PLUS (Pmode, retval, tp);
break;
}
default:
gcc_unreachable ();
}
 
return dest;
}
 
rtx
frv_legitimize_address (rtx x,
rtx oldx ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (x) == SYMBOL_REF)
{
enum tls_model model = SYMBOL_REF_TLS_MODEL (x);
if (model != 0)
return frv_legitimize_tls_address (x, model);
}
 
return NULL_RTX;
}
/* Test whether a local function descriptor is canonical, i.e.,
whether we can use FUNCDESC_GOTOFF to compute the address of the
function. */
 
static bool
frv_local_funcdesc_p (rtx fnx)
{
tree fn;
enum symbol_visibility vis;
bool ret;
 
if (! SYMBOL_REF_LOCAL_P (fnx))
return FALSE;
 
fn = SYMBOL_REF_DECL (fnx);
 
if (! fn)
return FALSE;
 
vis = DECL_VISIBILITY (fn);
 
if (vis == VISIBILITY_PROTECTED)
/* Private function descriptors for protected functions are not
canonical. Temporarily change the visibility to global. */
vis = VISIBILITY_DEFAULT;
else if (flag_shlib)
/* If we're already compiling for a shared library (that, unlike
executables, can't assume that the existence of a definition
implies local binding), we can skip the re-testing. */
return TRUE;
 
ret = default_binds_local_p_1 (fn, flag_pic);
 
DECL_VISIBILITY (fn) = vis;
 
return ret;
}
 
/* Load the _gp symbol into DEST. SRC is supposed to be the FDPIC
register. */
 
rtx
frv_gen_GPsym2reg (rtx dest, rtx src)
{
tree gp = get_identifier ("_gp");
rtx gp_sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (gp));
 
return gen_symGOT2reg (dest, gp_sym, src, GEN_INT (R_FRV_GOT12));
}
 
static const char *
unspec_got_name (int i)
{
switch (i)
{
case R_FRV_GOT12: return "got12";
case R_FRV_GOTHI: return "gothi";
case R_FRV_GOTLO: return "gotlo";
case R_FRV_FUNCDESC: return "funcdesc";
case R_FRV_FUNCDESC_GOT12: return "gotfuncdesc12";
case R_FRV_FUNCDESC_GOTHI: return "gotfuncdeschi";
case R_FRV_FUNCDESC_GOTLO: return "gotfuncdesclo";
case R_FRV_FUNCDESC_VALUE: return "funcdescvalue";
case R_FRV_FUNCDESC_GOTOFF12: return "gotofffuncdesc12";
case R_FRV_FUNCDESC_GOTOFFHI: return "gotofffuncdeschi";
case R_FRV_FUNCDESC_GOTOFFLO: return "gotofffuncdesclo";
case R_FRV_GOTOFF12: return "gotoff12";
case R_FRV_GOTOFFHI: return "gotoffhi";
case R_FRV_GOTOFFLO: return "gotofflo";
case R_FRV_GPREL12: return "gprel12";
case R_FRV_GPRELHI: return "gprelhi";
case R_FRV_GPRELLO: return "gprello";
case R_FRV_GOTTLSOFF_HI: return "gottlsoffhi";
case R_FRV_GOTTLSOFF_LO: return "gottlsofflo";
case R_FRV_TLSMOFFHI: return "tlsmoffhi";
case R_FRV_TLSMOFFLO: return "tlsmofflo";
case R_FRV_TLSMOFF12: return "tlsmoff12";
case R_FRV_TLSDESCHI: return "tlsdeschi";
case R_FRV_TLSDESCLO: return "tlsdesclo";
case R_FRV_GOTTLSDESCHI: return "gottlsdeschi";
case R_FRV_GOTTLSDESCLO: return "gottlsdesclo";
default: gcc_unreachable ();
}
}
 
/* Write the assembler syntax for UNSPEC to STREAM. Note that any offset
is added inside the relocation operator. */
 
static void
frv_output_const_unspec (FILE *stream, const struct frv_unspec *unspec)
{
fprintf (stream, "#%s(", unspec_got_name (unspec->reloc));
output_addr_const (stream, plus_constant (unspec->symbol, unspec->offset));
fputs (")", stream);
}
 
/* Implement FIND_BASE_TERM. See whether ORIG_X represents #gprel12(foo)
or #gotoff12(foo) for some small data symbol foo. If so, return foo,
otherwise return ORIG_X. */
 
rtx
frv_find_base_term (rtx x)
{
struct frv_unspec unspec;
 
if (frv_const_unspec_p (x, &unspec)
&& frv_small_data_reloc_p (unspec.symbol, unspec.reloc))
return plus_constant (unspec.symbol, unspec.offset);
 
return x;
}
 
/* Return 1 if operand is a valid FRV address. CONDEXEC_P is true if
the operand is used by a predicated instruction. */
 
int
frv_legitimate_memory_operand (rtx op, enum machine_mode mode, int condexec_p)
{
return ((GET_MODE (op) == mode || mode == VOIDmode)
&& GET_CODE (op) == MEM
&& frv_legitimate_address_p (mode, XEXP (op, 0),
reload_completed, condexec_p, FALSE));
}
 
void
frv_expand_fdpic_call (rtx *operands, bool ret_value, bool sibcall)
{
rtx lr = gen_rtx_REG (Pmode, LR_REGNO);
rtx picreg = get_hard_reg_initial_val (SImode, FDPIC_REG);
rtx c, rvrtx=0;
rtx addr;
 
if (ret_value)
{
rvrtx = operands[0];
operands ++;
}
 
addr = XEXP (operands[0], 0);
 
/* Inline PLTs if we're optimizing for speed. We'd like to inline
any calls that would involve a PLT, but can't tell, since we
don't know whether an extern function is going to be provided by
a separate translation unit or imported from a separate module.
When compiling for shared libraries, if the function has default
visibility, we assume it's overridable, so we inline the PLT, but
for executables, we don't really have a way to make a good
decision: a function is as likely to be imported from a shared
library as it is to be defined in the executable itself. We
assume executables will get global functions defined locally,
whereas shared libraries will have them potentially overridden,
so we only inline PLTs when compiling for shared libraries.
 
In order to mark a function as local to a shared library, any
non-default visibility attribute suffices. Unfortunately,
there's no simple way to tag a function declaration as ``in a
different module'', which we could then use to trigger PLT
inlining on executables. There's -minline-plt, but it affects
all external functions, so one would have to also mark function
declarations available in the same module with non-default
visibility, which is advantageous in itself. */
if (GET_CODE (addr) == SYMBOL_REF
&& ((!SYMBOL_REF_LOCAL_P (addr) && TARGET_INLINE_PLT)
|| sibcall))
{
rtx x, dest;
dest = gen_reg_rtx (SImode);
if (flag_pic != 1)
x = gen_symGOTOFF2reg_hilo (dest, addr, OUR_FDPIC_REG,
GEN_INT (R_FRV_FUNCDESC_GOTOFF12));
else
x = gen_symGOTOFF2reg (dest, addr, OUR_FDPIC_REG,
GEN_INT (R_FRV_FUNCDESC_GOTOFF12));
emit_insn (x);
cfun->uses_pic_offset_table = TRUE;
addr = dest;
}
else if (GET_CODE (addr) == SYMBOL_REF)
{
/* These are always either local, or handled through a local
PLT. */
if (ret_value)
c = gen_call_value_fdpicsi (rvrtx, addr, operands[1],
operands[2], picreg, lr);
else
c = gen_call_fdpicsi (addr, operands[1], operands[2], picreg, lr);
emit_call_insn (c);
return;
}
else if (! ldd_address_operand (addr, Pmode))
addr = force_reg (Pmode, addr);
 
picreg = gen_reg_rtx (DImode);
emit_insn (gen_movdi_ldd (picreg, addr));
 
if (sibcall && ret_value)
c = gen_sibcall_value_fdpicdi (rvrtx, picreg, const0_rtx);
else if (sibcall)
c = gen_sibcall_fdpicdi (picreg, const0_rtx);
else if (ret_value)
c = gen_call_value_fdpicdi (rvrtx, picreg, const0_rtx, lr);
else
c = gen_call_fdpicdi (picreg, const0_rtx, lr);
emit_call_insn (c);
}
/* Look for a SYMBOL_REF of a function in an rtx. We always want to
process these separately from any offsets, such that we add any
offsets to the function descriptor (the actual pointer), not to the
function address. */
 
static bool
frv_function_symbol_referenced_p (rtx x)
{
const char *format;
int length;
int j;
 
if (GET_CODE (x) == SYMBOL_REF)
return SYMBOL_REF_FUNCTION_P (x);
 
length = GET_RTX_LENGTH (GET_CODE (x));
format = GET_RTX_FORMAT (GET_CODE (x));
 
for (j = 0; j < length; ++j)
{
switch (format[j])
{
case 'e':
if (frv_function_symbol_referenced_p (XEXP (x, j)))
return TRUE;
break;
 
case 'V':
case 'E':
if (XVEC (x, j) != 0)
{
int k;
for (k = 0; k < XVECLEN (x, j); ++k)
if (frv_function_symbol_referenced_p (XVECEXP (x, j, k)))
return TRUE;
}
break;
 
default:
/* Nothing to do. */
break;
}
}
 
return FALSE;
}
 
/* Return true if the memory operand is one that can be conditionally
executed. */
 
int
condexec_memory_operand (rtx op, enum machine_mode mode)
{
enum machine_mode op_mode = GET_MODE (op);
rtx addr;
 
if (mode != VOIDmode && op_mode != mode)
return FALSE;
 
switch (op_mode)
{
default:
return FALSE;
 
case QImode:
case HImode:
case SImode:
case SFmode:
break;
}
 
if (GET_CODE (op) != MEM)
return FALSE;
 
addr = XEXP (op, 0);
return frv_legitimate_address_p (mode, addr, reload_completed, TRUE, FALSE);
}
/* Return true if the bare return instruction can be used outside of the
epilog code. For frv, we only do it if there was no stack allocation. */
 
int
direct_return_p (void)
{
frv_stack_t *info;
 
if (!reload_completed)
return FALSE;
 
info = frv_stack_info ();
return (info->total_size == 0);
}
 
void
frv_emit_move (enum machine_mode mode, rtx dest, rtx src)
{
if (GET_CODE (src) == SYMBOL_REF)
{
enum tls_model model = SYMBOL_REF_TLS_MODEL (src);
if (model != 0)
src = frv_legitimize_tls_address (src, model);
}
 
switch (mode)
{
case SImode:
if (frv_emit_movsi (dest, src))
return;
break;
 
case QImode:
case HImode:
case DImode:
case SFmode:
case DFmode:
if (!reload_in_progress
&& !reload_completed
&& !register_operand (dest, mode)
&& !reg_or_0_operand (src, mode))
src = copy_to_mode_reg (mode, src);
break;
 
default:
gcc_unreachable ();
}
 
emit_insn (gen_rtx_SET (VOIDmode, dest, src));
}
 
/* Emit code to handle a MOVSI, adding in the small data register or pic
register if needed to load up addresses. Return TRUE if the appropriate
instructions are emitted. */
 
int
frv_emit_movsi (rtx dest, rtx src)
{
int base_regno = -1;
int unspec = 0;
rtx sym = src;
struct frv_unspec old_unspec;
 
if (!reload_in_progress
&& !reload_completed
&& !register_operand (dest, SImode)
&& (!reg_or_0_operand (src, SImode)
/* Virtual registers will almost always be replaced by an
add instruction, so expose this to CSE by copying to
an intermediate register. */
|| (GET_CODE (src) == REG
&& IN_RANGE_P (REGNO (src),
FIRST_VIRTUAL_REGISTER,
LAST_VIRTUAL_REGISTER))))
{
emit_insn (gen_rtx_SET (VOIDmode, dest, copy_to_mode_reg (SImode, src)));
return TRUE;
}
 
/* Explicitly add in the PIC or small data register if needed. */
switch (GET_CODE (src))
{
default:
break;
 
case LABEL_REF:
handle_label:
if (TARGET_FDPIC)
{
/* Using GPREL12, we use a single GOT entry for all symbols
in read-only sections, but trade sequences such as:
 
sethi #gothi(label), gr#
setlo #gotlo(label), gr#
ld @(gr15,gr#), gr#
 
for
 
ld @(gr15,#got12(_gp)), gr#
sethi #gprelhi(label), gr##
setlo #gprello(label), gr##
add gr#, gr##, gr##
 
We may often be able to share gr# for multiple
computations of GPREL addresses, and we may often fold
the final add into the pair of registers of a load or
store instruction, so it's often profitable. Even when
optimizing for size, we're trading a GOT entry for an
additional instruction, which trades GOT space
(read-write) for code size (read-only, shareable), as
long as the symbol is not used in more than two different
locations.
With -fpie/-fpic, we'd be trading a single load for a
sequence of 4 instructions, because the offset of the
label can't be assumed to be addressable with 12 bits, so
we don't do this. */
if (TARGET_GPREL_RO)
unspec = R_FRV_GPREL12;
else
unspec = R_FRV_GOT12;
}
else if (flag_pic)
base_regno = PIC_REGNO;
 
break;
 
case CONST:
if (frv_const_unspec_p (src, &old_unspec))
break;
 
if (TARGET_FDPIC && frv_function_symbol_referenced_p (XEXP (src, 0)))
{
handle_whatever:
src = force_reg (GET_MODE (XEXP (src, 0)), XEXP (src, 0));
emit_move_insn (dest, src);
return TRUE;
}
else
{
sym = XEXP (sym, 0);
if (GET_CODE (sym) == PLUS
&& GET_CODE (XEXP (sym, 0)) == SYMBOL_REF
&& GET_CODE (XEXP (sym, 1)) == CONST_INT)
sym = XEXP (sym, 0);
if (GET_CODE (sym) == SYMBOL_REF)
goto handle_sym;
else if (GET_CODE (sym) == LABEL_REF)
goto handle_label;
else
goto handle_whatever;
}
break;
 
case SYMBOL_REF:
handle_sym:
if (TARGET_FDPIC)
{
enum tls_model model = SYMBOL_REF_TLS_MODEL (sym);
 
if (model != 0)
{
src = frv_legitimize_tls_address (src, model);
emit_move_insn (dest, src);
return TRUE;
}
 
if (SYMBOL_REF_FUNCTION_P (sym))
{
if (frv_local_funcdesc_p (sym))
unspec = R_FRV_FUNCDESC_GOTOFF12;
else
unspec = R_FRV_FUNCDESC_GOT12;
}
else
{
if (CONSTANT_POOL_ADDRESS_P (sym))
switch (GET_CODE (get_pool_constant (sym)))
{
case CONST:
case SYMBOL_REF:
case LABEL_REF:
if (flag_pic)
{
unspec = R_FRV_GOTOFF12;
break;
}
/* Fall through. */
default:
if (TARGET_GPREL_RO)
unspec = R_FRV_GPREL12;
else
unspec = R_FRV_GOT12;
break;
}
else if (SYMBOL_REF_LOCAL_P (sym)
&& !SYMBOL_REF_EXTERNAL_P (sym)
&& SYMBOL_REF_DECL (sym)
&& (!DECL_P (SYMBOL_REF_DECL (sym))
|| !DECL_COMMON (SYMBOL_REF_DECL (sym))))
{
tree decl = SYMBOL_REF_DECL (sym);
tree init = TREE_CODE (decl) == VAR_DECL
? DECL_INITIAL (decl)
: TREE_CODE (decl) == CONSTRUCTOR
? decl : 0;
int reloc = 0;
bool named_section, readonly;
 
if (init && init != error_mark_node)
reloc = compute_reloc_for_constant (init);
named_section = TREE_CODE (decl) == VAR_DECL
&& lookup_attribute ("section", DECL_ATTRIBUTES (decl));
readonly = decl_readonly_section (decl, reloc);
if (named_section)
unspec = R_FRV_GOT12;
else if (!readonly)
unspec = R_FRV_GOTOFF12;
else if (readonly && TARGET_GPREL_RO)
unspec = R_FRV_GPREL12;
else
unspec = R_FRV_GOT12;
}
else
unspec = R_FRV_GOT12;
}
}
 
else if (SYMBOL_REF_SMALL_P (sym))
base_regno = SDA_BASE_REG;
 
else if (flag_pic)
base_regno = PIC_REGNO;
 
break;
}
 
if (base_regno >= 0)
{
if (GET_CODE (sym) == SYMBOL_REF && SYMBOL_REF_SMALL_P (sym))
emit_insn (gen_symGOTOFF2reg (dest, src,
gen_rtx_REG (Pmode, base_regno),
GEN_INT (R_FRV_GPREL12)));
else
emit_insn (gen_symGOTOFF2reg_hilo (dest, src,
gen_rtx_REG (Pmode, base_regno),
GEN_INT (R_FRV_GPREL12)));
if (base_regno == PIC_REGNO)
cfun->uses_pic_offset_table = TRUE;
return TRUE;
}
 
if (unspec)
{
rtx x;
 
/* Since OUR_FDPIC_REG is a pseudo register, we can't safely introduce
new uses of it once reload has begun. */
gcc_assert (!reload_in_progress && !reload_completed);
 
switch (unspec)
{
case R_FRV_GOTOFF12:
if (!frv_small_data_reloc_p (sym, unspec))
x = gen_symGOTOFF2reg_hilo (dest, src, OUR_FDPIC_REG,
GEN_INT (unspec));
else
x = gen_symGOTOFF2reg (dest, src, OUR_FDPIC_REG, GEN_INT (unspec));
break;
case R_FRV_GPREL12:
if (!frv_small_data_reloc_p (sym, unspec))
x = gen_symGPREL2reg_hilo (dest, src, OUR_FDPIC_REG,
GEN_INT (unspec));
else
x = gen_symGPREL2reg (dest, src, OUR_FDPIC_REG, GEN_INT (unspec));
break;
case R_FRV_FUNCDESC_GOTOFF12:
if (flag_pic != 1)
x = gen_symGOTOFF2reg_hilo (dest, src, OUR_FDPIC_REG,
GEN_INT (unspec));
else
x = gen_symGOTOFF2reg (dest, src, OUR_FDPIC_REG, GEN_INT (unspec));
break;
default:
if (flag_pic != 1)
x = gen_symGOT2reg_hilo (dest, src, OUR_FDPIC_REG,
GEN_INT (unspec));
else
x = gen_symGOT2reg (dest, src, OUR_FDPIC_REG, GEN_INT (unspec));
break;
}
emit_insn (x);
cfun->uses_pic_offset_table = TRUE;
return TRUE;
}
 
 
return FALSE;
}
 
/* Return a string to output a single word move. */
 
const char *
output_move_single (rtx operands[], rtx insn)
{
rtx dest = operands[0];
rtx src = operands[1];
 
if (GET_CODE (dest) == REG)
{
int dest_regno = REGNO (dest);
enum machine_mode mode = GET_MODE (dest);
 
if (GPR_P (dest_regno))
{
if (GET_CODE (src) == REG)
{
/* gpr <- some sort of register */
int src_regno = REGNO (src);
 
if (GPR_P (src_regno))
return "mov %1, %0";
 
else if (FPR_P (src_regno))
return "movfg %1, %0";
 
else if (SPR_P (src_regno))
return "movsg %1, %0";
}
 
else if (GET_CODE (src) == MEM)
{
/* gpr <- memory */
switch (mode)
{
default:
break;
 
case QImode:
return "ldsb%I1%U1 %M1,%0";
 
case HImode:
return "ldsh%I1%U1 %M1,%0";
 
case SImode:
case SFmode:
return "ld%I1%U1 %M1, %0";
}
}
 
else if (GET_CODE (src) == CONST_INT
|| GET_CODE (src) == CONST_DOUBLE)
{
/* gpr <- integer/floating constant */
HOST_WIDE_INT value;
 
if (GET_CODE (src) == CONST_INT)
value = INTVAL (src);
 
else if (mode == SFmode)
{
REAL_VALUE_TYPE rv;
long l;
 
REAL_VALUE_FROM_CONST_DOUBLE (rv, src);
REAL_VALUE_TO_TARGET_SINGLE (rv, l);
value = l;
}
 
else
value = CONST_DOUBLE_LOW (src);
 
if (IN_RANGE_P (value, -32768, 32767))
return "setlos %1, %0";
 
return "#";
}
 
else if (GET_CODE (src) == SYMBOL_REF
|| GET_CODE (src) == LABEL_REF
|| GET_CODE (src) == CONST)
{
return "#";
}
}
 
else if (FPR_P (dest_regno))
{
if (GET_CODE (src) == REG)
{
/* fpr <- some sort of register */
int src_regno = REGNO (src);
 
if (GPR_P (src_regno))
return "movgf %1, %0";
 
else if (FPR_P (src_regno))
{
if (TARGET_HARD_FLOAT)
return "fmovs %1, %0";
else
return "mor %1, %1, %0";
}
}
 
else if (GET_CODE (src) == MEM)
{
/* fpr <- memory */
switch (mode)
{
default:
break;
 
case QImode:
return "ldbf%I1%U1 %M1,%0";
 
case HImode:
return "ldhf%I1%U1 %M1,%0";
 
case SImode:
case SFmode:
return "ldf%I1%U1 %M1, %0";
}
}
 
else if (ZERO_P (src))
return "movgf %., %0";
}
 
else if (SPR_P (dest_regno))
{
if (GET_CODE (src) == REG)
{
/* spr <- some sort of register */
int src_regno = REGNO (src);
 
if (GPR_P (src_regno))
return "movgs %1, %0";
}
else if (ZERO_P (src))
return "movgs %., %0";
}
}
 
else if (GET_CODE (dest) == MEM)
{
if (GET_CODE (src) == REG)
{
int src_regno = REGNO (src);
enum machine_mode mode = GET_MODE (dest);
 
if (GPR_P (src_regno))
{
switch (mode)
{
default:
break;
 
case QImode:
return "stb%I0%U0 %1, %M0";
 
case HImode:
return "sth%I0%U0 %1, %M0";
 
case SImode:
case SFmode:
return "st%I0%U0 %1, %M0";
}
}
 
else if (FPR_P (src_regno))
{
switch (mode)
{
default:
break;
 
case QImode:
return "stbf%I0%U0 %1, %M0";
 
case HImode:
return "sthf%I0%U0 %1, %M0";
 
case SImode:
case SFmode:
return "stf%I0%U0 %1, %M0";
}
}
}
 
else if (ZERO_P (src))
{
switch (GET_MODE (dest))
{
default:
break;
 
case QImode:
return "stb%I0%U0 %., %M0";
 
case HImode:
return "sth%I0%U0 %., %M0";
 
case SImode:
case SFmode:
return "st%I0%U0 %., %M0";
}
}
}
 
fatal_insn ("bad output_move_single operand", insn);
return "";
}
 
/* Return a string to output a double word move. */
 
const char *
output_move_double (rtx operands[], rtx insn)
{
rtx dest = operands[0];
rtx src = operands[1];
enum machine_mode mode = GET_MODE (dest);
 
if (GET_CODE (dest) == REG)
{
int dest_regno = REGNO (dest);
 
if (GPR_P (dest_regno))
{
if (GET_CODE (src) == REG)
{
/* gpr <- some sort of register */
int src_regno = REGNO (src);
 
if (GPR_P (src_regno))
return "#";
 
else if (FPR_P (src_regno))
{
if (((dest_regno - GPR_FIRST) & 1) == 0
&& ((src_regno - FPR_FIRST) & 1) == 0)
return "movfgd %1, %0";
 
return "#";
}
}
 
else if (GET_CODE (src) == MEM)
{
/* gpr <- memory */
if (dbl_memory_one_insn_operand (src, mode))
return "ldd%I1%U1 %M1, %0";
 
return "#";
}
 
else if (GET_CODE (src) == CONST_INT
|| GET_CODE (src) == CONST_DOUBLE)
return "#";
}
 
else if (FPR_P (dest_regno))
{
if (GET_CODE (src) == REG)
{
/* fpr <- some sort of register */
int src_regno = REGNO (src);
 
if (GPR_P (src_regno))
{
if (((dest_regno - FPR_FIRST) & 1) == 0
&& ((src_regno - GPR_FIRST) & 1) == 0)
return "movgfd %1, %0";
 
return "#";
}
 
else if (FPR_P (src_regno))
{
if (TARGET_DOUBLE
&& ((dest_regno - FPR_FIRST) & 1) == 0
&& ((src_regno - FPR_FIRST) & 1) == 0)
return "fmovd %1, %0";
 
return "#";
}
}
 
else if (GET_CODE (src) == MEM)
{
/* fpr <- memory */
if (dbl_memory_one_insn_operand (src, mode))
return "lddf%I1%U1 %M1, %0";
 
return "#";
}
 
else if (ZERO_P (src))
return "#";
}
}
 
else if (GET_CODE (dest) == MEM)
{
if (GET_CODE (src) == REG)
{
int src_regno = REGNO (src);
 
if (GPR_P (src_regno))
{
if (((src_regno - GPR_FIRST) & 1) == 0
&& dbl_memory_one_insn_operand (dest, mode))
return "std%I0%U0 %1, %M0";
 
return "#";
}
 
if (FPR_P (src_regno))
{
if (((src_regno - FPR_FIRST) & 1) == 0
&& dbl_memory_one_insn_operand (dest, mode))
return "stdf%I0%U0 %1, %M0";
 
return "#";
}
}
 
else if (ZERO_P (src))
{
if (dbl_memory_one_insn_operand (dest, mode))
return "std%I0%U0 %., %M0";
 
return "#";
}
}
 
fatal_insn ("bad output_move_double operand", insn);
return "";
}
 
/* Return a string to output a single word conditional move.
Operand0 -- EQ/NE of ccr register and 0
Operand1 -- CCR register
Operand2 -- destination
Operand3 -- source */
 
const char *
output_condmove_single (rtx operands[], rtx insn)
{
rtx dest = operands[2];
rtx src = operands[3];
 
if (GET_CODE (dest) == REG)
{
int dest_regno = REGNO (dest);
enum machine_mode mode = GET_MODE (dest);
 
if (GPR_P (dest_regno))
{
if (GET_CODE (src) == REG)
{
/* gpr <- some sort of register */
int src_regno = REGNO (src);
 
if (GPR_P (src_regno))
return "cmov %z3, %2, %1, %e0";
 
else if (FPR_P (src_regno))
return "cmovfg %3, %2, %1, %e0";
}
 
else if (GET_CODE (src) == MEM)
{
/* gpr <- memory */
switch (mode)
{
default:
break;
 
case QImode:
return "cldsb%I3%U3 %M3, %2, %1, %e0";
 
case HImode:
return "cldsh%I3%U3 %M3, %2, %1, %e0";
 
case SImode:
case SFmode:
return "cld%I3%U3 %M3, %2, %1, %e0";
}
}
 
else if (ZERO_P (src))
return "cmov %., %2, %1, %e0";
}
 
else if (FPR_P (dest_regno))
{
if (GET_CODE (src) == REG)
{
/* fpr <- some sort of register */
int src_regno = REGNO (src);
 
if (GPR_P (src_regno))
return "cmovgf %3, %2, %1, %e0";
 
else if (FPR_P (src_regno))
{
if (TARGET_HARD_FLOAT)
return "cfmovs %3,%2,%1,%e0";
else
return "cmor %3, %3, %2, %1, %e0";
}
}
 
else if (GET_CODE (src) == MEM)
{
/* fpr <- memory */
if (mode == SImode || mode == SFmode)
return "cldf%I3%U3 %M3, %2, %1, %e0";
}
 
else if (ZERO_P (src))
return "cmovgf %., %2, %1, %e0";
}
}
 
else if (GET_CODE (dest) == MEM)
{
if (GET_CODE (src) == REG)
{
int src_regno = REGNO (src);
enum machine_mode mode = GET_MODE (dest);
 
if (GPR_P (src_regno))
{
switch (mode)
{
default:
break;
 
case QImode:
return "cstb%I2%U2 %3, %M2, %1, %e0";
 
case HImode:
return "csth%I2%U2 %3, %M2, %1, %e0";
 
case SImode:
case SFmode:
return "cst%I2%U2 %3, %M2, %1, %e0";
}
}
 
else if (FPR_P (src_regno) && (mode == SImode || mode == SFmode))
return "cstf%I2%U2 %3, %M2, %1, %e0";
}
 
else if (ZERO_P (src))
{
enum machine_mode mode = GET_MODE (dest);
switch (mode)
{
default:
break;
 
case QImode:
return "cstb%I2%U2 %., %M2, %1, %e0";
 
case HImode:
return "csth%I2%U2 %., %M2, %1, %e0";
 
case SImode:
case SFmode:
return "cst%I2%U2 %., %M2, %1, %e0";
}
}
}
 
fatal_insn ("bad output_condmove_single operand", insn);
return "";
}
 
/* Emit the appropriate code to do a comparison, returning the register the
comparison was done it. */
 
static rtx
frv_emit_comparison (enum rtx_code test, rtx op0, rtx op1)
{
enum machine_mode cc_mode;
rtx cc_reg;
 
/* Floating point doesn't have comparison against a constant. */
if (GET_MODE (op0) == CC_FPmode && GET_CODE (op1) != REG)
op1 = force_reg (GET_MODE (op0), op1);
 
/* Possibly disable using anything but a fixed register in order to work
around cse moving comparisons past function calls. */
cc_mode = SELECT_CC_MODE (test, op0, op1);
cc_reg = ((TARGET_ALLOC_CC)
? gen_reg_rtx (cc_mode)
: gen_rtx_REG (cc_mode,
(cc_mode == CC_FPmode) ? FCC_FIRST : ICC_FIRST));
 
emit_insn (gen_rtx_SET (VOIDmode, cc_reg,
gen_rtx_COMPARE (cc_mode, op0, op1)));
 
return cc_reg;
}
 
/* Emit code for a conditional branch. The comparison operands were previously
stored in frv_compare_op0 and frv_compare_op1.
 
XXX: I originally wanted to add a clobber of a CCR register to use in
conditional execution, but that confuses the rest of the compiler. */
 
int
frv_emit_cond_branch (enum rtx_code test, rtx label)
{
rtx test_rtx;
rtx label_ref;
rtx if_else;
rtx cc_reg = frv_emit_comparison (test, frv_compare_op0, frv_compare_op1);
enum machine_mode cc_mode = GET_MODE (cc_reg);
 
/* Branches generate:
(set (pc)
(if_then_else (<test>, <cc_reg>, (const_int 0))
(label_ref <branch_label>)
(pc))) */
label_ref = gen_rtx_LABEL_REF (VOIDmode, label);
test_rtx = gen_rtx_fmt_ee (test, cc_mode, cc_reg, const0_rtx);
if_else = gen_rtx_IF_THEN_ELSE (cc_mode, test_rtx, label_ref, pc_rtx);
emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, if_else));
return TRUE;
}
 
/* Emit code to set a gpr to 1/0 based on a comparison. The comparison
operands were previously stored in frv_compare_op0 and frv_compare_op1. */
 
int
frv_emit_scc (enum rtx_code test, rtx target)
{
rtx set;
rtx test_rtx;
rtx clobber;
rtx cr_reg;
rtx cc_reg = frv_emit_comparison (test, frv_compare_op0, frv_compare_op1);
 
/* SCC instructions generate:
(parallel [(set <target> (<test>, <cc_reg>, (const_int 0))
(clobber (<ccr_reg>))]) */
test_rtx = gen_rtx_fmt_ee (test, SImode, cc_reg, const0_rtx);
set = gen_rtx_SET (VOIDmode, target, test_rtx);
 
cr_reg = ((TARGET_ALLOC_CC)
? gen_reg_rtx (CC_CCRmode)
: gen_rtx_REG (CC_CCRmode,
((GET_MODE (cc_reg) == CC_FPmode)
? FCR_FIRST
: ICR_FIRST)));
 
clobber = gen_rtx_CLOBBER (VOIDmode, cr_reg);
emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber)));
return TRUE;
}
 
/* Split a SCC instruction into component parts, returning a SEQUENCE to hold
the separate insns. */
 
rtx
frv_split_scc (rtx dest, rtx test, rtx cc_reg, rtx cr_reg, HOST_WIDE_INT value)
{
rtx ret;
 
start_sequence ();
 
/* Set the appropriate CCR bit. */
emit_insn (gen_rtx_SET (VOIDmode,
cr_reg,
gen_rtx_fmt_ee (GET_CODE (test),
GET_MODE (cr_reg),
cc_reg,
const0_rtx)));
 
/* Move the value into the destination. */
emit_move_insn (dest, GEN_INT (value));
 
/* Move 0 into the destination if the test failed */
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_EQ (GET_MODE (cr_reg),
cr_reg,
const0_rtx),
gen_rtx_SET (VOIDmode, dest, const0_rtx)));
 
/* Finish up, return sequence. */
ret = get_insns ();
end_sequence ();
return ret;
}
 
/* Emit the code for a conditional move, return TRUE if we could do the
move. */
 
int
frv_emit_cond_move (rtx dest, rtx test_rtx, rtx src1, rtx src2)
{
rtx set;
rtx clobber_cc;
rtx test2;
rtx cr_reg;
rtx if_rtx;
enum rtx_code test = GET_CODE (test_rtx);
rtx cc_reg = frv_emit_comparison (test, frv_compare_op0, frv_compare_op1);
enum machine_mode cc_mode = GET_MODE (cc_reg);
 
/* Conditional move instructions generate:
(parallel [(set <target>
(if_then_else (<test> <cc_reg> (const_int 0))
<src1>
<src2>))
(clobber (<ccr_reg>))]) */
 
/* Handle various cases of conditional move involving two constants. */
if (GET_CODE (src1) == CONST_INT && GET_CODE (src2) == CONST_INT)
{
HOST_WIDE_INT value1 = INTVAL (src1);
HOST_WIDE_INT value2 = INTVAL (src2);
 
/* Having 0 as one of the constants can be done by loading the other
constant, and optionally moving in gr0. */
if (value1 == 0 || value2 == 0)
;
 
/* If the first value is within an addi range and also the difference
between the two fits in an addi's range, load up the difference, then
conditionally move in 0, and then unconditionally add the first
value. */
else if (IN_RANGE_P (value1, -2048, 2047)
&& IN_RANGE_P (value2 - value1, -2048, 2047))
;
 
/* If neither condition holds, just force the constant into a
register. */
else
{
src1 = force_reg (GET_MODE (dest), src1);
src2 = force_reg (GET_MODE (dest), src2);
}
}
 
/* If one value is a register, insure the other value is either 0 or a
register. */
else
{
if (GET_CODE (src1) == CONST_INT && INTVAL (src1) != 0)
src1 = force_reg (GET_MODE (dest), src1);
 
if (GET_CODE (src2) == CONST_INT && INTVAL (src2) != 0)
src2 = force_reg (GET_MODE (dest), src2);
}
 
test2 = gen_rtx_fmt_ee (test, cc_mode, cc_reg, const0_rtx);
if_rtx = gen_rtx_IF_THEN_ELSE (GET_MODE (dest), test2, src1, src2);
 
set = gen_rtx_SET (VOIDmode, dest, if_rtx);
 
cr_reg = ((TARGET_ALLOC_CC)
? gen_reg_rtx (CC_CCRmode)
: gen_rtx_REG (CC_CCRmode,
(cc_mode == CC_FPmode) ? FCR_FIRST : ICR_FIRST));
 
clobber_cc = gen_rtx_CLOBBER (VOIDmode, cr_reg);
emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber_cc)));
return TRUE;
}
 
/* Split a conditional move into constituent parts, returning a SEQUENCE
containing all of the insns. */
 
rtx
frv_split_cond_move (rtx operands[])
{
rtx dest = operands[0];
rtx test = operands[1];
rtx cc_reg = operands[2];
rtx src1 = operands[3];
rtx src2 = operands[4];
rtx cr_reg = operands[5];
rtx ret;
enum machine_mode cr_mode = GET_MODE (cr_reg);
 
start_sequence ();
 
/* Set the appropriate CCR bit. */
emit_insn (gen_rtx_SET (VOIDmode,
cr_reg,
gen_rtx_fmt_ee (GET_CODE (test),
GET_MODE (cr_reg),
cc_reg,
const0_rtx)));
 
/* Handle various cases of conditional move involving two constants. */
if (GET_CODE (src1) == CONST_INT && GET_CODE (src2) == CONST_INT)
{
HOST_WIDE_INT value1 = INTVAL (src1);
HOST_WIDE_INT value2 = INTVAL (src2);
 
/* Having 0 as one of the constants can be done by loading the other
constant, and optionally moving in gr0. */
if (value1 == 0)
{
emit_move_insn (dest, src2);
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_NE (cr_mode, cr_reg,
const0_rtx),
gen_rtx_SET (VOIDmode, dest, src1)));
}
 
else if (value2 == 0)
{
emit_move_insn (dest, src1);
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_EQ (cr_mode, cr_reg,
const0_rtx),
gen_rtx_SET (VOIDmode, dest, src2)));
}
 
/* If the first value is within an addi range and also the difference
between the two fits in an addi's range, load up the difference, then
conditionally move in 0, and then unconditionally add the first
value. */
else if (IN_RANGE_P (value1, -2048, 2047)
&& IN_RANGE_P (value2 - value1, -2048, 2047))
{
rtx dest_si = ((GET_MODE (dest) == SImode)
? dest
: gen_rtx_SUBREG (SImode, dest, 0));
 
emit_move_insn (dest_si, GEN_INT (value2 - value1));
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_NE (cr_mode, cr_reg,
const0_rtx),
gen_rtx_SET (VOIDmode, dest_si,
const0_rtx)));
emit_insn (gen_addsi3 (dest_si, dest_si, src1));
}
 
else
gcc_unreachable ();
}
else
{
/* Emit the conditional move for the test being true if needed. */
if (! rtx_equal_p (dest, src1))
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_NE (cr_mode, cr_reg, const0_rtx),
gen_rtx_SET (VOIDmode, dest, src1)));
 
/* Emit the conditional move for the test being false if needed. */
if (! rtx_equal_p (dest, src2))
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_EQ (cr_mode, cr_reg, const0_rtx),
gen_rtx_SET (VOIDmode, dest, src2)));
}
 
/* Finish up, return sequence. */
ret = get_insns ();
end_sequence ();
return ret;
}
 
/* Split (set DEST SOURCE), where DEST is a double register and SOURCE is a
memory location that is not known to be dword-aligned. */
void
frv_split_double_load (rtx dest, rtx source)
{
int regno = REGNO (dest);
rtx dest1 = gen_highpart (SImode, dest);
rtx dest2 = gen_lowpart (SImode, dest);
rtx address = XEXP (source, 0);
 
/* If the address is pre-modified, load the lower-numbered register
first, then load the other register using an integer offset from
the modified base register. This order should always be safe,
since the pre-modification cannot affect the same registers as the
load does.
 
The situation for other loads is more complicated. Loading one
of the registers could affect the value of ADDRESS, so we must
be careful which order we do them in. */
if (GET_CODE (address) == PRE_MODIFY
|| ! refers_to_regno_p (regno, regno + 1, address, NULL))
{
/* It is safe to load the lower-numbered register first. */
emit_move_insn (dest1, change_address (source, SImode, NULL));
emit_move_insn (dest2, frv_index_memory (source, SImode, 1));
}
else
{
/* ADDRESS is not pre-modified and the address depends on the
lower-numbered register. Load the higher-numbered register
first. */
emit_move_insn (dest2, frv_index_memory (source, SImode, 1));
emit_move_insn (dest1, change_address (source, SImode, NULL));
}
}
 
/* Split (set DEST SOURCE), where DEST refers to a dword memory location
and SOURCE is either a double register or the constant zero. */
void
frv_split_double_store (rtx dest, rtx source)
{
rtx dest1 = change_address (dest, SImode, NULL);
rtx dest2 = frv_index_memory (dest, SImode, 1);
if (ZERO_P (source))
{
emit_move_insn (dest1, CONST0_RTX (SImode));
emit_move_insn (dest2, CONST0_RTX (SImode));
}
else
{
emit_move_insn (dest1, gen_highpart (SImode, source));
emit_move_insn (dest2, gen_lowpart (SImode, source));
}
}
 
/* Split a min/max operation returning a SEQUENCE containing all of the
insns. */
 
rtx
frv_split_minmax (rtx operands[])
{
rtx dest = operands[0];
rtx minmax = operands[1];
rtx src1 = operands[2];
rtx src2 = operands[3];
rtx cc_reg = operands[4];
rtx cr_reg = operands[5];
rtx ret;
enum rtx_code test_code;
enum machine_mode cr_mode = GET_MODE (cr_reg);
 
start_sequence ();
 
/* Figure out which test to use. */
switch (GET_CODE (minmax))
{
default:
gcc_unreachable ();
 
case SMIN: test_code = LT; break;
case SMAX: test_code = GT; break;
case UMIN: test_code = LTU; break;
case UMAX: test_code = GTU; break;
}
 
/* Issue the compare instruction. */
emit_insn (gen_rtx_SET (VOIDmode,
cc_reg,
gen_rtx_COMPARE (GET_MODE (cc_reg),
src1, src2)));
 
/* Set the appropriate CCR bit. */
emit_insn (gen_rtx_SET (VOIDmode,
cr_reg,
gen_rtx_fmt_ee (test_code,
GET_MODE (cr_reg),
cc_reg,
const0_rtx)));
 
/* If are taking the min/max of a nonzero constant, load that first, and
then do a conditional move of the other value. */
if (GET_CODE (src2) == CONST_INT && INTVAL (src2) != 0)
{
gcc_assert (!rtx_equal_p (dest, src1));
 
emit_move_insn (dest, src2);
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_NE (cr_mode, cr_reg, const0_rtx),
gen_rtx_SET (VOIDmode, dest, src1)));
}
 
/* Otherwise, do each half of the move. */
else
{
/* Emit the conditional move for the test being true if needed. */
if (! rtx_equal_p (dest, src1))
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_NE (cr_mode, cr_reg, const0_rtx),
gen_rtx_SET (VOIDmode, dest, src1)));
 
/* Emit the conditional move for the test being false if needed. */
if (! rtx_equal_p (dest, src2))
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_EQ (cr_mode, cr_reg, const0_rtx),
gen_rtx_SET (VOIDmode, dest, src2)));
}
 
/* Finish up, return sequence. */
ret = get_insns ();
end_sequence ();
return ret;
}
 
/* Split an integer abs operation returning a SEQUENCE containing all of the
insns. */
 
rtx
frv_split_abs (rtx operands[])
{
rtx dest = operands[0];
rtx src = operands[1];
rtx cc_reg = operands[2];
rtx cr_reg = operands[3];
rtx ret;
 
start_sequence ();
 
/* Issue the compare < 0 instruction. */
emit_insn (gen_rtx_SET (VOIDmode,
cc_reg,
gen_rtx_COMPARE (CCmode, src, const0_rtx)));
 
/* Set the appropriate CCR bit. */
emit_insn (gen_rtx_SET (VOIDmode,
cr_reg,
gen_rtx_fmt_ee (LT, CC_CCRmode, cc_reg, const0_rtx)));
 
/* Emit the conditional negate if the value is negative. */
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_NE (CC_CCRmode, cr_reg, const0_rtx),
gen_negsi2 (dest, src)));
 
/* Emit the conditional move for the test being false if needed. */
if (! rtx_equal_p (dest, src))
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_EQ (CC_CCRmode, cr_reg, const0_rtx),
gen_rtx_SET (VOIDmode, dest, src)));
 
/* Finish up, return sequence. */
ret = get_insns ();
end_sequence ();
return ret;
}
 
/* An internal function called by for_each_rtx to clear in a hard_reg set each
register used in an insn. */
 
static int
frv_clear_registers_used (rtx *ptr, void *data)
{
if (GET_CODE (*ptr) == REG)
{
int regno = REGNO (*ptr);
HARD_REG_SET *p_regs = (HARD_REG_SET *)data;
 
if (regno < FIRST_PSEUDO_REGISTER)
{
int reg_max = regno + HARD_REGNO_NREGS (regno, GET_MODE (*ptr));
 
while (regno < reg_max)
{
CLEAR_HARD_REG_BIT (*p_regs, regno);
regno++;
}
}
}
 
return 0;
}
 
/* Initialize the extra fields provided by IFCVT_EXTRA_FIELDS. */
 
/* On the FR-V, we don't have any extra fields per se, but it is useful hook to
initialize the static storage. */
void
frv_ifcvt_init_extra_fields (ce_if_block_t *ce_info ATTRIBUTE_UNUSED)
{
frv_ifcvt.added_insns_list = NULL_RTX;
frv_ifcvt.cur_scratch_regs = 0;
frv_ifcvt.num_nested_cond_exec = 0;
frv_ifcvt.cr_reg = NULL_RTX;
frv_ifcvt.nested_cc_reg = NULL_RTX;
frv_ifcvt.extra_int_cr = NULL_RTX;
frv_ifcvt.extra_fp_cr = NULL_RTX;
frv_ifcvt.last_nested_if_cr = NULL_RTX;
}
 
/* Internal function to add a potential insn to the list of insns to be inserted
if the conditional execution conversion is successful. */
 
static void
frv_ifcvt_add_insn (rtx pattern, rtx insn, int before_p)
{
rtx link = alloc_EXPR_LIST (VOIDmode, pattern, insn);
 
link->jump = before_p; /* Mark to add this before or after insn. */
frv_ifcvt.added_insns_list = alloc_EXPR_LIST (VOIDmode, link,
frv_ifcvt.added_insns_list);
 
if (TARGET_DEBUG_COND_EXEC)
{
fprintf (stderr,
"\n:::::::::: frv_ifcvt_add_insn: add the following %s insn %d:\n",
(before_p) ? "before" : "after",
(int)INSN_UID (insn));
 
debug_rtx (pattern);
}
}
 
/* A C expression to modify the code described by the conditional if
information CE_INFO, possibly updating the tests in TRUE_EXPR, and
FALSE_EXPR for converting if-then and if-then-else code to conditional
instructions. Set either TRUE_EXPR or FALSE_EXPR to a null pointer if the
tests cannot be converted. */
 
void
frv_ifcvt_modify_tests (ce_if_block_t *ce_info, rtx *p_true, rtx *p_false)
{
basic_block test_bb = ce_info->test_bb; /* test basic block */
basic_block then_bb = ce_info->then_bb; /* THEN */
basic_block else_bb = ce_info->else_bb; /* ELSE or NULL */
basic_block join_bb = ce_info->join_bb; /* join block or NULL */
rtx true_expr = *p_true;
rtx cr;
rtx cc;
rtx nested_cc;
enum machine_mode mode = GET_MODE (true_expr);
int j;
basic_block *bb;
int num_bb;
frv_tmp_reg_t *tmp_reg = &frv_ifcvt.tmp_reg;
rtx check_insn;
rtx sub_cond_exec_reg;
enum rtx_code code;
enum rtx_code code_true;
enum rtx_code code_false;
enum reg_class cc_class;
enum reg_class cr_class;
int cc_first;
int cc_last;
reg_set_iterator rsi;
 
/* Make sure we are only dealing with hard registers. Also honor the
-mno-cond-exec switch, and -mno-nested-cond-exec switches if
applicable. */
if (!reload_completed || !TARGET_COND_EXEC
|| (!TARGET_NESTED_CE && ce_info->pass > 1))
goto fail;
 
/* Figure out which registers we can allocate for our own purposes. Only
consider registers that are not preserved across function calls and are
not fixed. However, allow the ICC/ICR temporary registers to be allocated
if we did not need to use them in reloading other registers. */
memset (&tmp_reg->regs, 0, sizeof (tmp_reg->regs));
COPY_HARD_REG_SET (tmp_reg->regs, call_used_reg_set);
AND_COMPL_HARD_REG_SET (tmp_reg->regs, fixed_reg_set);
SET_HARD_REG_BIT (tmp_reg->regs, ICC_TEMP);
SET_HARD_REG_BIT (tmp_reg->regs, ICR_TEMP);
 
/* If this is a nested IF, we need to discover whether the CC registers that
are set/used inside of the block are used anywhere else. If not, we can
change them to be the CC register that is paired with the CR register that
controls the outermost IF block. */
if (ce_info->pass > 1)
{
CLEAR_HARD_REG_SET (frv_ifcvt.nested_cc_ok_rewrite);
for (j = CC_FIRST; j <= CC_LAST; j++)
if (TEST_HARD_REG_BIT (tmp_reg->regs, j))
{
if (REGNO_REG_SET_P (then_bb->il.rtl->global_live_at_start, j))
continue;
 
if (else_bb
&& REGNO_REG_SET_P (else_bb->il.rtl->global_live_at_start, j))
continue;
 
if (join_bb
&& REGNO_REG_SET_P (join_bb->il.rtl->global_live_at_start, j))
continue;
 
SET_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite, j);
}
}
 
for (j = 0; j < frv_ifcvt.cur_scratch_regs; j++)
frv_ifcvt.scratch_regs[j] = NULL_RTX;
 
frv_ifcvt.added_insns_list = NULL_RTX;
frv_ifcvt.cur_scratch_regs = 0;
 
bb = (basic_block *) alloca ((2 + ce_info->num_multiple_test_blocks)
* sizeof (basic_block));
 
if (join_bb)
{
unsigned int regno;
 
/* Remove anything live at the beginning of the join block from being
available for allocation. */
EXECUTE_IF_SET_IN_REG_SET (join_bb->il.rtl->global_live_at_start, 0, regno, rsi)
{
if (regno < FIRST_PSEUDO_REGISTER)
CLEAR_HARD_REG_BIT (tmp_reg->regs, regno);
}
}
 
/* Add in all of the blocks in multiple &&/|| blocks to be scanned. */
num_bb = 0;
if (ce_info->num_multiple_test_blocks)
{
basic_block multiple_test_bb = ce_info->last_test_bb;
 
while (multiple_test_bb != test_bb)
{
bb[num_bb++] = multiple_test_bb;
multiple_test_bb = EDGE_PRED (multiple_test_bb, 0)->src;
}
}
 
/* Add in the THEN and ELSE blocks to be scanned. */
bb[num_bb++] = then_bb;
if (else_bb)
bb[num_bb++] = else_bb;
 
sub_cond_exec_reg = NULL_RTX;
frv_ifcvt.num_nested_cond_exec = 0;
 
/* Scan all of the blocks for registers that must not be allocated. */
for (j = 0; j < num_bb; j++)
{
rtx last_insn = BB_END (bb[j]);
rtx insn = BB_HEAD (bb[j]);
unsigned int regno;
 
if (dump_file)
fprintf (dump_file, "Scanning %s block %d, start %d, end %d\n",
(bb[j] == else_bb) ? "else" : ((bb[j] == then_bb) ? "then" : "test"),
(int) bb[j]->index,
(int) INSN_UID (BB_HEAD (bb[j])),
(int) INSN_UID (BB_END (bb[j])));
 
/* Anything live at the beginning of the block is obviously unavailable
for allocation. */
EXECUTE_IF_SET_IN_REG_SET (bb[j]->il.rtl->global_live_at_start, 0, regno, rsi)
{
if (regno < FIRST_PSEUDO_REGISTER)
CLEAR_HARD_REG_BIT (tmp_reg->regs, regno);
}
 
/* Loop through the insns in the block. */
for (;;)
{
/* Mark any new registers that are created as being unavailable for
allocation. Also see if the CC register used in nested IFs can be
reallocated. */
if (INSN_P (insn))
{
rtx pattern;
rtx set;
int skip_nested_if = FALSE;
 
for_each_rtx (&PATTERN (insn), frv_clear_registers_used,
(void *)&tmp_reg->regs);
 
pattern = PATTERN (insn);
if (GET_CODE (pattern) == COND_EXEC)
{
rtx reg = XEXP (COND_EXEC_TEST (pattern), 0);
 
if (reg != sub_cond_exec_reg)
{
sub_cond_exec_reg = reg;
frv_ifcvt.num_nested_cond_exec++;
}
}
 
set = single_set_pattern (pattern);
if (set)
{
rtx dest = SET_DEST (set);
rtx src = SET_SRC (set);
 
if (GET_CODE (dest) == REG)
{
int regno = REGNO (dest);
enum rtx_code src_code = GET_CODE (src);
 
if (CC_P (regno) && src_code == COMPARE)
skip_nested_if = TRUE;
 
else if (CR_P (regno)
&& (src_code == IF_THEN_ELSE
|| COMPARISON_P (src)))
skip_nested_if = TRUE;
}
}
 
if (! skip_nested_if)
for_each_rtx (&PATTERN (insn), frv_clear_registers_used,
(void *)&frv_ifcvt.nested_cc_ok_rewrite);
}
 
if (insn == last_insn)
break;
 
insn = NEXT_INSN (insn);
}
}
 
/* If this is a nested if, rewrite the CC registers that are available to
include the ones that can be rewritten, to increase the chance of being
able to allocate a paired CC/CR register combination. */
if (ce_info->pass > 1)
{
for (j = CC_FIRST; j <= CC_LAST; j++)
if (TEST_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite, j))
SET_HARD_REG_BIT (tmp_reg->regs, j);
else
CLEAR_HARD_REG_BIT (tmp_reg->regs, j);
}
 
if (dump_file)
{
int num_gprs = 0;
fprintf (dump_file, "Available GPRs: ");
 
for (j = GPR_FIRST; j <= GPR_LAST; j++)
if (TEST_HARD_REG_BIT (tmp_reg->regs, j))
{
fprintf (dump_file, " %d [%s]", j, reg_names[j]);
if (++num_gprs > GPR_TEMP_NUM+2)
break;
}
 
fprintf (dump_file, "%s\nAvailable CRs: ",
(num_gprs > GPR_TEMP_NUM+2) ? " ..." : "");
 
for (j = CR_FIRST; j <= CR_LAST; j++)
if (TEST_HARD_REG_BIT (tmp_reg->regs, j))
fprintf (dump_file, " %d [%s]", j, reg_names[j]);
 
fputs ("\n", dump_file);
 
if (ce_info->pass > 1)
{
fprintf (dump_file, "Modifiable CCs: ");
for (j = CC_FIRST; j <= CC_LAST; j++)
if (TEST_HARD_REG_BIT (tmp_reg->regs, j))
fprintf (dump_file, " %d [%s]", j, reg_names[j]);
 
fprintf (dump_file, "\n%d nested COND_EXEC statements\n",
frv_ifcvt.num_nested_cond_exec);
}
}
 
/* Allocate the appropriate temporary condition code register. Try to
allocate the ICR/FCR register that corresponds to the ICC/FCC register so
that conditional cmp's can be done. */
if (mode == CCmode || mode == CC_UNSmode || mode == CC_NZmode)
{
cr_class = ICR_REGS;
cc_class = ICC_REGS;
cc_first = ICC_FIRST;
cc_last = ICC_LAST;
}
else if (mode == CC_FPmode)
{
cr_class = FCR_REGS;
cc_class = FCC_REGS;
cc_first = FCC_FIRST;
cc_last = FCC_LAST;
}
else
{
cc_first = cc_last = 0;
cr_class = cc_class = NO_REGS;
}
 
cc = XEXP (true_expr, 0);
nested_cc = cr = NULL_RTX;
if (cc_class != NO_REGS)
{
/* For nested IFs and &&/||, see if we can find a CC and CR register pair
so we can execute a csubcc/caddcc/cfcmps instruction. */
int cc_regno;
 
for (cc_regno = cc_first; cc_regno <= cc_last; cc_regno++)
{
int cr_regno = cc_regno - CC_FIRST + CR_FIRST;
 
if (TEST_HARD_REG_BIT (frv_ifcvt.tmp_reg.regs, cc_regno)
&& TEST_HARD_REG_BIT (frv_ifcvt.tmp_reg.regs, cr_regno))
{
frv_ifcvt.tmp_reg.next_reg[ (int)cr_class ] = cr_regno;
cr = frv_alloc_temp_reg (tmp_reg, cr_class, CC_CCRmode, TRUE,
TRUE);
 
frv_ifcvt.tmp_reg.next_reg[ (int)cc_class ] = cc_regno;
nested_cc = frv_alloc_temp_reg (tmp_reg, cc_class, CCmode,
TRUE, TRUE);
break;
}
}
}
 
if (! cr)
{
if (dump_file)
fprintf (dump_file, "Could not allocate a CR temporary register\n");
 
goto fail;
}
 
if (dump_file)
fprintf (dump_file,
"Will use %s for conditional execution, %s for nested comparisons\n",
reg_names[ REGNO (cr)],
(nested_cc) ? reg_names[ REGNO (nested_cc) ] : "<none>");
 
/* Set the CCR bit. Note for integer tests, we reverse the condition so that
in an IF-THEN-ELSE sequence, we are testing the TRUE case against the CCR
bit being true. We don't do this for floating point, because of NaNs. */
code = GET_CODE (true_expr);
if (GET_MODE (cc) != CC_FPmode)
{
code = reverse_condition (code);
code_true = EQ;
code_false = NE;
}
else
{
code_true = NE;
code_false = EQ;
}
 
check_insn = gen_rtx_SET (VOIDmode, cr,
gen_rtx_fmt_ee (code, CC_CCRmode, cc, const0_rtx));
 
/* Record the check insn to be inserted later. */
frv_ifcvt_add_insn (check_insn, BB_END (test_bb), TRUE);
 
/* Update the tests. */
frv_ifcvt.cr_reg = cr;
frv_ifcvt.nested_cc_reg = nested_cc;
*p_true = gen_rtx_fmt_ee (code_true, CC_CCRmode, cr, const0_rtx);
*p_false = gen_rtx_fmt_ee (code_false, CC_CCRmode, cr, const0_rtx);
return;
 
/* Fail, don't do this conditional execution. */
fail:
*p_true = NULL_RTX;
*p_false = NULL_RTX;
if (dump_file)
fprintf (dump_file, "Disabling this conditional execution.\n");
 
return;
}
 
/* A C expression to modify the code described by the conditional if
information CE_INFO, for the basic block BB, possibly updating the tests in
TRUE_EXPR, and FALSE_EXPR for converting the && and || parts of if-then or
if-then-else code to conditional instructions. Set either TRUE_EXPR or
FALSE_EXPR to a null pointer if the tests cannot be converted. */
 
/* p_true and p_false are given expressions of the form:
 
(and (eq:CC_CCR (reg:CC_CCR)
(const_int 0))
(eq:CC (reg:CC)
(const_int 0))) */
 
void
frv_ifcvt_modify_multiple_tests (ce_if_block_t *ce_info,
basic_block bb,
rtx *p_true,
rtx *p_false)
{
rtx old_true = XEXP (*p_true, 0);
rtx old_false = XEXP (*p_false, 0);
rtx true_expr = XEXP (*p_true, 1);
rtx false_expr = XEXP (*p_false, 1);
rtx test_expr;
rtx old_test;
rtx cr = XEXP (old_true, 0);
rtx check_insn;
rtx new_cr = NULL_RTX;
rtx *p_new_cr = (rtx *)0;
rtx if_else;
rtx compare;
rtx cc;
enum reg_class cr_class;
enum machine_mode mode = GET_MODE (true_expr);
rtx (*logical_func)(rtx, rtx, rtx);
 
if (TARGET_DEBUG_COND_EXEC)
{
fprintf (stderr,
"\n:::::::::: frv_ifcvt_modify_multiple_tests, before modification for %s\ntrue insn:\n",
ce_info->and_and_p ? "&&" : "||");
 
debug_rtx (*p_true);
 
fputs ("\nfalse insn:\n", stderr);
debug_rtx (*p_false);
}
 
if (!TARGET_MULTI_CE)
goto fail;
 
if (GET_CODE (cr) != REG)
goto fail;
 
if (mode == CCmode || mode == CC_UNSmode || mode == CC_NZmode)
{
cr_class = ICR_REGS;
p_new_cr = &frv_ifcvt.extra_int_cr;
}
else if (mode == CC_FPmode)
{
cr_class = FCR_REGS;
p_new_cr = &frv_ifcvt.extra_fp_cr;
}
else
goto fail;
 
/* Allocate a temp CR, reusing a previously allocated temp CR if we have 3 or
more &&/|| tests. */
new_cr = *p_new_cr;
if (! new_cr)
{
new_cr = *p_new_cr = frv_alloc_temp_reg (&frv_ifcvt.tmp_reg, cr_class,
CC_CCRmode, TRUE, TRUE);
if (! new_cr)
goto fail;
}
 
if (ce_info->and_and_p)
{
old_test = old_false;
test_expr = true_expr;
logical_func = (GET_CODE (old_true) == EQ) ? gen_andcr : gen_andncr;
*p_true = gen_rtx_NE (CC_CCRmode, cr, const0_rtx);
*p_false = gen_rtx_EQ (CC_CCRmode, cr, const0_rtx);
}
else
{
old_test = old_false;
test_expr = false_expr;
logical_func = (GET_CODE (old_false) == EQ) ? gen_orcr : gen_orncr;
*p_true = gen_rtx_EQ (CC_CCRmode, cr, const0_rtx);
*p_false = gen_rtx_NE (CC_CCRmode, cr, const0_rtx);
}
 
/* First add the andcr/andncr/orcr/orncr, which will be added after the
conditional check instruction, due to frv_ifcvt_add_insn being a LIFO
stack. */
frv_ifcvt_add_insn ((*logical_func) (cr, cr, new_cr), BB_END (bb), TRUE);
 
/* Now add the conditional check insn. */
cc = XEXP (test_expr, 0);
compare = gen_rtx_fmt_ee (GET_CODE (test_expr), CC_CCRmode, cc, const0_rtx);
if_else = gen_rtx_IF_THEN_ELSE (CC_CCRmode, old_test, compare, const0_rtx);
 
check_insn = gen_rtx_SET (VOIDmode, new_cr, if_else);
 
/* Add the new check insn to the list of check insns that need to be
inserted. */
frv_ifcvt_add_insn (check_insn, BB_END (bb), TRUE);
 
if (TARGET_DEBUG_COND_EXEC)
{
fputs ("\n:::::::::: frv_ifcvt_modify_multiple_tests, after modification\ntrue insn:\n",
stderr);
 
debug_rtx (*p_true);
 
fputs ("\nfalse insn:\n", stderr);
debug_rtx (*p_false);
}
 
return;
 
fail:
*p_true = *p_false = NULL_RTX;
 
/* If we allocated a CR register, release it. */
if (new_cr)
{
CLEAR_HARD_REG_BIT (frv_ifcvt.tmp_reg.regs, REGNO (new_cr));
*p_new_cr = NULL_RTX;
}
 
if (TARGET_DEBUG_COND_EXEC)
fputs ("\n:::::::::: frv_ifcvt_modify_multiple_tests, failed.\n", stderr);
 
return;
}
 
/* Return a register which will be loaded with a value if an IF block is
converted to conditional execution. This is used to rewrite instructions
that use constants to ones that just use registers. */
 
static rtx
frv_ifcvt_load_value (rtx value, rtx insn ATTRIBUTE_UNUSED)
{
int num_alloc = frv_ifcvt.cur_scratch_regs;
int i;
rtx reg;
 
/* We know gr0 == 0, so replace any errant uses. */
if (value == const0_rtx)
return gen_rtx_REG (SImode, GPR_FIRST);
 
/* First search all registers currently loaded to see if we have an
applicable constant. */
if (CONSTANT_P (value)
|| (GET_CODE (value) == REG && REGNO (value) == LR_REGNO))
{
for (i = 0; i < num_alloc; i++)
{
if (rtx_equal_p (SET_SRC (frv_ifcvt.scratch_regs[i]), value))
return SET_DEST (frv_ifcvt.scratch_regs[i]);
}
}
 
/* Have we exhausted the number of registers available? */
if (num_alloc >= GPR_TEMP_NUM)
{
if (dump_file)
fprintf (dump_file, "Too many temporary registers allocated\n");
 
return NULL_RTX;
}
 
/* Allocate the new register. */
reg = frv_alloc_temp_reg (&frv_ifcvt.tmp_reg, GPR_REGS, SImode, TRUE, TRUE);
if (! reg)
{
if (dump_file)
fputs ("Could not find a scratch register\n", dump_file);
 
return NULL_RTX;
}
 
frv_ifcvt.cur_scratch_regs++;
frv_ifcvt.scratch_regs[num_alloc] = gen_rtx_SET (VOIDmode, reg, value);
 
if (dump_file)
{
if (GET_CODE (value) == CONST_INT)
fprintf (dump_file, "Register %s will hold %ld\n",
reg_names[ REGNO (reg)], (long)INTVAL (value));
 
else if (GET_CODE (value) == REG && REGNO (value) == LR_REGNO)
fprintf (dump_file, "Register %s will hold LR\n",
reg_names[ REGNO (reg)]);
 
else
fprintf (dump_file, "Register %s will hold a saved value\n",
reg_names[ REGNO (reg)]);
}
 
return reg;
}
 
/* Update a MEM used in conditional code that might contain an offset to put
the offset into a scratch register, so that the conditional load/store
operations can be used. This function returns the original pointer if the
MEM is valid to use in conditional code, NULL if we can't load up the offset
into a temporary register, or the new MEM if we were successful. */
 
static rtx
frv_ifcvt_rewrite_mem (rtx mem, enum machine_mode mode, rtx insn)
{
rtx addr = XEXP (mem, 0);
 
if (!frv_legitimate_address_p (mode, addr, reload_completed, TRUE, FALSE))
{
if (GET_CODE (addr) == PLUS)
{
rtx addr_op0 = XEXP (addr, 0);
rtx addr_op1 = XEXP (addr, 1);
 
if (GET_CODE (addr_op0) == REG && CONSTANT_P (addr_op1))
{
rtx reg = frv_ifcvt_load_value (addr_op1, insn);
if (!reg)
return NULL_RTX;
 
addr = gen_rtx_PLUS (Pmode, addr_op0, reg);
}
 
else
return NULL_RTX;
}
 
else if (CONSTANT_P (addr))
addr = frv_ifcvt_load_value (addr, insn);
 
else
return NULL_RTX;
 
if (addr == NULL_RTX)
return NULL_RTX;
 
else if (XEXP (mem, 0) != addr)
return change_address (mem, mode, addr);
}
 
return mem;
}
 
/* Given a PATTERN, return a SET expression if this PATTERN has only a single
SET, possibly conditionally executed. It may also have CLOBBERs, USEs. */
 
static rtx
single_set_pattern (rtx pattern)
{
rtx set;
int i;
 
if (GET_CODE (pattern) == COND_EXEC)
pattern = COND_EXEC_CODE (pattern);
 
if (GET_CODE (pattern) == SET)
return pattern;
 
else if (GET_CODE (pattern) == PARALLEL)
{
for (i = 0, set = 0; i < XVECLEN (pattern, 0); i++)
{
rtx sub = XVECEXP (pattern, 0, i);
 
switch (GET_CODE (sub))
{
case USE:
case CLOBBER:
break;
 
case SET:
if (set)
return 0;
else
set = sub;
break;
 
default:
return 0;
}
}
return set;
}
 
return 0;
}
 
/* A C expression to modify the code described by the conditional if
information CE_INFO with the new PATTERN in INSN. If PATTERN is a null
pointer after the IFCVT_MODIFY_INSN macro executes, it is assumed that that
insn cannot be converted to be executed conditionally. */
 
rtx
frv_ifcvt_modify_insn (ce_if_block_t *ce_info,
rtx pattern,
rtx insn)
{
rtx orig_ce_pattern = pattern;
rtx set;
rtx op0;
rtx op1;
rtx test;
 
gcc_assert (GET_CODE (pattern) == COND_EXEC);
 
test = COND_EXEC_TEST (pattern);
if (GET_CODE (test) == AND)
{
rtx cr = frv_ifcvt.cr_reg;
rtx test_reg;
 
op0 = XEXP (test, 0);
if (! rtx_equal_p (cr, XEXP (op0, 0)))
goto fail;
 
op1 = XEXP (test, 1);
test_reg = XEXP (op1, 0);
if (GET_CODE (test_reg) != REG)
goto fail;
 
/* Is this the first nested if block in this sequence? If so, generate
an andcr or andncr. */
if (! frv_ifcvt.last_nested_if_cr)
{
rtx and_op;
 
frv_ifcvt.last_nested_if_cr = test_reg;
if (GET_CODE (op0) == NE)
and_op = gen_andcr (test_reg, cr, test_reg);
else
and_op = gen_andncr (test_reg, cr, test_reg);
 
frv_ifcvt_add_insn (and_op, insn, TRUE);
}
 
/* If this isn't the first statement in the nested if sequence, see if we
are dealing with the same register. */
else if (! rtx_equal_p (test_reg, frv_ifcvt.last_nested_if_cr))
goto fail;
 
COND_EXEC_TEST (pattern) = test = op1;
}
 
/* If this isn't a nested if, reset state variables. */
else
{
frv_ifcvt.last_nested_if_cr = NULL_RTX;
}
 
set = single_set_pattern (pattern);
if (set)
{
rtx dest = SET_DEST (set);
rtx src = SET_SRC (set);
enum machine_mode mode = GET_MODE (dest);
 
/* Check for normal binary operators. */
if (mode == SImode && ARITHMETIC_P (src))
{
op0 = XEXP (src, 0);
op1 = XEXP (src, 1);
 
if (integer_register_operand (op0, SImode) && CONSTANT_P (op1))
{
op1 = frv_ifcvt_load_value (op1, insn);
if (op1)
COND_EXEC_CODE (pattern)
= gen_rtx_SET (VOIDmode, dest, gen_rtx_fmt_ee (GET_CODE (src),
GET_MODE (src),
op0, op1));
else
goto fail;
}
}
 
/* For multiply by a constant, we need to handle the sign extending
correctly. Add a USE of the value after the multiply to prevent flow
from cratering because only one register out of the two were used. */
else if (mode == DImode && GET_CODE (src) == MULT)
{
op0 = XEXP (src, 0);
op1 = XEXP (src, 1);
if (GET_CODE (op0) == SIGN_EXTEND && GET_CODE (op1) == CONST_INT)
{
op1 = frv_ifcvt_load_value (op1, insn);
if (op1)
{
op1 = gen_rtx_SIGN_EXTEND (DImode, op1);
COND_EXEC_CODE (pattern)
= gen_rtx_SET (VOIDmode, dest,
gen_rtx_MULT (DImode, op0, op1));
}
else
goto fail;
}
 
frv_ifcvt_add_insn (gen_rtx_USE (VOIDmode, dest), insn, FALSE);
}
 
/* If we are just loading a constant created for a nested conditional
execution statement, just load the constant without any conditional
execution, since we know that the constant will not interfere with any
other registers. */
else if (frv_ifcvt.scratch_insns_bitmap
&& bitmap_bit_p (frv_ifcvt.scratch_insns_bitmap,
INSN_UID (insn))
&& REG_P (SET_DEST (set))
/* We must not unconditionally set a scratch reg chosen
for a nested if-converted block if its incoming
value from the TEST block (or the result of the THEN
branch) could/should propagate to the JOIN block.
It suffices to test whether the register is live at
the JOIN point: if it's live there, we can infer
that we set it in the former JOIN block of the
nested if-converted block (otherwise it wouldn't
have been available as a scratch register), and it
is either propagated through or set in the other
conditional block. It's probably not worth trying
to catch the latter case, and it could actually
limit scheduling of the combined block quite
severely. */
&& ce_info->join_bb
&& ! (REGNO_REG_SET_P
(ce_info->join_bb->il.rtl->global_live_at_start,
REGNO (SET_DEST (set))))
/* Similarly, we must not unconditionally set a reg
used as scratch in the THEN branch if the same reg
is live in the ELSE branch. */
&& (! ce_info->else_bb
|| BLOCK_FOR_INSN (insn) == ce_info->else_bb
|| ! (REGNO_REG_SET_P
(ce_info->else_bb->il.rtl->global_live_at_start,
REGNO (SET_DEST (set))))))
pattern = set;
 
else if (mode == QImode || mode == HImode || mode == SImode
|| mode == SFmode)
{
int changed_p = FALSE;
 
/* Check for just loading up a constant */
if (CONSTANT_P (src) && integer_register_operand (dest, mode))
{
src = frv_ifcvt_load_value (src, insn);
if (!src)
goto fail;
 
changed_p = TRUE;
}
 
/* See if we need to fix up stores */
if (GET_CODE (dest) == MEM)
{
rtx new_mem = frv_ifcvt_rewrite_mem (dest, mode, insn);
 
if (!new_mem)
goto fail;
 
else if (new_mem != dest)
{
changed_p = TRUE;
dest = new_mem;
}
}
 
/* See if we need to fix up loads */
if (GET_CODE (src) == MEM)
{
rtx new_mem = frv_ifcvt_rewrite_mem (src, mode, insn);
 
if (!new_mem)
goto fail;
 
else if (new_mem != src)
{
changed_p = TRUE;
src = new_mem;
}
}
 
/* If either src or destination changed, redo SET. */
if (changed_p)
COND_EXEC_CODE (pattern) = gen_rtx_SET (VOIDmode, dest, src);
}
 
/* Rewrite a nested set cccr in terms of IF_THEN_ELSE. Also deal with
rewriting the CC register to be the same as the paired CC/CR register
for nested ifs. */
else if (mode == CC_CCRmode && COMPARISON_P (src))
{
int regno = REGNO (XEXP (src, 0));
rtx if_else;
 
if (ce_info->pass > 1
&& regno != (int)REGNO (frv_ifcvt.nested_cc_reg)
&& TEST_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite, regno))
{
src = gen_rtx_fmt_ee (GET_CODE (src),
CC_CCRmode,
frv_ifcvt.nested_cc_reg,
XEXP (src, 1));
}
 
if_else = gen_rtx_IF_THEN_ELSE (CC_CCRmode, test, src, const0_rtx);
pattern = gen_rtx_SET (VOIDmode, dest, if_else);
}
 
/* Remap a nested compare instruction to use the paired CC/CR reg. */
else if (ce_info->pass > 1
&& GET_CODE (dest) == REG
&& CC_P (REGNO (dest))
&& REGNO (dest) != REGNO (frv_ifcvt.nested_cc_reg)
&& TEST_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite,
REGNO (dest))
&& GET_CODE (src) == COMPARE)
{
PUT_MODE (frv_ifcvt.nested_cc_reg, GET_MODE (dest));
COND_EXEC_CODE (pattern)
= gen_rtx_SET (VOIDmode, frv_ifcvt.nested_cc_reg, copy_rtx (src));
}
}
 
if (TARGET_DEBUG_COND_EXEC)
{
rtx orig_pattern = PATTERN (insn);
 
PATTERN (insn) = pattern;
fprintf (stderr,
"\n:::::::::: frv_ifcvt_modify_insn: pass = %d, insn after modification:\n",
ce_info->pass);
 
debug_rtx (insn);
PATTERN (insn) = orig_pattern;
}
 
return pattern;
 
fail:
if (TARGET_DEBUG_COND_EXEC)
{
rtx orig_pattern = PATTERN (insn);
 
PATTERN (insn) = orig_ce_pattern;
fprintf (stderr,
"\n:::::::::: frv_ifcvt_modify_insn: pass = %d, insn could not be modified:\n",
ce_info->pass);
 
debug_rtx (insn);
PATTERN (insn) = orig_pattern;
}
 
return NULL_RTX;
}
 
/* A C expression to perform any final machine dependent modifications in
converting code to conditional execution in the code described by the
conditional if information CE_INFO. */
 
void
frv_ifcvt_modify_final (ce_if_block_t *ce_info ATTRIBUTE_UNUSED)
{
rtx existing_insn;
rtx check_insn;
rtx p = frv_ifcvt.added_insns_list;
int i;
 
/* Loop inserting the check insns. The last check insn is the first test,
and is the appropriate place to insert constants. */
gcc_assert (p);
 
do
{
rtx check_and_insert_insns = XEXP (p, 0);
rtx old_p = p;
 
check_insn = XEXP (check_and_insert_insns, 0);
existing_insn = XEXP (check_and_insert_insns, 1);
p = XEXP (p, 1);
 
/* The jump bit is used to say that the new insn is to be inserted BEFORE
the existing insn, otherwise it is to be inserted AFTER. */
if (check_and_insert_insns->jump)
{
emit_insn_before (check_insn, existing_insn);
check_and_insert_insns->jump = 0;
}
else
emit_insn_after (check_insn, existing_insn);
 
free_EXPR_LIST_node (check_and_insert_insns);
free_EXPR_LIST_node (old_p);
}
while (p != NULL_RTX);
 
/* Load up any constants needed into temp gprs */
for (i = 0; i < frv_ifcvt.cur_scratch_regs; i++)
{
rtx insn = emit_insn_before (frv_ifcvt.scratch_regs[i], existing_insn);
if (! frv_ifcvt.scratch_insns_bitmap)
frv_ifcvt.scratch_insns_bitmap = BITMAP_ALLOC (NULL);
bitmap_set_bit (frv_ifcvt.scratch_insns_bitmap, INSN_UID (insn));
frv_ifcvt.scratch_regs[i] = NULL_RTX;
}
 
frv_ifcvt.added_insns_list = NULL_RTX;
frv_ifcvt.cur_scratch_regs = 0;
}
 
/* A C expression to cancel any machine dependent modifications in converting
code to conditional execution in the code described by the conditional if
information CE_INFO. */
 
void
frv_ifcvt_modify_cancel (ce_if_block_t *ce_info ATTRIBUTE_UNUSED)
{
int i;
rtx p = frv_ifcvt.added_insns_list;
 
/* Loop freeing up the EXPR_LIST's allocated. */
while (p != NULL_RTX)
{
rtx check_and_jump = XEXP (p, 0);
rtx old_p = p;
 
p = XEXP (p, 1);
free_EXPR_LIST_node (check_and_jump);
free_EXPR_LIST_node (old_p);
}
 
/* Release any temporary gprs allocated. */
for (i = 0; i < frv_ifcvt.cur_scratch_regs; i++)
frv_ifcvt.scratch_regs[i] = NULL_RTX;
 
frv_ifcvt.added_insns_list = NULL_RTX;
frv_ifcvt.cur_scratch_regs = 0;
return;
}
/* A C expression for the size in bytes of the trampoline, as an integer.
The template is:
 
setlo #0, <jmp_reg>
setlo #0, <static_chain>
sethi #0, <jmp_reg>
sethi #0, <static_chain>
jmpl @(gr0,<jmp_reg>) */
 
int
frv_trampoline_size (void)
{
if (TARGET_FDPIC)
/* Allocate room for the function descriptor and the lddi
instruction. */
return 8 + 6 * 4;
return 5 /* instructions */ * 4 /* instruction size. */;
}
 
/* A C statement to initialize the variable parts of a trampoline. ADDR is an
RTX for the address of the trampoline; FNADDR is an RTX for the address of
the nested function; STATIC_CHAIN is an RTX for the static chain value that
should be passed to the function when it is called.
 
The template is:
 
setlo #0, <jmp_reg>
setlo #0, <static_chain>
sethi #0, <jmp_reg>
sethi #0, <static_chain>
jmpl @(gr0,<jmp_reg>) */
 
void
frv_initialize_trampoline (rtx addr, rtx fnaddr, rtx static_chain)
{
rtx sc_reg = force_reg (Pmode, static_chain);
 
emit_library_call (gen_rtx_SYMBOL_REF (SImode, "__trampoline_setup"),
FALSE, VOIDmode, 4,
addr, Pmode,
GEN_INT (frv_trampoline_size ()), SImode,
fnaddr, Pmode,
sc_reg, Pmode);
}
 
/* Many machines have some registers that cannot be copied directly to or from
memory or even from other types of registers. An example is the `MQ'
register, which on most machines, can only be copied to or from general
registers, but not memory. Some machines allow copying all registers to and
from memory, but require a scratch register for stores to some memory
locations (e.g., those with symbolic address on the RT, and those with
certain symbolic address on the SPARC when compiling PIC). In some cases,
both an intermediate and a scratch register are required.
 
You should define these macros to indicate to the reload phase that it may
need to allocate at least one register for a reload in addition to the
register to contain the data. Specifically, if copying X to a register
CLASS in MODE requires an intermediate register, you should define
`SECONDARY_INPUT_RELOAD_CLASS' to return the largest register class all of
whose registers can be used as intermediate registers or scratch registers.
 
If copying a register CLASS in MODE to X requires an intermediate or scratch
register, `SECONDARY_OUTPUT_RELOAD_CLASS' should be defined to return the
largest register class required. If the requirements for input and output
reloads are the same, the macro `SECONDARY_RELOAD_CLASS' should be used
instead of defining both macros identically.
 
The values returned by these macros are often `GENERAL_REGS'. Return
`NO_REGS' if no spare register is needed; i.e., if X can be directly copied
to or from a register of CLASS in MODE without requiring a scratch register.
Do not define this macro if it would always return `NO_REGS'.
 
If a scratch register is required (either with or without an intermediate
register), you should define patterns for `reload_inM' or `reload_outM', as
required.. These patterns, which will normally be implemented with a
`define_expand', should be similar to the `movM' patterns, except that
operand 2 is the scratch register.
 
Define constraints for the reload register and scratch register that contain
a single register class. If the original reload register (whose class is
CLASS) can meet the constraint given in the pattern, the value returned by
these macros is used for the class of the scratch register. Otherwise, two
additional reload registers are required. Their classes are obtained from
the constraints in the insn pattern.
 
X might be a pseudo-register or a `subreg' of a pseudo-register, which could
either be in a hard register or in memory. Use `true_regnum' to find out;
it will return -1 if the pseudo is in memory and the hard register number if
it is in a register.
 
These macros should not be used in the case where a particular class of
registers can only be copied to memory and not to another class of
registers. In that case, secondary reload registers are not needed and
would not be helpful. Instead, a stack location must be used to perform the
copy and the `movM' pattern should use memory as an intermediate storage.
This case often occurs between floating-point and general registers. */
 
enum reg_class
frv_secondary_reload_class (enum reg_class class,
enum machine_mode mode ATTRIBUTE_UNUSED,
rtx x,
int in_p ATTRIBUTE_UNUSED)
{
enum reg_class ret;
 
switch (class)
{
default:
ret = NO_REGS;
break;
 
/* Accumulators/Accumulator guard registers need to go through floating
point registers. */
case QUAD_REGS:
case EVEN_REGS:
case GPR_REGS:
ret = NO_REGS;
if (x && GET_CODE (x) == REG)
{
int regno = REGNO (x);
 
if (ACC_P (regno) || ACCG_P (regno))
ret = FPR_REGS;
}
break;
 
/* Nonzero constants should be loaded into an FPR through a GPR. */
case QUAD_FPR_REGS:
case FEVEN_REGS:
case FPR_REGS:
if (x && CONSTANT_P (x) && !ZERO_P (x))
ret = GPR_REGS;
else
ret = NO_REGS;
break;
 
/* All of these types need gpr registers. */
case ICC_REGS:
case FCC_REGS:
case CC_REGS:
case ICR_REGS:
case FCR_REGS:
case CR_REGS:
case LCR_REG:
case LR_REG:
ret = GPR_REGS;
break;
 
/* The accumulators need fpr registers */
case ACC_REGS:
case EVEN_ACC_REGS:
case QUAD_ACC_REGS:
case ACCG_REGS:
ret = FPR_REGS;
break;
}
 
return ret;
}
 
/* A C expression whose value is nonzero if pseudos that have been assigned to
registers of class CLASS would likely be spilled because registers of CLASS
are needed for spill registers.
 
The default value of this macro returns 1 if CLASS has exactly one register
and zero otherwise. On most machines, this default should be used. Only
define this macro to some other expression if pseudo allocated by
`local-alloc.c' end up in memory because their hard registers were needed
for spill registers. If this macro returns nonzero for those classes, those
pseudos will only be allocated by `global.c', which knows how to reallocate
the pseudo to another register. If there would not be another register
available for reallocation, you should not change the definition of this
macro since the only effect of such a definition would be to slow down
register allocation. */
 
int
frv_class_likely_spilled_p (enum reg_class class)
{
switch (class)
{
default:
break;
 
case GR8_REGS:
case GR9_REGS:
case GR89_REGS:
case FDPIC_FPTR_REGS:
case FDPIC_REGS:
case ICC_REGS:
case FCC_REGS:
case CC_REGS:
case ICR_REGS:
case FCR_REGS:
case CR_REGS:
case LCR_REG:
case LR_REG:
case SPR_REGS:
case QUAD_ACC_REGS:
case EVEN_ACC_REGS:
case ACC_REGS:
case ACCG_REGS:
return TRUE;
}
 
return FALSE;
}
 
/* An expression for the alignment of a structure field FIELD if the
alignment computed in the usual way is COMPUTED. GCC uses this
value instead of the value in `BIGGEST_ALIGNMENT' or
`BIGGEST_FIELD_ALIGNMENT', if defined, for structure fields only. */
 
/* The definition type of the bit field data is either char, short, long or
long long. The maximum bit size is the number of bits of its own type.
 
The bit field data is assigned to a storage unit that has an adequate size
for bit field data retention and is located at the smallest address.
 
Consecutive bit field data are packed at consecutive bits having the same
storage unit, with regard to the type, beginning with the MSB and continuing
toward the LSB.
 
If a field to be assigned lies over a bit field type boundary, its
assignment is completed by aligning it with a boundary suitable for the
type.
 
When a bit field having a bit length of 0 is declared, it is forcibly
assigned to the next storage unit.
 
e.g)
struct {
int a:2;
int b:6;
char c:4;
int d:10;
int :0;
int f:2;
} x;
 
+0 +1 +2 +3
&x 00000000 00000000 00000000 00000000
MLM----L
a b
&x+4 00000000 00000000 00000000 00000000
M--L
c
&x+8 00000000 00000000 00000000 00000000
M----------L
d
&x+12 00000000 00000000 00000000 00000000
ML
f
*/
 
int
frv_adjust_field_align (tree field, int computed)
{
/* Make sure that the bitfield is not wider than the type. */
if (DECL_BIT_FIELD (field)
&& !DECL_ARTIFICIAL (field))
{
tree parent = DECL_CONTEXT (field);
tree prev = NULL_TREE;
tree cur;
 
for (cur = TYPE_FIELDS (parent); cur && cur != field; cur = TREE_CHAIN (cur))
{
if (TREE_CODE (cur) != FIELD_DECL)
continue;
 
prev = cur;
}
 
gcc_assert (cur);
 
/* If this isn't a :0 field and if the previous element is a bitfield
also, see if the type is different, if so, we will need to align the
bit-field to the next boundary. */
if (prev
&& ! DECL_PACKED (field)
&& ! integer_zerop (DECL_SIZE (field))
&& DECL_BIT_FIELD_TYPE (field) != DECL_BIT_FIELD_TYPE (prev))
{
int prev_align = TYPE_ALIGN (TREE_TYPE (prev));
int cur_align = TYPE_ALIGN (TREE_TYPE (field));
computed = (prev_align > cur_align) ? prev_align : cur_align;
}
}
 
return computed;
}
 
/* A C expression that is nonzero if it is permissible to store a value of mode
MODE in hard register number REGNO (or in several registers starting with
that one). For a machine where all registers are equivalent, a suitable
definition is
 
#define HARD_REGNO_MODE_OK(REGNO, MODE) 1
 
It is not necessary for this macro to check for the numbers of fixed
registers, because the allocation mechanism considers them to be always
occupied.
 
On some machines, double-precision values must be kept in even/odd register
pairs. The way to implement that is to define this macro to reject odd
register numbers for such modes.
 
The minimum requirement for a mode to be OK in a register is that the
`movMODE' instruction pattern support moves between the register and any
other hard register for which the mode is OK; and that moving a value into
the register and back out not alter it.
 
Since the same instruction used to move `SImode' will work for all narrower
integer modes, it is not necessary on any machine for `HARD_REGNO_MODE_OK'
to distinguish between these modes, provided you define patterns `movhi',
etc., to take advantage of this. This is useful because of the interaction
between `HARD_REGNO_MODE_OK' and `MODES_TIEABLE_P'; it is very desirable for
all integer modes to be tieable.
 
Many machines have special registers for floating point arithmetic. Often
people assume that floating point machine modes are allowed only in floating
point registers. This is not true. Any registers that can hold integers
can safely *hold* a floating point machine mode, whether or not floating
arithmetic can be done on it in those registers. Integer move instructions
can be used to move the values.
 
On some machines, though, the converse is true: fixed-point machine modes
may not go in floating registers. This is true if the floating registers
normalize any value stored in them, because storing a non-floating value
there would garble it. In this case, `HARD_REGNO_MODE_OK' should reject
fixed-point machine modes in floating registers. But if the floating
registers do not automatically normalize, if you can store any bit pattern
in one and retrieve it unchanged without a trap, then any machine mode may
go in a floating register, so you can define this macro to say so.
 
The primary significance of special floating registers is rather that they
are the registers acceptable in floating point arithmetic instructions.
However, this is of no concern to `HARD_REGNO_MODE_OK'. You handle it by
writing the proper constraints for those instructions.
 
On some machines, the floating registers are especially slow to access, so
that it is better to store a value in a stack frame than in such a register
if floating point arithmetic is not being done. As long as the floating
registers are not in class `GENERAL_REGS', they will not be used unless some
pattern's constraint asks for one. */
 
int
frv_hard_regno_mode_ok (int regno, enum machine_mode mode)
{
int base;
int mask;
 
switch (mode)
{
case CCmode:
case CC_UNSmode:
case CC_NZmode:
return ICC_P (regno) || GPR_P (regno);
 
case CC_CCRmode:
return CR_P (regno) || GPR_P (regno);
 
case CC_FPmode:
return FCC_P (regno) || GPR_P (regno);
 
default:
break;
}
 
/* Set BASE to the first register in REGNO's class. Set MASK to the
bits that must be clear in (REGNO - BASE) for the register to be
well-aligned. */
if (INTEGRAL_MODE_P (mode) || FLOAT_MODE_P (mode) || VECTOR_MODE_P (mode))
{
if (ACCG_P (regno))
{
/* ACCGs store one byte. Two-byte quantities must start in
even-numbered registers, four-byte ones in registers whose
numbers are divisible by four, and so on. */
base = ACCG_FIRST;
mask = GET_MODE_SIZE (mode) - 1;
}
else
{
/* The other registers store one word. */
if (GPR_P (regno) || regno == AP_FIRST)
base = GPR_FIRST;
 
else if (FPR_P (regno))
base = FPR_FIRST;
 
else if (ACC_P (regno))
base = ACC_FIRST;
 
else if (SPR_P (regno))
return mode == SImode;
 
/* Fill in the table. */
else
return 0;
 
/* Anything smaller than an SI is OK in any word-sized register. */
if (GET_MODE_SIZE (mode) < 4)
return 1;
 
mask = (GET_MODE_SIZE (mode) / 4) - 1;
}
return (((regno - base) & mask) == 0);
}
 
return 0;
}
 
/* A C expression for the number of consecutive hard registers, starting at
register number REGNO, required to hold a value of mode MODE.
 
On a machine where all registers are exactly one word, a suitable definition
of this macro is
 
#define HARD_REGNO_NREGS(REGNO, MODE) \
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) \
/ UNITS_PER_WORD)) */
 
/* On the FRV, make the CC_FP mode take 3 words in the integer registers, so
that we can build the appropriate instructions to properly reload the
values. Also, make the byte-sized accumulator guards use one guard
for each byte. */
 
int
frv_hard_regno_nregs (int regno, enum machine_mode mode)
{
if (ACCG_P (regno))
return GET_MODE_SIZE (mode);
else
return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
}
 
/* A C expression for the maximum number of consecutive registers of
class CLASS needed to hold a value of mode MODE.
 
This is closely related to the macro `HARD_REGNO_NREGS'. In fact, the value
of the macro `CLASS_MAX_NREGS (CLASS, MODE)' should be the maximum value of
`HARD_REGNO_NREGS (REGNO, MODE)' for all REGNO values in the class CLASS.
 
This macro helps control the handling of multiple-word values in
the reload pass.
 
This declaration is required. */
 
int
frv_class_max_nregs (enum reg_class class, enum machine_mode mode)
{
if (class == ACCG_REGS)
/* An N-byte value requires N accumulator guards. */
return GET_MODE_SIZE (mode);
else
return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
}
 
/* A C expression that is nonzero if X is a legitimate constant for an
immediate operand on the target machine. You can assume that X satisfies
`CONSTANT_P', so you need not check this. In fact, `1' is a suitable
definition for this macro on machines where anything `CONSTANT_P' is valid. */
 
int
frv_legitimate_constant_p (rtx x)
{
enum machine_mode mode = GET_MODE (x);
 
/* frv_cannot_force_const_mem always returns true for FDPIC. This
means that the move expanders will be expected to deal with most
kinds of constant, regardless of what we return here.
 
However, among its other duties, LEGITIMATE_CONSTANT_P decides whether
a constant can be entered into reg_equiv_constant[]. If we return true,
reload can create new instances of the constant whenever it likes.
 
The idea is therefore to accept as many constants as possible (to give
reload more freedom) while rejecting constants that can only be created
at certain times. In particular, anything with a symbolic component will
require use of the pseudo FDPIC register, which is only available before
reload. */
if (TARGET_FDPIC)
return LEGITIMATE_PIC_OPERAND_P (x);
 
/* All of the integer constants are ok. */
if (GET_CODE (x) != CONST_DOUBLE)
return TRUE;
 
/* double integer constants are ok. */
if (mode == VOIDmode || mode == DImode)
return TRUE;
 
/* 0 is always ok. */
if (x == CONST0_RTX (mode))
return TRUE;
 
/* If floating point is just emulated, allow any constant, since it will be
constructed in the GPRs. */
if (!TARGET_HAS_FPRS)
return TRUE;
 
if (mode == DFmode && !TARGET_DOUBLE)
return TRUE;
 
/* Otherwise store the constant away and do a load. */
return FALSE;
}
 
/* Implement SELECT_CC_MODE. Choose CC_FP for floating-point comparisons,
CC_NZ for comparisons against zero in which a single Z or N flag test
is enough, CC_UNS for other unsigned comparisons, and CC for other
signed comparisons. */
 
enum machine_mode
frv_select_cc_mode (enum rtx_code code, rtx x, rtx y)
{
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
return CC_FPmode;
 
switch (code)
{
case EQ:
case NE:
case LT:
case GE:
return y == const0_rtx ? CC_NZmode : CCmode;
 
case GTU:
case GEU:
case LTU:
case LEU:
return y == const0_rtx ? CC_NZmode : CC_UNSmode;
 
default:
return CCmode;
}
}
/* A C expression for the cost of moving data from a register in class FROM to
one in class TO. The classes are expressed using the enumeration values
such as `GENERAL_REGS'. A value of 4 is the default; other values are
interpreted relative to that.
 
It is not required that the cost always equal 2 when FROM is the same as TO;
on some machines it is expensive to move between registers if they are not
general registers.
 
If reload sees an insn consisting of a single `set' between two hard
registers, and if `REGISTER_MOVE_COST' applied to their classes returns a
value of 2, reload does not check to ensure that the constraints of the insn
are met. Setting a cost of other than 2 will allow reload to verify that
the constraints are met. You should do this if the `movM' pattern's
constraints do not allow such copying. */
 
#define HIGH_COST 40
#define MEDIUM_COST 3
#define LOW_COST 1
 
int
frv_register_move_cost (enum reg_class from, enum reg_class to)
{
switch (from)
{
default:
break;
 
case QUAD_REGS:
case EVEN_REGS:
case GPR_REGS:
switch (to)
{
default:
break;
 
case QUAD_REGS:
case EVEN_REGS:
case GPR_REGS:
return LOW_COST;
 
case FEVEN_REGS:
case FPR_REGS:
return LOW_COST;
 
case LCR_REG:
case LR_REG:
case SPR_REGS:
return LOW_COST;
}
 
case FEVEN_REGS:
case FPR_REGS:
switch (to)
{
default:
break;
 
case QUAD_REGS:
case EVEN_REGS:
case GPR_REGS:
case ACC_REGS:
case EVEN_ACC_REGS:
case QUAD_ACC_REGS:
case ACCG_REGS:
return MEDIUM_COST;
 
case FEVEN_REGS:
case FPR_REGS:
return LOW_COST;
}
 
case LCR_REG:
case LR_REG:
case SPR_REGS:
switch (to)
{
default:
break;
 
case QUAD_REGS:
case EVEN_REGS:
case GPR_REGS:
return MEDIUM_COST;
}
 
case ACC_REGS:
case EVEN_ACC_REGS:
case QUAD_ACC_REGS:
case ACCG_REGS:
switch (to)
{
default:
break;
 
case FEVEN_REGS:
case FPR_REGS:
return MEDIUM_COST;
 
}
}
 
return HIGH_COST;
}
/* Implementation of TARGET_ASM_INTEGER. In the FRV case we need to
use ".picptr" to generate safe relocations for PIC code. We also
need a fixup entry for aligned (non-debugging) code. */
 
static bool
frv_assemble_integer (rtx value, unsigned int size, int aligned_p)
{
if ((flag_pic || TARGET_FDPIC) && size == UNITS_PER_WORD)
{
if (GET_CODE (value) == CONST
|| GET_CODE (value) == SYMBOL_REF
|| GET_CODE (value) == LABEL_REF)
{
if (TARGET_FDPIC && GET_CODE (value) == SYMBOL_REF
&& SYMBOL_REF_FUNCTION_P (value))
{
fputs ("\t.picptr\tfuncdesc(", asm_out_file);
output_addr_const (asm_out_file, value);
fputs (")\n", asm_out_file);
return true;
}
else if (TARGET_FDPIC && GET_CODE (value) == CONST
&& frv_function_symbol_referenced_p (value))
return false;
if (aligned_p && !TARGET_FDPIC)
{
static int label_num = 0;
char buf[256];
const char *p;
 
ASM_GENERATE_INTERNAL_LABEL (buf, "LCP", label_num++);
p = (* targetm.strip_name_encoding) (buf);
 
fprintf (asm_out_file, "%s:\n", p);
fprintf (asm_out_file, "%s\n", FIXUP_SECTION_ASM_OP);
fprintf (asm_out_file, "\t.picptr\t%s\n", p);
fprintf (asm_out_file, "\t.previous\n");
}
assemble_integer_with_op ("\t.picptr\t", value);
return true;
}
if (!aligned_p)
{
/* We've set the unaligned SI op to NULL, so we always have to
handle the unaligned case here. */
assemble_integer_with_op ("\t.4byte\t", value);
return true;
}
}
return default_assemble_integer (value, size, aligned_p);
}
 
/* Function to set up the backend function structure. */
 
static struct machine_function *
frv_init_machine_status (void)
{
return ggc_alloc_cleared (sizeof (struct machine_function));
}
/* Implement TARGET_SCHED_ISSUE_RATE. */
 
int
frv_issue_rate (void)
{
if (!TARGET_PACK)
return 1;
 
switch (frv_cpu_type)
{
default:
case FRV_CPU_FR300:
case FRV_CPU_SIMPLE:
return 1;
 
case FRV_CPU_FR400:
case FRV_CPU_FR405:
case FRV_CPU_FR450:
return 2;
 
case FRV_CPU_GENERIC:
case FRV_CPU_FR500:
case FRV_CPU_TOMCAT:
return 4;
 
case FRV_CPU_FR550:
return 8;
}
}
/* A for_each_rtx callback. If X refers to an accumulator, return
ACC_GROUP_ODD if the bit 2 of the register number is set and
ACC_GROUP_EVEN if it is clear. Return 0 (ACC_GROUP_NONE)
otherwise. */
 
static int
frv_acc_group_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
{
if (REG_P (*x))
{
if (ACC_P (REGNO (*x)))
return (REGNO (*x) - ACC_FIRST) & 4 ? ACC_GROUP_ODD : ACC_GROUP_EVEN;
if (ACCG_P (REGNO (*x)))
return (REGNO (*x) - ACCG_FIRST) & 4 ? ACC_GROUP_ODD : ACC_GROUP_EVEN;
}
return 0;
}
 
/* Return the value of INSN's acc_group attribute. */
 
int
frv_acc_group (rtx insn)
{
/* This distinction only applies to the FR550 packing constraints. */
if (frv_cpu_type != FRV_CPU_FR550)
return ACC_GROUP_NONE;
return for_each_rtx (&PATTERN (insn), frv_acc_group_1, 0);
}
 
/* Return the index of the DFA unit in FRV_UNIT_NAMES[] that instruction
INSN will try to claim first. Since this value depends only on the
type attribute, we can cache the results in FRV_TYPE_TO_UNIT[]. */
 
static unsigned int
frv_insn_unit (rtx insn)
{
enum attr_type type;
 
type = get_attr_type (insn);
if (frv_type_to_unit[type] == ARRAY_SIZE (frv_unit_codes))
{
/* We haven't seen this type of instruction before. */
state_t state;
unsigned int unit;
 
/* Issue the instruction on its own to see which unit it prefers. */
state = alloca (state_size ());
state_reset (state);
state_transition (state, insn);
 
/* Find out which unit was taken. */
for (unit = 0; unit < ARRAY_SIZE (frv_unit_codes); unit++)
if (cpu_unit_reservation_p (state, frv_unit_codes[unit]))
break;
 
gcc_assert (unit != ARRAY_SIZE (frv_unit_codes));
 
frv_type_to_unit[type] = unit;
}
return frv_type_to_unit[type];
}
 
/* Return true if INSN issues to a branch unit. */
 
static bool
frv_issues_to_branch_unit_p (rtx insn)
{
return frv_unit_groups[frv_insn_unit (insn)] == GROUP_B;
}
/* The current state of the packing pass, implemented by frv_pack_insns. */
static struct {
/* The state of the pipeline DFA. */
state_t dfa_state;
 
/* Which hardware registers are set within the current packet,
and the conditions under which they are set. */
regstate_t regstate[FIRST_PSEUDO_REGISTER];
 
/* The memory locations that have been modified so far in this
packet. MEM is the memref and COND is the regstate_t condition
under which it is set. */
struct {
rtx mem;
regstate_t cond;
} mems[2];
 
/* The number of valid entries in MEMS. The value is larger than
ARRAY_SIZE (mems) if there were too many mems to record. */
unsigned int num_mems;
 
/* The maximum number of instructions that can be packed together. */
unsigned int issue_rate;
 
/* The instructions in the packet, partitioned into groups. */
struct frv_packet_group {
/* How many instructions in the packet belong to this group. */
unsigned int num_insns;
 
/* A list of the instructions that belong to this group, in the order
they appear in the rtl stream. */
rtx insns[ARRAY_SIZE (frv_unit_codes)];
 
/* The contents of INSNS after they have been sorted into the correct
assembly-language order. Element X issues to unit X. The list may
contain extra nops. */
rtx sorted[ARRAY_SIZE (frv_unit_codes)];
 
/* The member of frv_nops[] to use in sorted[]. */
rtx nop;
} groups[NUM_GROUPS];
 
/* The instructions that make up the current packet. */
rtx insns[ARRAY_SIZE (frv_unit_codes)];
unsigned int num_insns;
} frv_packet;
 
/* Return the regstate_t flags for the given COND_EXEC condition.
Abort if the condition isn't in the right form. */
 
static int
frv_cond_flags (rtx cond)
{
gcc_assert ((GET_CODE (cond) == EQ || GET_CODE (cond) == NE)
&& GET_CODE (XEXP (cond, 0)) == REG
&& CR_P (REGNO (XEXP (cond, 0)))
&& XEXP (cond, 1) == const0_rtx);
return ((REGNO (XEXP (cond, 0)) - CR_FIRST)
| (GET_CODE (cond) == NE
? REGSTATE_IF_TRUE
: REGSTATE_IF_FALSE));
}
 
 
/* Return true if something accessed under condition COND2 can
conflict with something written under condition COND1. */
 
static bool
frv_regstate_conflict_p (regstate_t cond1, regstate_t cond2)
{
/* If either reference was unconditional, we have a conflict. */
if ((cond1 & REGSTATE_IF_EITHER) == 0
|| (cond2 & REGSTATE_IF_EITHER) == 0)
return true;
 
/* The references might conflict if they were controlled by
different CRs. */
if ((cond1 & REGSTATE_CC_MASK) != (cond2 & REGSTATE_CC_MASK))
return true;
 
/* They definitely conflict if they are controlled by the
same condition. */
if ((cond1 & cond2 & REGSTATE_IF_EITHER) != 0)
return true;
 
return false;
}
 
 
/* A for_each_rtx callback. Return 1 if *X depends on an instruction in
the current packet. DATA points to a regstate_t that describes the
condition under which *X might be set or used. */
 
static int
frv_registers_conflict_p_1 (rtx *x, void *data)
{
unsigned int regno, i;
regstate_t cond;
 
cond = *(regstate_t *) data;
 
if (GET_CODE (*x) == REG)
FOR_EACH_REGNO (regno, *x)
if ((frv_packet.regstate[regno] & REGSTATE_MODIFIED) != 0)
if (frv_regstate_conflict_p (frv_packet.regstate[regno], cond))
return 1;
 
if (GET_CODE (*x) == MEM)
{
/* If we ran out of memory slots, assume a conflict. */
if (frv_packet.num_mems > ARRAY_SIZE (frv_packet.mems))
return 1;
 
/* Check for output or true dependencies with earlier MEMs. */
for (i = 0; i < frv_packet.num_mems; i++)
if (frv_regstate_conflict_p (frv_packet.mems[i].cond, cond))
{
if (true_dependence (frv_packet.mems[i].mem, VOIDmode,
*x, rtx_varies_p))
return 1;
 
if (output_dependence (frv_packet.mems[i].mem, *x))
return 1;
}
}
 
/* The return values of calls aren't significant: they describe
the effect of the call as a whole, not of the insn itself. */
if (GET_CODE (*x) == SET && GET_CODE (SET_SRC (*x)) == CALL)
{
if (for_each_rtx (&SET_SRC (*x), frv_registers_conflict_p_1, data))
return 1;
return -1;
}
 
/* Check subexpressions. */
return 0;
}
 
 
/* Return true if something in X might depend on an instruction
in the current packet. */
 
static bool
frv_registers_conflict_p (rtx x)
{
regstate_t flags;
 
flags = 0;
if (GET_CODE (x) == COND_EXEC)
{
if (for_each_rtx (&XEXP (x, 0), frv_registers_conflict_p_1, &flags))
return true;
 
flags |= frv_cond_flags (XEXP (x, 0));
x = XEXP (x, 1);
}
return for_each_rtx (&x, frv_registers_conflict_p_1, &flags);
}
 
 
/* A note_stores callback. DATA points to the regstate_t condition
under which X is modified. Update FRV_PACKET accordingly. */
 
static void
frv_registers_update_1 (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
{
unsigned int regno;
 
if (GET_CODE (x) == REG)
FOR_EACH_REGNO (regno, x)
frv_packet.regstate[regno] |= *(regstate_t *) data;
 
if (GET_CODE (x) == MEM)
{
if (frv_packet.num_mems < ARRAY_SIZE (frv_packet.mems))
{
frv_packet.mems[frv_packet.num_mems].mem = x;
frv_packet.mems[frv_packet.num_mems].cond = *(regstate_t *) data;
}
frv_packet.num_mems++;
}
}
 
 
/* Update the register state information for an instruction whose
body is X. */
 
static void
frv_registers_update (rtx x)
{
regstate_t flags;
 
flags = REGSTATE_MODIFIED;
if (GET_CODE (x) == COND_EXEC)
{
flags |= frv_cond_flags (XEXP (x, 0));
x = XEXP (x, 1);
}
note_stores (x, frv_registers_update_1, &flags);
}
 
 
/* Initialize frv_packet for the start of a new packet. */
 
static void
frv_start_packet (void)
{
enum frv_insn_group group;
 
memset (frv_packet.regstate, 0, sizeof (frv_packet.regstate));
frv_packet.num_mems = 0;
frv_packet.num_insns = 0;
for (group = 0; group < NUM_GROUPS; group++)
frv_packet.groups[group].num_insns = 0;
}
 
 
/* Likewise for the start of a new basic block. */
 
static void
frv_start_packet_block (void)
{
state_reset (frv_packet.dfa_state);
frv_start_packet ();
}
 
 
/* Finish the current packet, if any, and start a new one. Call
HANDLE_PACKET with FRV_PACKET describing the completed packet. */
 
static void
frv_finish_packet (void (*handle_packet) (void))
{
if (frv_packet.num_insns > 0)
{
handle_packet ();
state_transition (frv_packet.dfa_state, 0);
frv_start_packet ();
}
}
 
 
/* Return true if INSN can be added to the current packet. Update
the DFA state on success. */
 
static bool
frv_pack_insn_p (rtx insn)
{
/* See if the packet is already as long as it can be. */
if (frv_packet.num_insns == frv_packet.issue_rate)
return false;
 
/* If the scheduler thought that an instruction should start a packet,
it's usually a good idea to believe it. It knows much more about
the latencies than we do.
 
There are some exceptions though:
 
- Conditional instructions are scheduled on the assumption that
they will be executed. This is usually a good thing, since it
tends to avoid unnecessary stalls in the conditional code.
But we want to pack conditional instructions as tightly as
possible, in order to optimize the case where they aren't
executed.
 
- The scheduler will always put branches on their own, even
if there's no real dependency.
 
- There's no point putting a call in its own packet unless
we have to. */
if (frv_packet.num_insns > 0
&& GET_CODE (insn) == INSN
&& GET_MODE (insn) == TImode
&& GET_CODE (PATTERN (insn)) != COND_EXEC)
return false;
 
/* Check for register conflicts. Don't do this for setlo since any
conflict will be with the partnering sethi, with which it can
be packed. */
if (get_attr_type (insn) != TYPE_SETLO)
if (frv_registers_conflict_p (PATTERN (insn)))
return false;
 
return state_transition (frv_packet.dfa_state, insn) < 0;
}
 
 
/* Add instruction INSN to the current packet. */
 
static void
frv_add_insn_to_packet (rtx insn)
{
struct frv_packet_group *packet_group;
 
packet_group = &frv_packet.groups[frv_unit_groups[frv_insn_unit (insn)]];
packet_group->insns[packet_group->num_insns++] = insn;
frv_packet.insns[frv_packet.num_insns++] = insn;
 
frv_registers_update (PATTERN (insn));
}
 
 
/* Insert INSN (a member of frv_nops[]) into the current packet. If the
packet ends in a branch or call, insert the nop before it, otherwise
add to the end. */
 
static void
frv_insert_nop_in_packet (rtx insn)
{
struct frv_packet_group *packet_group;
rtx last;
 
packet_group = &frv_packet.groups[frv_unit_groups[frv_insn_unit (insn)]];
last = frv_packet.insns[frv_packet.num_insns - 1];
if (GET_CODE (last) != INSN)
{
insn = emit_insn_before (PATTERN (insn), last);
frv_packet.insns[frv_packet.num_insns - 1] = insn;
frv_packet.insns[frv_packet.num_insns++] = last;
}
else
{
insn = emit_insn_after (PATTERN (insn), last);
frv_packet.insns[frv_packet.num_insns++] = insn;
}
packet_group->insns[packet_group->num_insns++] = insn;
}
 
 
/* If packing is enabled, divide the instructions into packets and
return true. Call HANDLE_PACKET for each complete packet. */
 
static bool
frv_for_each_packet (void (*handle_packet) (void))
{
rtx insn, next_insn;
 
frv_packet.issue_rate = frv_issue_rate ();
 
/* Early exit if we don't want to pack insns. */
if (!optimize
|| !flag_schedule_insns_after_reload
|| !TARGET_VLIW_BRANCH
|| frv_packet.issue_rate == 1)
return false;
 
/* Set up the initial packing state. */
dfa_start ();
frv_packet.dfa_state = alloca (state_size ());
 
frv_start_packet_block ();
for (insn = get_insns (); insn != 0; insn = next_insn)
{
enum rtx_code code;
bool eh_insn_p;
 
code = GET_CODE (insn);
next_insn = NEXT_INSN (insn);
 
if (code == CODE_LABEL)
{
frv_finish_packet (handle_packet);
frv_start_packet_block ();
}
 
if (INSN_P (insn))
switch (GET_CODE (PATTERN (insn)))
{
case USE:
case CLOBBER:
case ADDR_VEC:
case ADDR_DIFF_VEC:
break;
 
default:
/* Calls mustn't be packed on a TOMCAT. */
if (GET_CODE (insn) == CALL_INSN && frv_cpu_type == FRV_CPU_TOMCAT)
frv_finish_packet (handle_packet);
 
/* Since the last instruction in a packet determines the EH
region, any exception-throwing instruction must come at
the end of reordered packet. Insns that issue to a
branch unit are bound to come last; for others it's
too hard to predict. */
eh_insn_p = (find_reg_note (insn, REG_EH_REGION, NULL) != NULL);
if (eh_insn_p && !frv_issues_to_branch_unit_p (insn))
frv_finish_packet (handle_packet);
 
/* Finish the current packet if we can't add INSN to it.
Simulate cycles until INSN is ready to issue. */
if (!frv_pack_insn_p (insn))
{
frv_finish_packet (handle_packet);
while (!frv_pack_insn_p (insn))
state_transition (frv_packet.dfa_state, 0);
}
 
/* Add the instruction to the packet. */
frv_add_insn_to_packet (insn);
 
/* Calls and jumps end a packet, as do insns that throw
an exception. */
if (code == CALL_INSN || code == JUMP_INSN || eh_insn_p)
frv_finish_packet (handle_packet);
break;
}
}
frv_finish_packet (handle_packet);
dfa_finish ();
return true;
}
/* Subroutine of frv_sort_insn_group. We are trying to sort
frv_packet.groups[GROUP].sorted[0...NUM_INSNS-1] into assembly
language order. We have already picked a new position for
frv_packet.groups[GROUP].sorted[X] if bit X of ISSUED is set.
These instructions will occupy elements [0, LOWER_SLOT) and
[UPPER_SLOT, NUM_INSNS) of the final (sorted) array. STATE is
the DFA state after issuing these instructions.
 
Try filling elements [LOWER_SLOT, UPPER_SLOT) with every permutation
of the unused instructions. Return true if one such permutation gives
a valid ordering, leaving the successful permutation in sorted[].
Do not modify sorted[] until a valid permutation is found. */
 
static bool
frv_sort_insn_group_1 (enum frv_insn_group group,
unsigned int lower_slot, unsigned int upper_slot,
unsigned int issued, unsigned int num_insns,
state_t state)
{
struct frv_packet_group *packet_group;
unsigned int i;
state_t test_state;
size_t dfa_size;
rtx insn;
 
/* Early success if we've filled all the slots. */
if (lower_slot == upper_slot)
return true;
 
packet_group = &frv_packet.groups[group];
dfa_size = state_size ();
test_state = alloca (dfa_size);
 
/* Try issuing each unused instruction. */
for (i = num_insns - 1; i + 1 != 0; i--)
if (~issued & (1 << i))
{
insn = packet_group->sorted[i];
memcpy (test_state, state, dfa_size);
if (state_transition (test_state, insn) < 0
&& cpu_unit_reservation_p (test_state,
NTH_UNIT (group, upper_slot - 1))
&& frv_sort_insn_group_1 (group, lower_slot, upper_slot - 1,
issued | (1 << i), num_insns,
test_state))
{
packet_group->sorted[upper_slot - 1] = insn;
return true;
}
}
 
return false;
}
 
/* Compare two instructions by their frv_insn_unit. */
 
static int
frv_compare_insns (const void *first, const void *second)
{
const rtx *insn1 = first, *insn2 = second;
return frv_insn_unit (*insn1) - frv_insn_unit (*insn2);
}
 
/* Copy frv_packet.groups[GROUP].insns[] to frv_packet.groups[GROUP].sorted[]
and sort it into assembly language order. See frv.md for a description of
the algorithm. */
 
static void
frv_sort_insn_group (enum frv_insn_group group)
{
struct frv_packet_group *packet_group;
unsigned int first, i, nop, max_unit, num_slots;
state_t state, test_state;
size_t dfa_size;
 
packet_group = &frv_packet.groups[group];
 
/* Assume no nop is needed. */
packet_group->nop = 0;
 
if (packet_group->num_insns == 0)
return;
 
/* Copy insns[] to sorted[]. */
memcpy (packet_group->sorted, packet_group->insns,
sizeof (rtx) * packet_group->num_insns);
 
/* Sort sorted[] by the unit that each insn tries to take first. */
if (packet_group->num_insns > 1)
qsort (packet_group->sorted, packet_group->num_insns,
sizeof (rtx), frv_compare_insns);
 
/* That's always enough for branch and control insns. */
if (group == GROUP_B || group == GROUP_C)
return;
 
dfa_size = state_size ();
state = alloca (dfa_size);
test_state = alloca (dfa_size);
 
/* Find the highest FIRST such that sorted[0...FIRST-1] can issue
consecutively and such that the DFA takes unit X when sorted[X]
is added. Set STATE to the new DFA state. */
state_reset (test_state);
for (first = 0; first < packet_group->num_insns; first++)
{
memcpy (state, test_state, dfa_size);
if (state_transition (test_state, packet_group->sorted[first]) >= 0
|| !cpu_unit_reservation_p (test_state, NTH_UNIT (group, first)))
break;
}
 
/* If all the instructions issued in ascending order, we're done. */
if (first == packet_group->num_insns)
return;
 
/* Add nops to the end of sorted[] and try each permutation until
we find one that works. */
for (nop = 0; nop < frv_num_nops; nop++)
{
max_unit = frv_insn_unit (frv_nops[nop]);
if (frv_unit_groups[max_unit] == group)
{
packet_group->nop = frv_nops[nop];
num_slots = UNIT_NUMBER (max_unit) + 1;
for (i = packet_group->num_insns; i < num_slots; i++)
packet_group->sorted[i] = frv_nops[nop];
if (frv_sort_insn_group_1 (group, first, num_slots,
(1 << first) - 1, num_slots, state))
return;
}
}
gcc_unreachable ();
}
/* Sort the current packet into assembly-language order. Set packing
flags as appropriate. */
 
static void
frv_reorder_packet (void)
{
unsigned int cursor[NUM_GROUPS];
rtx insns[ARRAY_SIZE (frv_unit_groups)];
unsigned int unit, to, from;
enum frv_insn_group group;
struct frv_packet_group *packet_group;
 
/* First sort each group individually. */
for (group = 0; group < NUM_GROUPS; group++)
{
cursor[group] = 0;
frv_sort_insn_group (group);
}
 
/* Go through the unit template and try add an instruction from
that unit's group. */
to = 0;
for (unit = 0; unit < ARRAY_SIZE (frv_unit_groups); unit++)
{
group = frv_unit_groups[unit];
packet_group = &frv_packet.groups[group];
if (cursor[group] < packet_group->num_insns)
{
/* frv_reorg should have added nops for us. */
gcc_assert (packet_group->sorted[cursor[group]]
!= packet_group->nop);
insns[to++] = packet_group->sorted[cursor[group]++];
}
}
 
gcc_assert (to == frv_packet.num_insns);
 
/* Clear the last instruction's packing flag, thus marking the end of
a packet. Reorder the other instructions relative to it. */
CLEAR_PACKING_FLAG (insns[to - 1]);
for (from = 0; from < to - 1; from++)
{
remove_insn (insns[from]);
add_insn_before (insns[from], insns[to - 1]);
SET_PACKING_FLAG (insns[from]);
}
}
 
 
/* Divide instructions into packets. Reorder the contents of each
packet so that they are in the correct assembly-language order.
 
Since this pass can change the raw meaning of the rtl stream, it must
only be called at the last minute, just before the instructions are
written out. */
 
static void
frv_pack_insns (void)
{
if (frv_for_each_packet (frv_reorder_packet))
frv_insn_packing_flag = 0;
else
frv_insn_packing_flag = -1;
}
/* See whether we need to add nops to group GROUP in order to
make a valid packet. */
 
static void
frv_fill_unused_units (enum frv_insn_group group)
{
unsigned int non_nops, nops, i;
struct frv_packet_group *packet_group;
 
packet_group = &frv_packet.groups[group];
 
/* Sort the instructions into assembly-language order.
Use nops to fill slots that are otherwise unused. */
frv_sort_insn_group (group);
 
/* See how many nops are needed before the final useful instruction. */
i = nops = 0;
for (non_nops = 0; non_nops < packet_group->num_insns; non_nops++)
while (packet_group->sorted[i++] == packet_group->nop)
nops++;
 
/* Insert that many nops into the instruction stream. */
while (nops-- > 0)
frv_insert_nop_in_packet (packet_group->nop);
}
 
/* Return true if accesses IO1 and IO2 refer to the same doubleword. */
 
static bool
frv_same_doubleword_p (const struct frv_io *io1, const struct frv_io *io2)
{
if (io1->const_address != 0 && io2->const_address != 0)
return io1->const_address == io2->const_address;
 
if (io1->var_address != 0 && io2->var_address != 0)
return rtx_equal_p (io1->var_address, io2->var_address);
 
return false;
}
 
/* Return true if operations IO1 and IO2 are guaranteed to complete
in order. */
 
static bool
frv_io_fixed_order_p (const struct frv_io *io1, const struct frv_io *io2)
{
/* The order of writes is always preserved. */
if (io1->type == FRV_IO_WRITE && io2->type == FRV_IO_WRITE)
return true;
 
/* The order of reads isn't preserved. */
if (io1->type != FRV_IO_WRITE && io2->type != FRV_IO_WRITE)
return false;
 
/* One operation is a write and the other is (or could be) a read.
The order is only guaranteed if the accesses are to the same
doubleword. */
return frv_same_doubleword_p (io1, io2);
}
 
/* Generalize I/O operation X so that it covers both X and Y. */
 
static void
frv_io_union (struct frv_io *x, const struct frv_io *y)
{
if (x->type != y->type)
x->type = FRV_IO_UNKNOWN;
if (!frv_same_doubleword_p (x, y))
{
x->const_address = 0;
x->var_address = 0;
}
}
 
/* Fill IO with information about the load or store associated with
membar instruction INSN. */
 
static void
frv_extract_membar (struct frv_io *io, rtx insn)
{
extract_insn (insn);
io->type = INTVAL (recog_data.operand[2]);
io->const_address = INTVAL (recog_data.operand[1]);
io->var_address = XEXP (recog_data.operand[0], 0);
}
 
/* A note_stores callback for which DATA points to an rtx. Nullify *DATA
if X is a register and *DATA depends on X. */
 
static void
frv_io_check_address (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
{
rtx *other = data;
 
if (REG_P (x) && *other != 0 && reg_overlap_mentioned_p (x, *other))
*other = 0;
}
 
/* A note_stores callback for which DATA points to a HARD_REG_SET.
Remove every modified register from the set. */
 
static void
frv_io_handle_set (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
{
HARD_REG_SET *set = data;
unsigned int regno;
 
if (REG_P (x))
FOR_EACH_REGNO (regno, x)
CLEAR_HARD_REG_BIT (*set, regno);
}
 
/* A for_each_rtx callback for which DATA points to a HARD_REG_SET.
Add every register in *X to the set. */
 
static int
frv_io_handle_use_1 (rtx *x, void *data)
{
HARD_REG_SET *set = data;
unsigned int regno;
 
if (REG_P (*x))
FOR_EACH_REGNO (regno, *x)
SET_HARD_REG_BIT (*set, regno);
 
return 0;
}
 
/* A note_stores callback that applies frv_io_handle_use_1 to an
entire rhs value. */
 
static void
frv_io_handle_use (rtx *x, void *data)
{
for_each_rtx (x, frv_io_handle_use_1, data);
}
 
/* Go through block BB looking for membars to remove. There are two
cases where intra-block analysis is enough:
 
- a membar is redundant if it occurs between two consecutive I/O
operations and if those operations are guaranteed to complete
in order.
 
- a membar for a __builtin_read is redundant if the result is
used before the next I/O operation is issued.
 
If the last membar in the block could not be removed, and there
are guaranteed to be no I/O operations between that membar and
the end of the block, store the membar in *LAST_MEMBAR, otherwise
store null.
 
Describe the block's first I/O operation in *NEXT_IO. Describe
an unknown operation if the block doesn't do any I/O. */
 
static void
frv_optimize_membar_local (basic_block bb, struct frv_io *next_io,
rtx *last_membar)
{
HARD_REG_SET used_regs;
rtx next_membar, set, insn;
bool next_is_end_p;
 
/* NEXT_IO is the next I/O operation to be performed after the current
instruction. It starts off as being an unknown operation. */
memset (next_io, 0, sizeof (*next_io));
 
/* NEXT_IS_END_P is true if NEXT_IO describes the end of the block. */
next_is_end_p = true;
 
/* If the current instruction is a __builtin_read or __builtin_write,
NEXT_MEMBAR is the membar instruction associated with it. NEXT_MEMBAR
is null if the membar has already been deleted.
 
Note that the initialization here should only be needed to
suppress warnings. */
next_membar = 0;
 
/* USED_REGS is the set of registers that are used before the
next I/O instruction. */
CLEAR_HARD_REG_SET (used_regs);
 
for (insn = BB_END (bb); insn != BB_HEAD (bb); insn = PREV_INSN (insn))
if (GET_CODE (insn) == CALL_INSN)
{
/* We can't predict what a call will do to volatile memory. */
memset (next_io, 0, sizeof (struct frv_io));
next_is_end_p = false;
CLEAR_HARD_REG_SET (used_regs);
}
else if (INSN_P (insn))
switch (recog_memoized (insn))
{
case CODE_FOR_optional_membar_qi:
case CODE_FOR_optional_membar_hi:
case CODE_FOR_optional_membar_si:
case CODE_FOR_optional_membar_di:
next_membar = insn;
if (next_is_end_p)
{
/* Local information isn't enough to decide whether this
membar is needed. Stash it away for later. */
*last_membar = insn;
frv_extract_membar (next_io, insn);
next_is_end_p = false;
}
else
{
/* Check whether the I/O operation before INSN could be
reordered with one described by NEXT_IO. If it can't,
INSN will not be needed. */
struct frv_io prev_io;
 
frv_extract_membar (&prev_io, insn);
if (frv_io_fixed_order_p (&prev_io, next_io))
{
if (dump_file)
fprintf (dump_file,
";; [Local] Removing membar %d since order"
" of accesses is guaranteed\n",
INSN_UID (next_membar));
 
insn = NEXT_INSN (insn);
delete_insn (next_membar);
next_membar = 0;
}
*next_io = prev_io;
}
break;
 
default:
/* Invalidate NEXT_IO's address if it depends on something that
is clobbered by INSN. */
if (next_io->var_address)
note_stores (PATTERN (insn), frv_io_check_address,
&next_io->var_address);
 
/* If the next membar is associated with a __builtin_read,
see if INSN reads from that address. If it does, and if
the destination register is used before the next I/O access,
there is no need for the membar. */
set = PATTERN (insn);
if (next_io->type == FRV_IO_READ
&& next_io->var_address != 0
&& next_membar != 0
&& GET_CODE (set) == SET
&& GET_CODE (SET_DEST (set)) == REG
&& TEST_HARD_REG_BIT (used_regs, REGNO (SET_DEST (set))))
{
rtx src;
 
src = SET_SRC (set);
if (GET_CODE (src) == ZERO_EXTEND)
src = XEXP (src, 0);
 
if (GET_CODE (src) == MEM
&& rtx_equal_p (XEXP (src, 0), next_io->var_address))
{
if (dump_file)
fprintf (dump_file,
";; [Local] Removing membar %d since the target"
" of %d is used before the I/O operation\n",
INSN_UID (next_membar), INSN_UID (insn));
 
if (next_membar == *last_membar)
*last_membar = 0;
 
delete_insn (next_membar);
next_membar = 0;
}
}
 
/* If INSN has volatile references, forget about any registers
that are used after it. Otherwise forget about uses that
are (or might be) defined by INSN. */
if (volatile_refs_p (PATTERN (insn)))
CLEAR_HARD_REG_SET (used_regs);
else
note_stores (PATTERN (insn), frv_io_handle_set, &used_regs);
 
note_uses (&PATTERN (insn), frv_io_handle_use, &used_regs);
break;
}
}
 
/* See if MEMBAR, the last membar instruction in BB, can be removed.
FIRST_IO[X] describes the first operation performed by basic block X. */
 
static void
frv_optimize_membar_global (basic_block bb, struct frv_io *first_io,
rtx membar)
{
struct frv_io this_io, next_io;
edge succ;
edge_iterator ei;
 
/* We need to keep the membar if there is an edge to the exit block. */
FOR_EACH_EDGE (succ, ei, bb->succs)
/* for (succ = bb->succ; succ != 0; succ = succ->succ_next) */
if (succ->dest == EXIT_BLOCK_PTR)
return;
 
/* Work out the union of all successor blocks. */
ei = ei_start (bb->succs);
ei_cond (ei, &succ);
/* next_io = first_io[bb->succ->dest->index]; */
next_io = first_io[succ->dest->index];
ei = ei_start (bb->succs);
if (ei_cond (ei, &succ))
{
for (ei_next (&ei); ei_cond (ei, &succ); ei_next (&ei))
/*for (succ = bb->succ->succ_next; succ != 0; succ = succ->succ_next)*/
frv_io_union (&next_io, &first_io[succ->dest->index]);
}
else
gcc_unreachable ();
 
frv_extract_membar (&this_io, membar);
if (frv_io_fixed_order_p (&this_io, &next_io))
{
if (dump_file)
fprintf (dump_file,
";; [Global] Removing membar %d since order of accesses"
" is guaranteed\n", INSN_UID (membar));
 
delete_insn (membar);
}
}
 
/* Remove redundant membars from the current function. */
 
static void
frv_optimize_membar (void)
{
basic_block bb;
struct frv_io *first_io;
rtx *last_membar;
 
compute_bb_for_insn ();
first_io = xcalloc (last_basic_block, sizeof (struct frv_io));
last_membar = xcalloc (last_basic_block, sizeof (rtx));
 
FOR_EACH_BB (bb)
frv_optimize_membar_local (bb, &first_io[bb->index],
&last_membar[bb->index]);
 
FOR_EACH_BB (bb)
if (last_membar[bb->index] != 0)
frv_optimize_membar_global (bb, first_io, last_membar[bb->index]);
 
free (first_io);
free (last_membar);
}
/* Used by frv_reorg to keep track of the current packet's address. */
static unsigned int frv_packet_address;
 
/* If the current packet falls through to a label, try to pad the packet
with nops in order to fit the label's alignment requirements. */
 
static void
frv_align_label (void)
{
unsigned int alignment, target, nop;
rtx x, last, barrier, label;
 
/* Walk forward to the start of the next packet. Set ALIGNMENT to the
maximum alignment of that packet, LABEL to the last label between
the packets, and BARRIER to the last barrier. */
last = frv_packet.insns[frv_packet.num_insns - 1];
label = barrier = 0;
alignment = 4;
for (x = NEXT_INSN (last); x != 0 && !INSN_P (x); x = NEXT_INSN (x))
{
if (LABEL_P (x))
{
unsigned int subalign = 1 << label_to_alignment (x);
alignment = MAX (alignment, subalign);
label = x;
}
if (BARRIER_P (x))
barrier = x;
}
 
/* If -malign-labels, and the packet falls through to an unaligned
label, try introducing a nop to align that label to 8 bytes. */
if (TARGET_ALIGN_LABELS
&& label != 0
&& barrier == 0
&& frv_packet.num_insns < frv_packet.issue_rate)
alignment = MAX (alignment, 8);
 
/* Advance the address to the end of the current packet. */
frv_packet_address += frv_packet.num_insns * 4;
 
/* Work out the target address, after alignment. */
target = (frv_packet_address + alignment - 1) & -alignment;
 
/* If the packet falls through to the label, try to find an efficient
padding sequence. */
if (barrier == 0)
{
/* First try adding nops to the current packet. */
for (nop = 0; nop < frv_num_nops; nop++)
while (frv_packet_address < target && frv_pack_insn_p (frv_nops[nop]))
{
frv_insert_nop_in_packet (frv_nops[nop]);
frv_packet_address += 4;
}
 
/* If we still haven't reached the target, add some new packets that
contain only nops. If there are two types of nop, insert an
alternating sequence of frv_nops[0] and frv_nops[1], which will
lead to packets like:
 
nop.p
mnop.p/fnop.p
nop.p
mnop/fnop
 
etc. Just emit frv_nops[0] if that's the only nop we have. */
last = frv_packet.insns[frv_packet.num_insns - 1];
nop = 0;
while (frv_packet_address < target)
{
last = emit_insn_after (PATTERN (frv_nops[nop]), last);
frv_packet_address += 4;
if (frv_num_nops > 1)
nop ^= 1;
}
}
 
frv_packet_address = target;
}
 
/* Subroutine of frv_reorg, called after each packet has been constructed
in frv_packet. */
 
static void
frv_reorg_packet (void)
{
frv_fill_unused_units (GROUP_I);
frv_fill_unused_units (GROUP_FM);
frv_align_label ();
}
 
/* Add an instruction with pattern NOP to frv_nops[]. */
 
static void
frv_register_nop (rtx nop)
{
nop = make_insn_raw (nop);
NEXT_INSN (nop) = 0;
PREV_INSN (nop) = 0;
frv_nops[frv_num_nops++] = nop;
}
 
/* Implement TARGET_MACHINE_DEPENDENT_REORG. Divide the instructions
into packets and check whether we need to insert nops in order to
fulfill the processor's issue requirements. Also, if the user has
requested a certain alignment for a label, try to meet that alignment
by inserting nops in the previous packet. */
 
static void
frv_reorg (void)
{
if (optimize > 0 && TARGET_OPTIMIZE_MEMBAR && cfun->machine->has_membar_p)
frv_optimize_membar ();
 
frv_num_nops = 0;
frv_register_nop (gen_nop ());
if (TARGET_MEDIA)
frv_register_nop (gen_mnop ());
if (TARGET_HARD_FLOAT)
frv_register_nop (gen_fnop ());
 
/* Estimate the length of each branch. Although this may change after
we've inserted nops, it will only do so in big functions. */
shorten_branches (get_insns ());
 
frv_packet_address = 0;
frv_for_each_packet (frv_reorg_packet);
}
#define def_builtin(name, type, code) \
lang_hooks.builtin_function ((name), (type), (code), BUILT_IN_MD, NULL, NULL)
 
struct builtin_description
{
enum insn_code icode;
const char *name;
enum frv_builtins code;
enum rtx_code comparison;
unsigned int flag;
};
 
/* Media intrinsics that take a single, constant argument. */
 
static struct builtin_description bdesc_set[] =
{
{ CODE_FOR_mhdsets, "__MHDSETS", FRV_BUILTIN_MHDSETS, 0, 0 }
};
 
/* Media intrinsics that take just one argument. */
 
static struct builtin_description bdesc_1arg[] =
{
{ CODE_FOR_mnot, "__MNOT", FRV_BUILTIN_MNOT, 0, 0 },
{ CODE_FOR_munpackh, "__MUNPACKH", FRV_BUILTIN_MUNPACKH, 0, 0 },
{ CODE_FOR_mbtoh, "__MBTOH", FRV_BUILTIN_MBTOH, 0, 0 },
{ CODE_FOR_mhtob, "__MHTOB", FRV_BUILTIN_MHTOB, 0, 0 },
{ CODE_FOR_mabshs, "__MABSHS", FRV_BUILTIN_MABSHS, 0, 0 },
{ CODE_FOR_scutss, "__SCUTSS", FRV_BUILTIN_SCUTSS, 0, 0 }
};
 
/* Media intrinsics that take two arguments. */
 
static struct builtin_description bdesc_2arg[] =
{
{ CODE_FOR_mand, "__MAND", FRV_BUILTIN_MAND, 0, 0 },
{ CODE_FOR_mor, "__MOR", FRV_BUILTIN_MOR, 0, 0 },
{ CODE_FOR_mxor, "__MXOR", FRV_BUILTIN_MXOR, 0, 0 },
{ CODE_FOR_maveh, "__MAVEH", FRV_BUILTIN_MAVEH, 0, 0 },
{ CODE_FOR_msaths, "__MSATHS", FRV_BUILTIN_MSATHS, 0, 0 },
{ CODE_FOR_msathu, "__MSATHU", FRV_BUILTIN_MSATHU, 0, 0 },
{ CODE_FOR_maddhss, "__MADDHSS", FRV_BUILTIN_MADDHSS, 0, 0 },
{ CODE_FOR_maddhus, "__MADDHUS", FRV_BUILTIN_MADDHUS, 0, 0 },
{ CODE_FOR_msubhss, "__MSUBHSS", FRV_BUILTIN_MSUBHSS, 0, 0 },
{ CODE_FOR_msubhus, "__MSUBHUS", FRV_BUILTIN_MSUBHUS, 0, 0 },
{ CODE_FOR_mqaddhss, "__MQADDHSS", FRV_BUILTIN_MQADDHSS, 0, 0 },
{ CODE_FOR_mqaddhus, "__MQADDHUS", FRV_BUILTIN_MQADDHUS, 0, 0 },
{ CODE_FOR_mqsubhss, "__MQSUBHSS", FRV_BUILTIN_MQSUBHSS, 0, 0 },
{ CODE_FOR_mqsubhus, "__MQSUBHUS", FRV_BUILTIN_MQSUBHUS, 0, 0 },
{ CODE_FOR_mpackh, "__MPACKH", FRV_BUILTIN_MPACKH, 0, 0 },
{ CODE_FOR_mcop1, "__Mcop1", FRV_BUILTIN_MCOP1, 0, 0 },
{ CODE_FOR_mcop2, "__Mcop2", FRV_BUILTIN_MCOP2, 0, 0 },
{ CODE_FOR_mwcut, "__MWCUT", FRV_BUILTIN_MWCUT, 0, 0 },
{ CODE_FOR_mqsaths, "__MQSATHS", FRV_BUILTIN_MQSATHS, 0, 0 },
{ CODE_FOR_mqlclrhs, "__MQLCLRHS", FRV_BUILTIN_MQLCLRHS, 0, 0 },
{ CODE_FOR_mqlmths, "__MQLMTHS", FRV_BUILTIN_MQLMTHS, 0, 0 },
{ CODE_FOR_smul, "__SMUL", FRV_BUILTIN_SMUL, 0, 0 },
{ CODE_FOR_umul, "__UMUL", FRV_BUILTIN_UMUL, 0, 0 },
{ CODE_FOR_addss, "__ADDSS", FRV_BUILTIN_ADDSS, 0, 0 },
{ CODE_FOR_subss, "__SUBSS", FRV_BUILTIN_SUBSS, 0, 0 },
{ CODE_FOR_slass, "__SLASS", FRV_BUILTIN_SLASS, 0, 0 },
{ CODE_FOR_scan, "__SCAN", FRV_BUILTIN_SCAN, 0, 0 }
};
 
/* Integer intrinsics that take two arguments and have no return value. */
 
static struct builtin_description bdesc_int_void2arg[] =
{
{ CODE_FOR_smass, "__SMASS", FRV_BUILTIN_SMASS, 0, 0 },
{ CODE_FOR_smsss, "__SMSSS", FRV_BUILTIN_SMSSS, 0, 0 },
{ CODE_FOR_smu, "__SMU", FRV_BUILTIN_SMU, 0, 0 }
};
 
static struct builtin_description bdesc_prefetches[] =
{
{ CODE_FOR_frv_prefetch0, "__data_prefetch0", FRV_BUILTIN_PREFETCH0, 0, 0 },
{ CODE_FOR_frv_prefetch, "__data_prefetch", FRV_BUILTIN_PREFETCH, 0, 0 }
};
 
/* Media intrinsics that take two arguments, the first being an ACC number. */
 
static struct builtin_description bdesc_cut[] =
{
{ CODE_FOR_mcut, "__MCUT", FRV_BUILTIN_MCUT, 0, 0 },
{ CODE_FOR_mcutss, "__MCUTSS", FRV_BUILTIN_MCUTSS, 0, 0 },
{ CODE_FOR_mdcutssi, "__MDCUTSSI", FRV_BUILTIN_MDCUTSSI, 0, 0 }
};
 
/* Two-argument media intrinsics with an immediate second argument. */
 
static struct builtin_description bdesc_2argimm[] =
{
{ CODE_FOR_mrotli, "__MROTLI", FRV_BUILTIN_MROTLI, 0, 0 },
{ CODE_FOR_mrotri, "__MROTRI", FRV_BUILTIN_MROTRI, 0, 0 },
{ CODE_FOR_msllhi, "__MSLLHI", FRV_BUILTIN_MSLLHI, 0, 0 },
{ CODE_FOR_msrlhi, "__MSRLHI", FRV_BUILTIN_MSRLHI, 0, 0 },
{ CODE_FOR_msrahi, "__MSRAHI", FRV_BUILTIN_MSRAHI, 0, 0 },
{ CODE_FOR_mexpdhw, "__MEXPDHW", FRV_BUILTIN_MEXPDHW, 0, 0 },
{ CODE_FOR_mexpdhd, "__MEXPDHD", FRV_BUILTIN_MEXPDHD, 0, 0 },
{ CODE_FOR_mdrotli, "__MDROTLI", FRV_BUILTIN_MDROTLI, 0, 0 },
{ CODE_FOR_mcplhi, "__MCPLHI", FRV_BUILTIN_MCPLHI, 0, 0 },
{ CODE_FOR_mcpli, "__MCPLI", FRV_BUILTIN_MCPLI, 0, 0 },
{ CODE_FOR_mhsetlos, "__MHSETLOS", FRV_BUILTIN_MHSETLOS, 0, 0 },
{ CODE_FOR_mhsetloh, "__MHSETLOH", FRV_BUILTIN_MHSETLOH, 0, 0 },
{ CODE_FOR_mhsethis, "__MHSETHIS", FRV_BUILTIN_MHSETHIS, 0, 0 },
{ CODE_FOR_mhsethih, "__MHSETHIH", FRV_BUILTIN_MHSETHIH, 0, 0 },
{ CODE_FOR_mhdseth, "__MHDSETH", FRV_BUILTIN_MHDSETH, 0, 0 },
{ CODE_FOR_mqsllhi, "__MQSLLHI", FRV_BUILTIN_MQSLLHI, 0, 0 },
{ CODE_FOR_mqsrahi, "__MQSRAHI", FRV_BUILTIN_MQSRAHI, 0, 0 }
};
 
/* Media intrinsics that take two arguments and return void, the first argument
being a pointer to 4 words in memory. */
 
static struct builtin_description bdesc_void2arg[] =
{
{ CODE_FOR_mdunpackh, "__MDUNPACKH", FRV_BUILTIN_MDUNPACKH, 0, 0 },
{ CODE_FOR_mbtohe, "__MBTOHE", FRV_BUILTIN_MBTOHE, 0, 0 },
};
 
/* Media intrinsics that take three arguments, the first being a const_int that
denotes an accumulator, and that return void. */
 
static struct builtin_description bdesc_void3arg[] =
{
{ CODE_FOR_mcpxrs, "__MCPXRS", FRV_BUILTIN_MCPXRS, 0, 0 },
{ CODE_FOR_mcpxru, "__MCPXRU", FRV_BUILTIN_MCPXRU, 0, 0 },
{ CODE_FOR_mcpxis, "__MCPXIS", FRV_BUILTIN_MCPXIS, 0, 0 },
{ CODE_FOR_mcpxiu, "__MCPXIU", FRV_BUILTIN_MCPXIU, 0, 0 },
{ CODE_FOR_mmulhs, "__MMULHS", FRV_BUILTIN_MMULHS, 0, 0 },
{ CODE_FOR_mmulhu, "__MMULHU", FRV_BUILTIN_MMULHU, 0, 0 },
{ CODE_FOR_mmulxhs, "__MMULXHS", FRV_BUILTIN_MMULXHS, 0, 0 },
{ CODE_FOR_mmulxhu, "__MMULXHU", FRV_BUILTIN_MMULXHU, 0, 0 },
{ CODE_FOR_mmachs, "__MMACHS", FRV_BUILTIN_MMACHS, 0, 0 },
{ CODE_FOR_mmachu, "__MMACHU", FRV_BUILTIN_MMACHU, 0, 0 },
{ CODE_FOR_mmrdhs, "__MMRDHS", FRV_BUILTIN_MMRDHS, 0, 0 },
{ CODE_FOR_mmrdhu, "__MMRDHU", FRV_BUILTIN_MMRDHU, 0, 0 },
{ CODE_FOR_mqcpxrs, "__MQCPXRS", FRV_BUILTIN_MQCPXRS, 0, 0 },
{ CODE_FOR_mqcpxru, "__MQCPXRU", FRV_BUILTIN_MQCPXRU, 0, 0 },
{ CODE_FOR_mqcpxis, "__MQCPXIS", FRV_BUILTIN_MQCPXIS, 0, 0 },
{ CODE_FOR_mqcpxiu, "__MQCPXIU", FRV_BUILTIN_MQCPXIU, 0, 0 },
{ CODE_FOR_mqmulhs, "__MQMULHS", FRV_BUILTIN_MQMULHS, 0, 0 },
{ CODE_FOR_mqmulhu, "__MQMULHU", FRV_BUILTIN_MQMULHU, 0, 0 },
{ CODE_FOR_mqmulxhs, "__MQMULXHS", FRV_BUILTIN_MQMULXHS, 0, 0 },
{ CODE_FOR_mqmulxhu, "__MQMULXHU", FRV_BUILTIN_MQMULXHU, 0, 0 },
{ CODE_FOR_mqmachs, "__MQMACHS", FRV_BUILTIN_MQMACHS, 0, 0 },
{ CODE_FOR_mqmachu, "__MQMACHU", FRV_BUILTIN_MQMACHU, 0, 0 },
{ CODE_FOR_mqxmachs, "__MQXMACHS", FRV_BUILTIN_MQXMACHS, 0, 0 },
{ CODE_FOR_mqxmacxhs, "__MQXMACXHS", FRV_BUILTIN_MQXMACXHS, 0, 0 },
{ CODE_FOR_mqmacxhs, "__MQMACXHS", FRV_BUILTIN_MQMACXHS, 0, 0 }
};
 
/* Media intrinsics that take two accumulator numbers as argument and
return void. */
 
static struct builtin_description bdesc_voidacc[] =
{
{ CODE_FOR_maddaccs, "__MADDACCS", FRV_BUILTIN_MADDACCS, 0, 0 },
{ CODE_FOR_msubaccs, "__MSUBACCS", FRV_BUILTIN_MSUBACCS, 0, 0 },
{ CODE_FOR_masaccs, "__MASACCS", FRV_BUILTIN_MASACCS, 0, 0 },
{ CODE_FOR_mdaddaccs, "__MDADDACCS", FRV_BUILTIN_MDADDACCS, 0, 0 },
{ CODE_FOR_mdsubaccs, "__MDSUBACCS", FRV_BUILTIN_MDSUBACCS, 0, 0 },
{ CODE_FOR_mdasaccs, "__MDASACCS", FRV_BUILTIN_MDASACCS, 0, 0 }
};
 
/* Intrinsics that load a value and then issue a MEMBAR. The load is
a normal move and the ICODE is for the membar. */
 
static struct builtin_description bdesc_loads[] =
{
{ CODE_FOR_optional_membar_qi, "__builtin_read8",
FRV_BUILTIN_READ8, 0, 0 },
{ CODE_FOR_optional_membar_hi, "__builtin_read16",
FRV_BUILTIN_READ16, 0, 0 },
{ CODE_FOR_optional_membar_si, "__builtin_read32",
FRV_BUILTIN_READ32, 0, 0 },
{ CODE_FOR_optional_membar_di, "__builtin_read64",
FRV_BUILTIN_READ64, 0, 0 }
};
 
/* Likewise stores. */
 
static struct builtin_description bdesc_stores[] =
{
{ CODE_FOR_optional_membar_qi, "__builtin_write8",
FRV_BUILTIN_WRITE8, 0, 0 },
{ CODE_FOR_optional_membar_hi, "__builtin_write16",
FRV_BUILTIN_WRITE16, 0, 0 },
{ CODE_FOR_optional_membar_si, "__builtin_write32",
FRV_BUILTIN_WRITE32, 0, 0 },
{ CODE_FOR_optional_membar_di, "__builtin_write64",
FRV_BUILTIN_WRITE64, 0, 0 },
};
 
/* Initialize media builtins. */
 
static void
frv_init_builtins (void)
{
tree endlink = void_list_node;
tree accumulator = integer_type_node;
tree integer = integer_type_node;
tree voidt = void_type_node;
tree uhalf = short_unsigned_type_node;
tree sword1 = long_integer_type_node;
tree uword1 = long_unsigned_type_node;
tree sword2 = long_long_integer_type_node;
tree uword2 = long_long_unsigned_type_node;
tree uword4 = build_pointer_type (uword1);
tree vptr = build_pointer_type (build_type_variant (void_type_node, 0, 1));
tree ubyte = unsigned_char_type_node;
tree iacc = integer_type_node;
 
#define UNARY(RET, T1) \
build_function_type (RET, tree_cons (NULL_TREE, T1, endlink))
 
#define BINARY(RET, T1, T2) \
build_function_type (RET, tree_cons (NULL_TREE, T1, \
tree_cons (NULL_TREE, T2, endlink)))
 
#define TRINARY(RET, T1, T2, T3) \
build_function_type (RET, tree_cons (NULL_TREE, T1, \
tree_cons (NULL_TREE, T2, \
tree_cons (NULL_TREE, T3, endlink))))
 
#define QUAD(RET, T1, T2, T3, T4) \
build_function_type (RET, tree_cons (NULL_TREE, T1, \
tree_cons (NULL_TREE, T2, \
tree_cons (NULL_TREE, T3, \
tree_cons (NULL_TREE, T4, endlink)))))
 
tree void_ftype_void = build_function_type (voidt, endlink);
 
tree void_ftype_acc = UNARY (voidt, accumulator);
tree void_ftype_uw4_uw1 = BINARY (voidt, uword4, uword1);
tree void_ftype_uw4_uw2 = BINARY (voidt, uword4, uword2);
tree void_ftype_acc_uw1 = BINARY (voidt, accumulator, uword1);
tree void_ftype_acc_acc = BINARY (voidt, accumulator, accumulator);
tree void_ftype_acc_uw1_uw1 = TRINARY (voidt, accumulator, uword1, uword1);
tree void_ftype_acc_sw1_sw1 = TRINARY (voidt, accumulator, sword1, sword1);
tree void_ftype_acc_uw2_uw2 = TRINARY (voidt, accumulator, uword2, uword2);
tree void_ftype_acc_sw2_sw2 = TRINARY (voidt, accumulator, sword2, sword2);
 
tree uw1_ftype_uw1 = UNARY (uword1, uword1);
tree uw1_ftype_sw1 = UNARY (uword1, sword1);
tree uw1_ftype_uw2 = UNARY (uword1, uword2);
tree uw1_ftype_acc = UNARY (uword1, accumulator);
tree uw1_ftype_uh_uh = BINARY (uword1, uhalf, uhalf);
tree uw1_ftype_uw1_uw1 = BINARY (uword1, uword1, uword1);
tree uw1_ftype_uw1_int = BINARY (uword1, uword1, integer);
tree uw1_ftype_acc_uw1 = BINARY (uword1, accumulator, uword1);
tree uw1_ftype_acc_sw1 = BINARY (uword1, accumulator, sword1);
tree uw1_ftype_uw2_uw1 = BINARY (uword1, uword2, uword1);
tree uw1_ftype_uw2_int = BINARY (uword1, uword2, integer);
 
tree sw1_ftype_int = UNARY (sword1, integer);
tree sw1_ftype_sw1_sw1 = BINARY (sword1, sword1, sword1);
tree sw1_ftype_sw1_int = BINARY (sword1, sword1, integer);
 
tree uw2_ftype_uw1 = UNARY (uword2, uword1);
tree uw2_ftype_uw1_int = BINARY (uword2, uword1, integer);
tree uw2_ftype_uw2_uw2 = BINARY (uword2, uword2, uword2);
tree uw2_ftype_uw2_int = BINARY (uword2, uword2, integer);
tree uw2_ftype_acc_int = BINARY (uword2, accumulator, integer);
tree uw2_ftype_uh_uh_uh_uh = QUAD (uword2, uhalf, uhalf, uhalf, uhalf);
 
tree sw2_ftype_sw2_sw2 = BINARY (sword2, sword2, sword2);
tree sw2_ftype_sw2_int = BINARY (sword2, sword2, integer);
tree uw2_ftype_uw1_uw1 = BINARY (uword2, uword1, uword1);
tree sw2_ftype_sw1_sw1 = BINARY (sword2, sword1, sword1);
tree void_ftype_sw1_sw1 = BINARY (voidt, sword1, sword1);
tree void_ftype_iacc_sw2 = BINARY (voidt, iacc, sword2);
tree void_ftype_iacc_sw1 = BINARY (voidt, iacc, sword1);
tree sw1_ftype_sw1 = UNARY (sword1, sword1);
tree sw2_ftype_iacc = UNARY (sword2, iacc);
tree sw1_ftype_iacc = UNARY (sword1, iacc);
tree void_ftype_ptr = UNARY (voidt, const_ptr_type_node);
tree uw1_ftype_vptr = UNARY (uword1, vptr);
tree uw2_ftype_vptr = UNARY (uword2, vptr);
tree void_ftype_vptr_ub = BINARY (voidt, vptr, ubyte);
tree void_ftype_vptr_uh = BINARY (voidt, vptr, uhalf);
tree void_ftype_vptr_uw1 = BINARY (voidt, vptr, uword1);
tree void_ftype_vptr_uw2 = BINARY (voidt, vptr, uword2);
 
def_builtin ("__MAND", uw1_ftype_uw1_uw1, FRV_BUILTIN_MAND);
def_builtin ("__MOR", uw1_ftype_uw1_uw1, FRV_BUILTIN_MOR);
def_builtin ("__MXOR", uw1_ftype_uw1_uw1, FRV_BUILTIN_MXOR);
def_builtin ("__MNOT", uw1_ftype_uw1, FRV_BUILTIN_MNOT);
def_builtin ("__MROTLI", uw1_ftype_uw1_int, FRV_BUILTIN_MROTLI);
def_builtin ("__MROTRI", uw1_ftype_uw1_int, FRV_BUILTIN_MROTRI);
def_builtin ("__MWCUT", uw1_ftype_uw2_uw1, FRV_BUILTIN_MWCUT);
def_builtin ("__MAVEH", uw1_ftype_uw1_uw1, FRV_BUILTIN_MAVEH);
def_builtin ("__MSLLHI", uw1_ftype_uw1_int, FRV_BUILTIN_MSLLHI);
def_builtin ("__MSRLHI", uw1_ftype_uw1_int, FRV_BUILTIN_MSRLHI);
def_builtin ("__MSRAHI", sw1_ftype_sw1_int, FRV_BUILTIN_MSRAHI);
def_builtin ("__MSATHS", sw1_ftype_sw1_sw1, FRV_BUILTIN_MSATHS);
def_builtin ("__MSATHU", uw1_ftype_uw1_uw1, FRV_BUILTIN_MSATHU);
def_builtin ("__MADDHSS", sw1_ftype_sw1_sw1, FRV_BUILTIN_MADDHSS);
def_builtin ("__MADDHUS", uw1_ftype_uw1_uw1, FRV_BUILTIN_MADDHUS);
def_builtin ("__MSUBHSS", sw1_ftype_sw1_sw1, FRV_BUILTIN_MSUBHSS);
def_builtin ("__MSUBHUS", uw1_ftype_uw1_uw1, FRV_BUILTIN_MSUBHUS);
def_builtin ("__MMULHS", void_ftype_acc_sw1_sw1, FRV_BUILTIN_MMULHS);
def_builtin ("__MMULHU", void_ftype_acc_uw1_uw1, FRV_BUILTIN_MMULHU);
def_builtin ("__MMULXHS", void_ftype_acc_sw1_sw1, FRV_BUILTIN_MMULXHS);
def_builtin ("__MMULXHU", void_ftype_acc_uw1_uw1, FRV_BUILTIN_MMULXHU);
def_builtin ("__MMACHS", void_ftype_acc_sw1_sw1, FRV_BUILTIN_MMACHS);
def_builtin ("__MMACHU", void_ftype_acc_uw1_uw1, FRV_BUILTIN_MMACHU);
def_builtin ("__MMRDHS", void_ftype_acc_sw1_sw1, FRV_BUILTIN_MMRDHS);
def_builtin ("__MMRDHU", void_ftype_acc_uw1_uw1, FRV_BUILTIN_MMRDHU);
def_builtin ("__MQADDHSS", sw2_ftype_sw2_sw2, FRV_BUILTIN_MQADDHSS);
def_builtin ("__MQADDHUS", uw2_ftype_uw2_uw2, FRV_BUILTIN_MQADDHUS);
def_builtin ("__MQSUBHSS", sw2_ftype_sw2_sw2, FRV_BUILTIN_MQSUBHSS);
def_builtin ("__MQSUBHUS", uw2_ftype_uw2_uw2, FRV_BUILTIN_MQSUBHUS);
def_builtin ("__MQMULHS", void_ftype_acc_sw2_sw2, FRV_BUILTIN_MQMULHS);
def_builtin ("__MQMULHU", void_ftype_acc_uw2_uw2, FRV_BUILTIN_MQMULHU);
def_builtin ("__MQMULXHS", void_ftype_acc_sw2_sw2, FRV_BUILTIN_MQMULXHS);
def_builtin ("__MQMULXHU", void_ftype_acc_uw2_uw2, FRV_BUILTIN_MQMULXHU);
def_builtin ("__MQMACHS", void_ftype_acc_sw2_sw2, FRV_BUILTIN_MQMACHS);
def_builtin ("__MQMACHU", void_ftype_acc_uw2_uw2, FRV_BUILTIN_MQMACHU);
def_builtin ("__MCPXRS", void_ftype_acc_sw1_sw1, FRV_BUILTIN_MCPXRS);
def_builtin ("__MCPXRU", void_ftype_acc_uw1_uw1, FRV_BUILTIN_MCPXRU);
def_builtin ("__MCPXIS", void_ftype_acc_sw1_sw1, FRV_BUILTIN_MCPXIS);
def_builtin ("__MCPXIU", void_ftype_acc_uw1_uw1, FRV_BUILTIN_MCPXIU);
def_builtin ("__MQCPXRS", void_ftype_acc_sw2_sw2, FRV_BUILTIN_MQCPXRS);
def_builtin ("__MQCPXRU", void_ftype_acc_uw2_uw2, FRV_BUILTIN_MQCPXRU);
def_builtin ("__MQCPXIS", void_ftype_acc_sw2_sw2, FRV_BUILTIN_MQCPXIS);
def_builtin ("__MQCPXIU", void_ftype_acc_uw2_uw2, FRV_BUILTIN_MQCPXIU);
def_builtin ("__MCUT", uw1_ftype_acc_uw1, FRV_BUILTIN_MCUT);
def_builtin ("__MCUTSS", uw1_ftype_acc_sw1, FRV_BUILTIN_MCUTSS);
def_builtin ("__MEXPDHW", uw1_ftype_uw1_int, FRV_BUILTIN_MEXPDHW);
def_builtin ("__MEXPDHD", uw2_ftype_uw1_int, FRV_BUILTIN_MEXPDHD);
def_builtin ("__MPACKH", uw1_ftype_uh_uh, FRV_BUILTIN_MPACKH);
def_builtin ("__MUNPACKH", uw2_ftype_uw1, FRV_BUILTIN_MUNPACKH);
def_builtin ("__MDPACKH", uw2_ftype_uh_uh_uh_uh, FRV_BUILTIN_MDPACKH);
def_builtin ("__MDUNPACKH", void_ftype_uw4_uw2, FRV_BUILTIN_MDUNPACKH);
def_builtin ("__MBTOH", uw2_ftype_uw1, FRV_BUILTIN_MBTOH);
def_builtin ("__MHTOB", uw1_ftype_uw2, FRV_BUILTIN_MHTOB);
def_builtin ("__MBTOHE", void_ftype_uw4_uw1, FRV_BUILTIN_MBTOHE);
def_builtin ("__MCLRACC", void_ftype_acc, FRV_BUILTIN_MCLRACC);
def_builtin ("__MCLRACCA", void_ftype_void, FRV_BUILTIN_MCLRACCA);
def_builtin ("__MRDACC", uw1_ftype_acc, FRV_BUILTIN_MRDACC);
def_builtin ("__MRDACCG", uw1_ftype_acc, FRV_BUILTIN_MRDACCG);
def_builtin ("__MWTACC", void_ftype_acc_uw1, FRV_BUILTIN_MWTACC);
def_builtin ("__MWTACCG", void_ftype_acc_uw1, FRV_BUILTIN_MWTACCG);
def_builtin ("__Mcop1", uw1_ftype_uw1_uw1, FRV_BUILTIN_MCOP1);
def_builtin ("__Mcop2", uw1_ftype_uw1_uw1, FRV_BUILTIN_MCOP2);
def_builtin ("__MTRAP", void_ftype_void, FRV_BUILTIN_MTRAP);
def_builtin ("__MQXMACHS", void_ftype_acc_sw2_sw2, FRV_BUILTIN_MQXMACHS);
def_builtin ("__MQXMACXHS", void_ftype_acc_sw2_sw2, FRV_BUILTIN_MQXMACXHS);
def_builtin ("__MQMACXHS", void_ftype_acc_sw2_sw2, FRV_BUILTIN_MQMACXHS);
def_builtin ("__MADDACCS", void_ftype_acc_acc, FRV_BUILTIN_MADDACCS);
def_builtin ("__MSUBACCS", void_ftype_acc_acc, FRV_BUILTIN_MSUBACCS);
def_builtin ("__MASACCS", void_ftype_acc_acc, FRV_BUILTIN_MASACCS);
def_builtin ("__MDADDACCS", void_ftype_acc_acc, FRV_BUILTIN_MDADDACCS);
def_builtin ("__MDSUBACCS", void_ftype_acc_acc, FRV_BUILTIN_MDSUBACCS);
def_builtin ("__MDASACCS", void_ftype_acc_acc, FRV_BUILTIN_MDASACCS);
def_builtin ("__MABSHS", uw1_ftype_sw1, FRV_BUILTIN_MABSHS);
def_builtin ("__MDROTLI", uw2_ftype_uw2_int, FRV_BUILTIN_MDROTLI);
def_builtin ("__MCPLHI", uw1_ftype_uw2_int, FRV_BUILTIN_MCPLHI);
def_builtin ("__MCPLI", uw1_ftype_uw2_int, FRV_BUILTIN_MCPLI);
def_builtin ("__MDCUTSSI", uw2_ftype_acc_int, FRV_BUILTIN_MDCUTSSI);
def_builtin ("__MQSATHS", sw2_ftype_sw2_sw2, FRV_BUILTIN_MQSATHS);
def_builtin ("__MHSETLOS", sw1_ftype_sw1_int, FRV_BUILTIN_MHSETLOS);
def_builtin ("__MHSETHIS", sw1_ftype_sw1_int, FRV_BUILTIN_MHSETHIS);
def_builtin ("__MHDSETS", sw1_ftype_int, FRV_BUILTIN_MHDSETS);
def_builtin ("__MHSETLOH", uw1_ftype_uw1_int, FRV_BUILTIN_MHSETLOH);
def_builtin ("__MHSETHIH", uw1_ftype_uw1_int, FRV_BUILTIN_MHSETHIH);
def_builtin ("__MHDSETH", uw1_ftype_uw1_int, FRV_BUILTIN_MHDSETH);
def_builtin ("__MQLCLRHS", sw2_ftype_sw2_sw2, FRV_BUILTIN_MQLCLRHS);
def_builtin ("__MQLMTHS", sw2_ftype_sw2_sw2, FRV_BUILTIN_MQLMTHS);
def_builtin ("__MQSLLHI", uw2_ftype_uw2_int, FRV_BUILTIN_MQSLLHI);
def_builtin ("__MQSRAHI", sw2_ftype_sw2_int, FRV_BUILTIN_MQSRAHI);
def_builtin ("__SMUL", sw2_ftype_sw1_sw1, FRV_BUILTIN_SMUL);
def_builtin ("__UMUL", uw2_ftype_uw1_uw1, FRV_BUILTIN_UMUL);
def_builtin ("__SMASS", void_ftype_sw1_sw1, FRV_BUILTIN_SMASS);
def_builtin ("__SMSSS", void_ftype_sw1_sw1, FRV_BUILTIN_SMSSS);
def_builtin ("__SMU", void_ftype_sw1_sw1, FRV_BUILTIN_SMU);
def_builtin ("__ADDSS", sw1_ftype_sw1_sw1, FRV_BUILTIN_ADDSS);
def_builtin ("__SUBSS", sw1_ftype_sw1_sw1, FRV_BUILTIN_SUBSS);
def_builtin ("__SLASS", sw1_ftype_sw1_sw1, FRV_BUILTIN_SLASS);
def_builtin ("__SCAN", sw1_ftype_sw1_sw1, FRV_BUILTIN_SCAN);
def_builtin ("__SCUTSS", sw1_ftype_sw1, FRV_BUILTIN_SCUTSS);
def_builtin ("__IACCreadll", sw2_ftype_iacc, FRV_BUILTIN_IACCreadll);
def_builtin ("__IACCreadl", sw1_ftype_iacc, FRV_BUILTIN_IACCreadl);
def_builtin ("__IACCsetll", void_ftype_iacc_sw2, FRV_BUILTIN_IACCsetll);
def_builtin ("__IACCsetl", void_ftype_iacc_sw1, FRV_BUILTIN_IACCsetl);
def_builtin ("__data_prefetch0", void_ftype_ptr, FRV_BUILTIN_PREFETCH0);
def_builtin ("__data_prefetch", void_ftype_ptr, FRV_BUILTIN_PREFETCH);
def_builtin ("__builtin_read8", uw1_ftype_vptr, FRV_BUILTIN_READ8);
def_builtin ("__builtin_read16", uw1_ftype_vptr, FRV_BUILTIN_READ16);
def_builtin ("__builtin_read32", uw1_ftype_vptr, FRV_BUILTIN_READ32);
def_builtin ("__builtin_read64", uw2_ftype_vptr, FRV_BUILTIN_READ64);
 
def_builtin ("__builtin_write8", void_ftype_vptr_ub, FRV_BUILTIN_WRITE8);
def_builtin ("__builtin_write16", void_ftype_vptr_uh, FRV_BUILTIN_WRITE16);
def_builtin ("__builtin_write32", void_ftype_vptr_uw1, FRV_BUILTIN_WRITE32);
def_builtin ("__builtin_write64", void_ftype_vptr_uw2, FRV_BUILTIN_WRITE64);
 
#undef UNARY
#undef BINARY
#undef TRINARY
#undef QUAD
}
 
/* Set the names for various arithmetic operations according to the
FRV ABI. */
static void
frv_init_libfuncs (void)
{
set_optab_libfunc (smod_optab, SImode, "__modi");
set_optab_libfunc (umod_optab, SImode, "__umodi");
 
set_optab_libfunc (add_optab, DImode, "__addll");
set_optab_libfunc (sub_optab, DImode, "__subll");
set_optab_libfunc (smul_optab, DImode, "__mulll");
set_optab_libfunc (sdiv_optab, DImode, "__divll");
set_optab_libfunc (smod_optab, DImode, "__modll");
set_optab_libfunc (umod_optab, DImode, "__umodll");
set_optab_libfunc (and_optab, DImode, "__andll");
set_optab_libfunc (ior_optab, DImode, "__orll");
set_optab_libfunc (xor_optab, DImode, "__xorll");
set_optab_libfunc (one_cmpl_optab, DImode, "__notll");
 
set_optab_libfunc (add_optab, SFmode, "__addf");
set_optab_libfunc (sub_optab, SFmode, "__subf");
set_optab_libfunc (smul_optab, SFmode, "__mulf");
set_optab_libfunc (sdiv_optab, SFmode, "__divf");
 
set_optab_libfunc (add_optab, DFmode, "__addd");
set_optab_libfunc (sub_optab, DFmode, "__subd");
set_optab_libfunc (smul_optab, DFmode, "__muld");
set_optab_libfunc (sdiv_optab, DFmode, "__divd");
 
set_conv_libfunc (sext_optab, DFmode, SFmode, "__ftod");
set_conv_libfunc (trunc_optab, SFmode, DFmode, "__dtof");
 
set_conv_libfunc (sfix_optab, SImode, SFmode, "__ftoi");
set_conv_libfunc (sfix_optab, DImode, SFmode, "__ftoll");
set_conv_libfunc (sfix_optab, SImode, DFmode, "__dtoi");
set_conv_libfunc (sfix_optab, DImode, DFmode, "__dtoll");
 
set_conv_libfunc (ufix_optab, SImode, SFmode, "__ftoui");
set_conv_libfunc (ufix_optab, DImode, SFmode, "__ftoull");
set_conv_libfunc (ufix_optab, SImode, DFmode, "__dtoui");
set_conv_libfunc (ufix_optab, DImode, DFmode, "__dtoull");
 
set_conv_libfunc (sfloat_optab, SFmode, SImode, "__itof");
set_conv_libfunc (sfloat_optab, SFmode, DImode, "__lltof");
set_conv_libfunc (sfloat_optab, DFmode, SImode, "__itod");
set_conv_libfunc (sfloat_optab, DFmode, DImode, "__lltod");
}
 
/* Convert an integer constant to an accumulator register. ICODE is the
code of the target instruction, OPNUM is the number of the
accumulator operand and OPVAL is the constant integer. Try both
ACC and ACCG registers; only report an error if neither fit the
instruction. */
 
static rtx
frv_int_to_acc (enum insn_code icode, int opnum, rtx opval)
{
rtx reg;
int i;
 
/* ACCs and ACCGs are implicit global registers if media intrinsics
are being used. We set up this lazily to avoid creating lots of
unnecessary call_insn rtl in non-media code. */
for (i = 0; i <= ACC_MASK; i++)
if ((i & ACC_MASK) == i)
global_regs[i + ACC_FIRST] = global_regs[i + ACCG_FIRST] = 1;
 
if (GET_CODE (opval) != CONST_INT)
{
error ("accumulator is not a constant integer");
return NULL_RTX;
}
if ((INTVAL (opval) & ~ACC_MASK) != 0)
{
error ("accumulator number is out of bounds");
return NULL_RTX;
}
 
reg = gen_rtx_REG (insn_data[icode].operand[opnum].mode,
ACC_FIRST + INTVAL (opval));
if (! (*insn_data[icode].operand[opnum].predicate) (reg, VOIDmode))
REGNO (reg) = ACCG_FIRST + INTVAL (opval);
 
if (! (*insn_data[icode].operand[opnum].predicate) (reg, VOIDmode))
{
error ("inappropriate accumulator for %qs", insn_data[icode].name);
return NULL_RTX;
}
return reg;
}
 
/* If an ACC rtx has mode MODE, return the mode that the matching ACCG
should have. */
 
static enum machine_mode
frv_matching_accg_mode (enum machine_mode mode)
{
switch (mode)
{
case V4SImode:
return V4QImode;
 
case DImode:
return HImode;
 
case SImode:
return QImode;
 
default:
gcc_unreachable ();
}
}
 
/* Given that a __builtin_read or __builtin_write function is accessing
address ADDRESS, return the value that should be used as operand 1
of the membar. */
 
static rtx
frv_io_address_cookie (rtx address)
{
return (GET_CODE (address) == CONST_INT
? GEN_INT (INTVAL (address) / 8 * 8)
: const0_rtx);
}
 
/* Return the accumulator guard that should be paired with accumulator
register ACC. The mode of the returned register is in the same
class as ACC, but is four times smaller. */
 
rtx
frv_matching_accg_for_acc (rtx acc)
{
return gen_rtx_REG (frv_matching_accg_mode (GET_MODE (acc)),
REGNO (acc) - ACC_FIRST + ACCG_FIRST);
}
 
/* Read a value from the head of the tree list pointed to by ARGLISTPTR.
Return the value as an rtx and replace *ARGLISTPTR with the tail of the
list. */
 
static rtx
frv_read_argument (tree *arglistptr)
{
tree next = TREE_VALUE (*arglistptr);
*arglistptr = TREE_CHAIN (*arglistptr);
return expand_expr (next, NULL_RTX, VOIDmode, 0);
}
 
/* Like frv_read_argument, but interpret the argument as the number
of an IACC register and return a (reg:MODE ...) rtx for it. */
 
static rtx
frv_read_iacc_argument (enum machine_mode mode, tree *arglistptr)
{
int i, regno;
rtx op;
 
op = frv_read_argument (arglistptr);
if (GET_CODE (op) != CONST_INT
|| INTVAL (op) < 0
|| INTVAL (op) > IACC_LAST - IACC_FIRST
|| ((INTVAL (op) * 4) & (GET_MODE_SIZE (mode) - 1)) != 0)
{
error ("invalid IACC argument");
op = const0_rtx;
}
 
/* IACCs are implicit global registers. We set up this lazily to
avoid creating lots of unnecessary call_insn rtl when IACCs aren't
being used. */
regno = INTVAL (op) + IACC_FIRST;
for (i = 0; i < HARD_REGNO_NREGS (regno, mode); i++)
global_regs[regno + i] = 1;
 
return gen_rtx_REG (mode, regno);
}
 
/* Return true if OPVAL can be used for operand OPNUM of instruction ICODE.
The instruction should require a constant operand of some sort. The
function prints an error if OPVAL is not valid. */
 
static int
frv_check_constant_argument (enum insn_code icode, int opnum, rtx opval)
{
if (GET_CODE (opval) != CONST_INT)
{
error ("%qs expects a constant argument", insn_data[icode].name);
return FALSE;
}
if (! (*insn_data[icode].operand[opnum].predicate) (opval, VOIDmode))
{
error ("constant argument out of range for %qs", insn_data[icode].name);
return FALSE;
}
return TRUE;
}
 
/* Return a legitimate rtx for instruction ICODE's return value. Use TARGET
if it's not null, has the right mode, and satisfies operand 0's
predicate. */
 
static rtx
frv_legitimize_target (enum insn_code icode, rtx target)
{
enum machine_mode mode = insn_data[icode].operand[0].mode;
 
if (! target
|| GET_MODE (target) != mode
|| ! (*insn_data[icode].operand[0].predicate) (target, mode))
return gen_reg_rtx (mode);
else
return target;
}
 
/* Given that ARG is being passed as operand OPNUM to instruction ICODE,
check whether ARG satisfies the operand's constraints. If it doesn't,
copy ARG to a temporary register and return that. Otherwise return ARG
itself. */
 
static rtx
frv_legitimize_argument (enum insn_code icode, int opnum, rtx arg)
{
enum machine_mode mode = insn_data[icode].operand[opnum].mode;
 
if ((*insn_data[icode].operand[opnum].predicate) (arg, mode))
return arg;
else
return copy_to_mode_reg (mode, arg);
}
 
/* Return a volatile memory reference of mode MODE whose address is ARG. */
 
static rtx
frv_volatile_memref (enum machine_mode mode, rtx arg)
{
rtx mem;
 
mem = gen_rtx_MEM (mode, memory_address (mode, arg));
MEM_VOLATILE_P (mem) = 1;
return mem;
}
 
/* Expand builtins that take a single, constant argument. At the moment,
only MHDSETS falls into this category. */
 
static rtx
frv_expand_set_builtin (enum insn_code icode, tree arglist, rtx target)
{
rtx pat;
rtx op0 = frv_read_argument (&arglist);
 
if (! frv_check_constant_argument (icode, 1, op0))
return NULL_RTX;
 
target = frv_legitimize_target (icode, target);
pat = GEN_FCN (icode) (target, op0);
if (! pat)
return NULL_RTX;
 
emit_insn (pat);
return target;
}
 
/* Expand builtins that take one operand. */
 
static rtx
frv_expand_unop_builtin (enum insn_code icode, tree arglist, rtx target)
{
rtx pat;
rtx op0 = frv_read_argument (&arglist);
 
target = frv_legitimize_target (icode, target);
op0 = frv_legitimize_argument (icode, 1, op0);
pat = GEN_FCN (icode) (target, op0);
if (! pat)
return NULL_RTX;
 
emit_insn (pat);
return target;
}
 
/* Expand builtins that take two operands. */
 
static rtx
frv_expand_binop_builtin (enum insn_code icode, tree arglist, rtx target)
{
rtx pat;
rtx op0 = frv_read_argument (&arglist);
rtx op1 = frv_read_argument (&arglist);
 
target = frv_legitimize_target (icode, target);
op0 = frv_legitimize_argument (icode, 1, op0);
op1 = frv_legitimize_argument (icode, 2, op1);
pat = GEN_FCN (icode) (target, op0, op1);
if (! pat)
return NULL_RTX;
 
emit_insn (pat);
return target;
}
 
/* Expand cut-style builtins, which take two operands and an implicit ACCG
one. */
 
static rtx
frv_expand_cut_builtin (enum insn_code icode, tree arglist, rtx target)
{
rtx pat;
rtx op0 = frv_read_argument (&arglist);
rtx op1 = frv_read_argument (&arglist);
rtx op2;
 
target = frv_legitimize_target (icode, target);
op0 = frv_int_to_acc (icode, 1, op0);
if (! op0)
return NULL_RTX;
 
if (icode == CODE_FOR_mdcutssi || GET_CODE (op1) == CONST_INT)
{
if (! frv_check_constant_argument (icode, 2, op1))
return NULL_RTX;
}
else
op1 = frv_legitimize_argument (icode, 2, op1);
 
op2 = frv_matching_accg_for_acc (op0);
pat = GEN_FCN (icode) (target, op0, op1, op2);
if (! pat)
return NULL_RTX;
 
emit_insn (pat);
return target;
}
 
/* Expand builtins that take two operands and the second is immediate. */
 
static rtx
frv_expand_binopimm_builtin (enum insn_code icode, tree arglist, rtx target)
{
rtx pat;
rtx op0 = frv_read_argument (&arglist);
rtx op1 = frv_read_argument (&arglist);
 
if (! frv_check_constant_argument (icode, 2, op1))
return NULL_RTX;
 
target = frv_legitimize_target (icode, target);
op0 = frv_legitimize_argument (icode, 1, op0);
pat = GEN_FCN (icode) (target, op0, op1);
if (! pat)
return NULL_RTX;
 
emit_insn (pat);
return target;
}
 
/* Expand builtins that take two operands, the first operand being a pointer to
ints and return void. */
 
static rtx
frv_expand_voidbinop_builtin (enum insn_code icode, tree arglist)
{
rtx pat;
rtx op0 = frv_read_argument (&arglist);
rtx op1 = frv_read_argument (&arglist);
enum machine_mode mode0 = insn_data[icode].operand[0].mode;
rtx addr;
 
if (GET_CODE (op0) != MEM)
{
rtx reg = op0;
 
if (! offsettable_address_p (0, mode0, op0))
{
reg = gen_reg_rtx (Pmode);
emit_insn (gen_rtx_SET (VOIDmode, reg, op0));
}
 
op0 = gen_rtx_MEM (SImode, reg);
}
 
addr = XEXP (op0, 0);
if (! offsettable_address_p (0, mode0, addr))
addr = copy_to_mode_reg (Pmode, op0);
 
op0 = change_address (op0, V4SImode, addr);
op1 = frv_legitimize_argument (icode, 1, op1);
pat = GEN_FCN (icode) (op0, op1);
if (! pat)
return 0;
 
emit_insn (pat);
return 0;
}
 
/* Expand builtins that take two long operands and return void. */
 
static rtx
frv_expand_int_void2arg (enum insn_code icode, tree arglist)
{
rtx pat;
rtx op0 = frv_read_argument (&arglist);
rtx op1 = frv_read_argument (&arglist);
 
op0 = frv_legitimize_argument (icode, 1, op0);
op1 = frv_legitimize_argument (icode, 1, op1);
pat = GEN_FCN (icode) (op0, op1);
if (! pat)
return NULL_RTX;
 
emit_insn (pat);
return NULL_RTX;
}
 
/* Expand prefetch builtins. These take a single address as argument. */
 
static rtx
frv_expand_prefetches (enum insn_code icode, tree arglist)
{
rtx pat;
rtx op0 = frv_read_argument (&arglist);
 
pat = GEN_FCN (icode) (force_reg (Pmode, op0));
if (! pat)
return 0;
 
emit_insn (pat);
return 0;
}
 
/* Expand builtins that take three operands and return void. The first
argument must be a constant that describes a pair or quad accumulators. A
fourth argument is created that is the accumulator guard register that
corresponds to the accumulator. */
 
static rtx
frv_expand_voidtriop_builtin (enum insn_code icode, tree arglist)
{
rtx pat;
rtx op0 = frv_read_argument (&arglist);
rtx op1 = frv_read_argument (&arglist);
rtx op2 = frv_read_argument (&arglist);
rtx op3;
 
op0 = frv_int_to_acc (icode, 0, op0);
if (! op0)
return NULL_RTX;
 
op1 = frv_legitimize_argument (icode, 1, op1);
op2 = frv_legitimize_argument (icode, 2, op2);
op3 = frv_matching_accg_for_acc (op0);
pat = GEN_FCN (icode) (op0, op1, op2, op3);
if (! pat)
return NULL_RTX;
 
emit_insn (pat);
return NULL_RTX;
}
 
/* Expand builtins that perform accumulator-to-accumulator operations.
These builtins take two accumulator numbers as argument and return
void. */
 
static rtx
frv_expand_voidaccop_builtin (enum insn_code icode, tree arglist)
{
rtx pat;
rtx op0 = frv_read_argument (&arglist);
rtx op1 = frv_read_argument (&arglist);
rtx op2;
rtx op3;
 
op0 = frv_int_to_acc (icode, 0, op0);
if (! op0)
return NULL_RTX;
 
op1 = frv_int_to_acc (icode, 1, op1);
if (! op1)
return NULL_RTX;
 
op2 = frv_matching_accg_for_acc (op0);
op3 = frv_matching_accg_for_acc (op1);
pat = GEN_FCN (icode) (op0, op1, op2, op3);
if (! pat)
return NULL_RTX;
 
emit_insn (pat);
return NULL_RTX;
}
 
/* Expand a __builtin_read* function. ICODE is the instruction code for the
membar and TARGET_MODE is the mode that the loaded value should have. */
 
static rtx
frv_expand_load_builtin (enum insn_code icode, enum machine_mode target_mode,
tree arglist, rtx target)
{
rtx op0 = frv_read_argument (&arglist);
rtx cookie = frv_io_address_cookie (op0);
 
if (target == 0 || !REG_P (target))
target = gen_reg_rtx (target_mode);
op0 = frv_volatile_memref (insn_data[icode].operand[0].mode, op0);
convert_move (target, op0, 1);
emit_insn (GEN_FCN (icode) (copy_rtx (op0), cookie, GEN_INT (FRV_IO_READ)));
cfun->machine->has_membar_p = 1;
return target;
}
 
/* Likewise __builtin_write* functions. */
 
static rtx
frv_expand_store_builtin (enum insn_code icode, tree arglist)
{
rtx op0 = frv_read_argument (&arglist);
rtx op1 = frv_read_argument (&arglist);
rtx cookie = frv_io_address_cookie (op0);
 
op0 = frv_volatile_memref (insn_data[icode].operand[0].mode, op0);
convert_move (op0, force_reg (insn_data[icode].operand[0].mode, op1), 1);
emit_insn (GEN_FCN (icode) (copy_rtx (op0), cookie, GEN_INT (FRV_IO_WRITE)));
cfun->machine->has_membar_p = 1;
return NULL_RTX;
}
 
/* Expand the MDPACKH builtin. It takes four unsigned short arguments and
each argument forms one word of the two double-word input registers.
ARGLIST is a TREE_LIST of the arguments and TARGET, if nonnull,
suggests a good place to put the return value. */
 
static rtx
frv_expand_mdpackh_builtin (tree arglist, rtx target)
{
enum insn_code icode = CODE_FOR_mdpackh;
rtx pat, op0, op1;
rtx arg1 = frv_read_argument (&arglist);
rtx arg2 = frv_read_argument (&arglist);
rtx arg3 = frv_read_argument (&arglist);
rtx arg4 = frv_read_argument (&arglist);
 
target = frv_legitimize_target (icode, target);
op0 = gen_reg_rtx (DImode);
op1 = gen_reg_rtx (DImode);
 
/* The high half of each word is not explicitly initialized, so indicate
that the input operands are not live before this point. */
emit_insn (gen_rtx_CLOBBER (DImode, op0));
emit_insn (gen_rtx_CLOBBER (DImode, op1));
 
/* Move each argument into the low half of its associated input word. */
emit_move_insn (simplify_gen_subreg (HImode, op0, DImode, 2), arg1);
emit_move_insn (simplify_gen_subreg (HImode, op0, DImode, 6), arg2);
emit_move_insn (simplify_gen_subreg (HImode, op1, DImode, 2), arg3);
emit_move_insn (simplify_gen_subreg (HImode, op1, DImode, 6), arg4);
 
pat = GEN_FCN (icode) (target, op0, op1);
if (! pat)
return NULL_RTX;
 
emit_insn (pat);
return target;
}
 
/* Expand the MCLRACC builtin. This builtin takes a single accumulator
number as argument. */
 
static rtx
frv_expand_mclracc_builtin (tree arglist)
{
enum insn_code icode = CODE_FOR_mclracc;
rtx pat;
rtx op0 = frv_read_argument (&arglist);
 
op0 = frv_int_to_acc (icode, 0, op0);
if (! op0)
return NULL_RTX;
 
pat = GEN_FCN (icode) (op0);
if (pat)
emit_insn (pat);
 
return NULL_RTX;
}
 
/* Expand builtins that take no arguments. */
 
static rtx
frv_expand_noargs_builtin (enum insn_code icode)
{
rtx pat = GEN_FCN (icode) (const0_rtx);
if (pat)
emit_insn (pat);
 
return NULL_RTX;
}
 
/* Expand MRDACC and MRDACCG. These builtins take a single accumulator
number or accumulator guard number as argument and return an SI integer. */
 
static rtx
frv_expand_mrdacc_builtin (enum insn_code icode, tree arglist)
{
rtx pat;
rtx target = gen_reg_rtx (SImode);
rtx op0 = frv_read_argument (&arglist);
 
op0 = frv_int_to_acc (icode, 1, op0);
if (! op0)
return NULL_RTX;
 
pat = GEN_FCN (icode) (target, op0);
if (! pat)
return NULL_RTX;
 
emit_insn (pat);
return target;
}
 
/* Expand MWTACC and MWTACCG. These builtins take an accumulator or
accumulator guard as their first argument and an SImode value as their
second. */
 
static rtx
frv_expand_mwtacc_builtin (enum insn_code icode, tree arglist)
{
rtx pat;
rtx op0 = frv_read_argument (&arglist);
rtx op1 = frv_read_argument (&arglist);
 
op0 = frv_int_to_acc (icode, 0, op0);
if (! op0)
return NULL_RTX;
 
op1 = frv_legitimize_argument (icode, 1, op1);
pat = GEN_FCN (icode) (op0, op1);
if (pat)
emit_insn (pat);
 
return NULL_RTX;
}
 
/* Emit a move from SRC to DEST in SImode chunks. This can be used
to move DImode values into and out of IACC0. */
 
static void
frv_split_iacc_move (rtx dest, rtx src)
{
enum machine_mode inner;
int i;
 
inner = GET_MODE (dest);
for (i = 0; i < GET_MODE_SIZE (inner); i += GET_MODE_SIZE (SImode))
emit_move_insn (simplify_gen_subreg (SImode, dest, inner, i),
simplify_gen_subreg (SImode, src, inner, i));
}
 
/* Expand builtins. */
 
static rtx
frv_expand_builtin (tree exp,
rtx target,
rtx subtarget ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED,
int ignore ATTRIBUTE_UNUSED)
{
tree arglist = TREE_OPERAND (exp, 1);
tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
unsigned fcode = (unsigned)DECL_FUNCTION_CODE (fndecl);
unsigned i;
struct builtin_description *d;
 
if (fcode < FRV_BUILTIN_FIRST_NONMEDIA && !TARGET_MEDIA)
{
error ("media functions are not available unless -mmedia is used");
return NULL_RTX;
}
 
switch (fcode)
{
case FRV_BUILTIN_MCOP1:
case FRV_BUILTIN_MCOP2:
case FRV_BUILTIN_MDUNPACKH:
case FRV_BUILTIN_MBTOHE:
if (! TARGET_MEDIA_REV1)
{
error ("this media function is only available on the fr500");
return NULL_RTX;
}
break;
 
case FRV_BUILTIN_MQXMACHS:
case FRV_BUILTIN_MQXMACXHS:
case FRV_BUILTIN_MQMACXHS:
case FRV_BUILTIN_MADDACCS:
case FRV_BUILTIN_MSUBACCS:
case FRV_BUILTIN_MASACCS:
case FRV_BUILTIN_MDADDACCS:
case FRV_BUILTIN_MDSUBACCS:
case FRV_BUILTIN_MDASACCS:
case FRV_BUILTIN_MABSHS:
case FRV_BUILTIN_MDROTLI:
case FRV_BUILTIN_MCPLHI:
case FRV_BUILTIN_MCPLI:
case FRV_BUILTIN_MDCUTSSI:
case FRV_BUILTIN_MQSATHS:
case FRV_BUILTIN_MHSETLOS:
case FRV_BUILTIN_MHSETLOH:
case FRV_BUILTIN_MHSETHIS:
case FRV_BUILTIN_MHSETHIH:
case FRV_BUILTIN_MHDSETS:
case FRV_BUILTIN_MHDSETH:
if (! TARGET_MEDIA_REV2)
{
error ("this media function is only available on the fr400"
" and fr550");
return NULL_RTX;
}
break;
 
case FRV_BUILTIN_SMASS:
case FRV_BUILTIN_SMSSS:
case FRV_BUILTIN_SMU:
case FRV_BUILTIN_ADDSS:
case FRV_BUILTIN_SUBSS:
case FRV_BUILTIN_SLASS:
case FRV_BUILTIN_SCUTSS:
case FRV_BUILTIN_IACCreadll:
case FRV_BUILTIN_IACCreadl:
case FRV_BUILTIN_IACCsetll:
case FRV_BUILTIN_IACCsetl:
if (!TARGET_FR405_BUILTINS)
{
error ("this builtin function is only available"
" on the fr405 and fr450");
return NULL_RTX;
}
break;
 
case FRV_BUILTIN_PREFETCH:
if (!TARGET_FR500_FR550_BUILTINS)
{
error ("this builtin function is only available on the fr500"
" and fr550");
return NULL_RTX;
}
break;
 
case FRV_BUILTIN_MQLCLRHS:
case FRV_BUILTIN_MQLMTHS:
case FRV_BUILTIN_MQSLLHI:
case FRV_BUILTIN_MQSRAHI:
if (!TARGET_MEDIA_FR450)
{
error ("this builtin function is only available on the fr450");
return NULL_RTX;
}
break;
 
default:
break;
}
 
/* Expand unique builtins. */
 
switch (fcode)
{
case FRV_BUILTIN_MTRAP:
return frv_expand_noargs_builtin (CODE_FOR_mtrap);
 
case FRV_BUILTIN_MCLRACC:
return frv_expand_mclracc_builtin (arglist);
 
case FRV_BUILTIN_MCLRACCA:
if (TARGET_ACC_8)
return frv_expand_noargs_builtin (CODE_FOR_mclracca8);
else
return frv_expand_noargs_builtin (CODE_FOR_mclracca4);
 
case FRV_BUILTIN_MRDACC:
return frv_expand_mrdacc_builtin (CODE_FOR_mrdacc, arglist);
 
case FRV_BUILTIN_MRDACCG:
return frv_expand_mrdacc_builtin (CODE_FOR_mrdaccg, arglist);
 
case FRV_BUILTIN_MWTACC:
return frv_expand_mwtacc_builtin (CODE_FOR_mwtacc, arglist);
 
case FRV_BUILTIN_MWTACCG:
return frv_expand_mwtacc_builtin (CODE_FOR_mwtaccg, arglist);
 
case FRV_BUILTIN_MDPACKH:
return frv_expand_mdpackh_builtin (arglist, target);
 
case FRV_BUILTIN_IACCreadll:
{
rtx src = frv_read_iacc_argument (DImode, &arglist);
if (target == 0 || !REG_P (target))
target = gen_reg_rtx (DImode);
frv_split_iacc_move (target, src);
return target;
}
 
case FRV_BUILTIN_IACCreadl:
return frv_read_iacc_argument (SImode, &arglist);
 
case FRV_BUILTIN_IACCsetll:
{
rtx dest = frv_read_iacc_argument (DImode, &arglist);
rtx src = frv_read_argument (&arglist);
frv_split_iacc_move (dest, force_reg (DImode, src));
return 0;
}
 
case FRV_BUILTIN_IACCsetl:
{
rtx dest = frv_read_iacc_argument (SImode, &arglist);
rtx src = frv_read_argument (&arglist);
emit_move_insn (dest, force_reg (SImode, src));
return 0;
}
 
default:
break;
}
 
/* Expand groups of builtins. */
 
for (i = 0, d = bdesc_set; i < ARRAY_SIZE (bdesc_set); i++, d++)
if (d->code == fcode)
return frv_expand_set_builtin (d->icode, arglist, target);
 
for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++)
if (d->code == fcode)
return frv_expand_unop_builtin (d->icode, arglist, target);
 
for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++)
if (d->code == fcode)
return frv_expand_binop_builtin (d->icode, arglist, target);
 
for (i = 0, d = bdesc_cut; i < ARRAY_SIZE (bdesc_cut); i++, d++)
if (d->code == fcode)
return frv_expand_cut_builtin (d->icode, arglist, target);
 
for (i = 0, d = bdesc_2argimm; i < ARRAY_SIZE (bdesc_2argimm); i++, d++)
if (d->code == fcode)
return frv_expand_binopimm_builtin (d->icode, arglist, target);
 
for (i = 0, d = bdesc_void2arg; i < ARRAY_SIZE (bdesc_void2arg); i++, d++)
if (d->code == fcode)
return frv_expand_voidbinop_builtin (d->icode, arglist);
 
for (i = 0, d = bdesc_void3arg; i < ARRAY_SIZE (bdesc_void3arg); i++, d++)
if (d->code == fcode)
return frv_expand_voidtriop_builtin (d->icode, arglist);
 
for (i = 0, d = bdesc_voidacc; i < ARRAY_SIZE (bdesc_voidacc); i++, d++)
if (d->code == fcode)
return frv_expand_voidaccop_builtin (d->icode, arglist);
 
for (i = 0, d = bdesc_int_void2arg;
i < ARRAY_SIZE (bdesc_int_void2arg); i++, d++)
if (d->code == fcode)
return frv_expand_int_void2arg (d->icode, arglist);
 
for (i = 0, d = bdesc_prefetches;
i < ARRAY_SIZE (bdesc_prefetches); i++, d++)
if (d->code == fcode)
return frv_expand_prefetches (d->icode, arglist);
 
for (i = 0, d = bdesc_loads; i < ARRAY_SIZE (bdesc_loads); i++, d++)
if (d->code == fcode)
return frv_expand_load_builtin (d->icode, TYPE_MODE (TREE_TYPE (exp)),
arglist, target);
 
for (i = 0, d = bdesc_stores; i < ARRAY_SIZE (bdesc_stores); i++, d++)
if (d->code == fcode)
return frv_expand_store_builtin (d->icode, arglist);
 
return 0;
}
 
static bool
frv_in_small_data_p (tree decl)
{
HOST_WIDE_INT size;
tree section_name;
 
/* Don't apply the -G flag to internal compiler structures. We
should leave such structures in the main data section, partly
for efficiency and partly because the size of some of them
(such as C++ typeinfos) is not known until later. */
if (TREE_CODE (decl) != VAR_DECL || DECL_ARTIFICIAL (decl))
return false;
 
/* If we already know which section the decl should be in, see if
it's a small data section. */
section_name = DECL_SECTION_NAME (decl);
if (section_name)
{
gcc_assert (TREE_CODE (section_name) == STRING_CST);
if (frv_string_begins_with (section_name, ".sdata"))
return true;
if (frv_string_begins_with (section_name, ".sbss"))
return true;
return false;
}
 
size = int_size_in_bytes (TREE_TYPE (decl));
if (size > 0 && (unsigned HOST_WIDE_INT) size <= g_switch_value)
return true;
 
return false;
}
static bool
frv_rtx_costs (rtx x,
int code ATTRIBUTE_UNUSED,
int outer_code ATTRIBUTE_UNUSED,
int *total)
{
if (outer_code == MEM)
{
/* Don't differentiate between memory addresses. All the ones
we accept have equal cost. */
*total = COSTS_N_INSNS (0);
return true;
}
 
switch (code)
{
case CONST_INT:
/* Make 12 bit integers really cheap. */
if (IN_RANGE_P (INTVAL (x), -2048, 2047))
{
*total = 0;
return true;
}
/* Fall through. */
 
case CONST:
case LABEL_REF:
case SYMBOL_REF:
case CONST_DOUBLE:
*total = COSTS_N_INSNS (2);
return true;
 
case PLUS:
case MINUS:
case AND:
case IOR:
case XOR:
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
case NOT:
case NEG:
case COMPARE:
if (GET_MODE (x) == SImode)
*total = COSTS_N_INSNS (1);
else if (GET_MODE (x) == DImode)
*total = COSTS_N_INSNS (2);
else
*total = COSTS_N_INSNS (3);
return true;
 
case MULT:
if (GET_MODE (x) == SImode)
*total = COSTS_N_INSNS (2);
else
*total = COSTS_N_INSNS (6); /* guess */
return true;
 
case DIV:
case UDIV:
case MOD:
case UMOD:
*total = COSTS_N_INSNS (18);
return true;
 
case MEM:
*total = COSTS_N_INSNS (3);
return true;
 
default:
return false;
}
}
static void
frv_asm_out_constructor (rtx symbol, int priority ATTRIBUTE_UNUSED)
{
switch_to_section (ctors_section);
assemble_align (POINTER_SIZE);
if (TARGET_FDPIC)
{
int ok = frv_assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, 1);
 
gcc_assert (ok);
return;
}
assemble_integer_with_op ("\t.picptr\t", symbol);
}
 
static void
frv_asm_out_destructor (rtx symbol, int priority ATTRIBUTE_UNUSED)
{
switch_to_section (dtors_section);
assemble_align (POINTER_SIZE);
if (TARGET_FDPIC)
{
int ok = frv_assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, 1);
gcc_assert (ok);
return;
}
assemble_integer_with_op ("\t.picptr\t", symbol);
}
 
/* Worker function for TARGET_STRUCT_VALUE_RTX. */
 
static rtx
frv_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
int incoming ATTRIBUTE_UNUSED)
{
return gen_rtx_REG (Pmode, FRV_STRUCT_VALUE_REGNUM);
}
 
#define TLS_BIAS (2048 - 16)
 
/* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL.
We need to emit DTP-relative relocations. */
 
static void
frv_output_dwarf_dtprel (FILE *file, int size, rtx x)
{
gcc_assert (size == 4);
fputs ("\t.picptr\ttlsmoff(", file);
/* We want the unbiased TLS offset, so add the bias to the
expression, such that the implicit biasing cancels out. */
output_addr_const (file, plus_constant (x, TLS_BIAS));
fputs (")", file);
}
 
#include "gt-frv.h"

powered by: WebSVN 2.1.0

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