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 = ®_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" |