<s>
const
char
*
thumb_load_double_from_address
(
rtx
*
operands
)
{
rtx
addr
;
rtx
base
;
rtx
offset
;
rtx
arg1
;
rtx
arg2
;
gcc_assert
(
GET_CODE
(
operands
[
0
]
)
==
REG
)
;
gcc_assert
(
GET_CODE
(
operands
[
1
]
)
==
MEM
)
;
addr
=
XEXP
(
operands
[
1
]
,
0
)
;
switch
(
GET_CODE
(
addr
)
)
{
case
REG
:
operands
[
2
]
=
adjust_address
(
operands
[
1
]
,
SImode
,
4
)
;
if
(
REGNO
(
operands
[
0
]
)
==
REGNO
(
addr
)
)
{
output_asm_insn
(
"ldr\t%H0, %2"
,
operands
)
;
output_asm_insn
(
"ldr\t%0, %1"
,
operands
)
;
}
else
{
output_asm_insn
(
"ldr\t%0, %1"
,
operands
)
;
output_asm_insn
(
"ldr\t%H0, %2"
,
operands
)
;
}
break
;
case
CONST
:
operands
[
2
]
=
adjust_address
(
operands
[
1
]
,
SImode
,
4
)
;
output_asm_insn
(
"ldr\t%0, %1"
,
operands
)
;
output_asm_insn
(
"ldr\t%H0, %2"
,
operands
)
;
break
;
case
PLUS
:
arg1
=
XEXP
(
addr
,
0
)
;
arg2
=
XEXP
(
addr
,
1
)
;
if
(
CONSTANT_P
(
arg1
)
)
base
=
arg2
,
offset
=
arg1
;
else
base
=
arg1
,
offset
=
arg2
;
gcc_assert
(
GET_CODE
(
base
)
==
REG
)
;
if
(
GET_CODE
(
offset
)
==
REG
)
{
int
reg_offset
=
REGNO
(
offset
)
;
int
reg_base
=
REGNO
(
base
)
;
int
reg_dest
=
REGNO
(
operands
[
0
]
)
;
asm_fprintf
(
asm_out_file
,
"\tadd\t%r, %r, %r"
,
reg_dest
+
1
,
reg_base
,
reg_offset
)
;
asm_fprintf
(
asm_out_file
,
"\tldr\t%r, [%r, #0]"
,
reg_dest
,
reg_dest
+
1
)
;
asm_fprintf
(
asm_out_file
,
"\tldr\t%r, [%r, #4]"
,
reg_dest
+
1
,
reg_dest
+
1
)
;
}
else
{
operands
[
2
]
=
adjust_address
(
operands
[
1
]
,
SImode
,
4
)
;
if
(
REGNO
(
operands
[
0
]
)
==
REGNO
(
base
)
)
{
output_asm_insn
(
"ldr\t%H0, %2"
,
operands
)
;
output_asm_insn
(
"ldr\t%0, %1"
,
operands
)
;
}
else
{
output_asm_insn
(
"ldr\t%0, %1"
,
operands
)
;
output_asm_insn
(
"ldr\t%H0, %2"
,
operands
)
;
}
}
break
;
case
LABEL_REF
:
operands
[
2
]
=
adjust_address
(
operands
[
1
]
,
SImode
,
4
)
;
output_asm_insn
(
"ldr\t%H0, %2"
,
operands
)
;
output_asm_insn
(
"ldr\t%0, %1"
,
operands
)
;
break
;
default
:
gcc_unreachable
(
)
;
}
return
""
;
}
</s>

Handle the case of a double word load into a low register from a computed memory address . The computed address may involve a register which is overwritten by the load .


<s>
static
void
thumb_pushpop
(
FILE
*
f
,
unsigned
long
mask
,
int
push
,
int
*
cfa_offset
,
unsigned
long
real_regs
)
{
int
regno
;
int
lo_mask
=
mask
&
0xFF
;
int
pushed_words
=
0
;
gcc_assert
(
mask
)
;
if
(
lo_mask
==
0
&&
!
push
&&
(
mask
&
(
1
<<
PC_REGNUM
)
)
)
{
thumb_exit
(
f
,
1
)
;
return
;
}
if
(
ARM_EABI_UNWIND_TABLES
&&
push
)
{
fprintf
(
f
,
"\t.save\t{"
)
;
for
(
regno
=
0
;
regno
<
15
;
regno
++
)
{
if
(
real_regs
&
(
1
<<
regno
)
)
{
if
(
real_regs
&
(
(
1
<<
regno
)
1
)
)
fprintf
(
f
,
", "
)
;
asm_fprintf
(
f
,
"%r"
,
regno
)
;
}
}
fprintf
(
f
,
"}\n"
)
;
}
fprintf
(
f
,
"\t%s\t{"
,
push
"push"
:
"pop"
)
;
for
(
regno
=
0
;
regno
<=
LAST_LO_REGNUM
;
regno
++
,
lo_mask
>>=
1
)
{
if
(
lo_mask
&
1
)
{
asm_fprintf
(
f
,
"%r"
,
regno
)
;
if
(
(
lo_mask
&
~
1
)
!=
0
)
fprintf
(
f
,
", "
)
;
pushed_words
++
;
}
}
if
(
push
&&
(
mask
&
(
1
<<
LR_REGNUM
)
)
)
{
if
(
mask
&
0xFF
)
fprintf
(
f
,
", "
)
;
asm_fprintf
(
f
,
"%r"
,
LR_REGNUM
)
;
pushed_words
++
;
}
else
if
(
!
push
&&
(
mask
&
(
1
<<
PC_REGNUM
)
)
)
{
if
(
TARGET_INTERWORK
||
TARGET_BACKTRACE
||
current_function_calls_eh_return
)
{
fprintf
(
f
,
"}\n"
)
;
thumb_exit
(
f
,
1
)
;
return
;
}
else
{
if
(
mask
&
0xFF
)
fprintf
(
f
,
", "
)
;
asm_fprintf
(
f
,
"%r"
,
PC_REGNUM
)
;
}
}
fprintf
(
f
,
"}\n"
)
;
if
(
push
&&
pushed_words
&&
dwarf2out_do_frame
(
)
)
{
char
*
l
=
dwarf2out_cfi_label
(
)
;
int
pushed_mask
=
real_regs
;
*
cfa_offset
+=
pushed_words
*
4
;
dwarf2out_def_cfa
(
l
,
SP_REGNUM
,
*
cfa_offset
)
;
pushed_words
=
0
;
pushed_mask
=
real_regs
;
for
(
regno
=
0
;
regno
<=
14
;
regno
++
,
pushed_mask
>>=
1
)
{
if
(
pushed_mask
&
1
)
dwarf2out_reg_save
(
l
,
regno
,
4
*
pushed_words
++
*
cfa_offset
)
;
}
}
}
</s>

Emit code to push or pop registers to or from the stack . F is the assembly file . MASK is the registers to push or pop . PUSH is nonzero if we should push , and zero if we should pop . For debugging output , if pushing , adjust CFA_OFFSET by the amount of space added to the stack . REAL_REGS should have the same number of bits set as MASK , and will be used instead ( in the same order ) to describe which registers were saved - this is used to mark the save slots when we push high registers after moving them to low registers .


"no low registers available for popping high registers"

The bits which are n't usefully expanded as rtl .


<s> int use_return_insn ( int iscond , rtx sibling ) { int regno ; unsigned int func_type ; unsigned long saved_int_regs ; unsigned HOST_WIDE_INT stack_adjust ; arm_stack_offsets * offsets ; if ( ! reload_completed ) return 0 ; func_type = arm_current_func_type ( ) ; if ( func_type & ( ARM_FT_VOLATILE | ARM_FT_NAKED ) ) return 0 ; if ( IS_INTERRUPT ( func_type ) && frame_pointer_needed ) return 0 ; offsets = arm_get_frame_offsets ( ) ; stack_adjust = offsets -> outgoing_args - offsets -> saved_regs ; if ( current_function_pretend_args_size || cfun -> machine -> uses_anonymous_args || current_function_calls_eh_return || current_function_calls_alloca || ! ( stack_adjust == 0 || ( frame_pointer_needed && stack_adjust == 4 ) ) ) return 0 ; saved_int_regs = arm_compute_save_reg_mask ( ) ; if ( stack_adjust == 4 && ! arm_arch5 ) { if ( ! call_used_regs [ 3 ] ) return 0 ; if ( arm_size_return_regs ( ) >= ( 4 * UNITS_PER_WORD ) ) return 0 ; if ( sibling ) { gcc_assert ( GET_CODE ( sibling ) == CALL_INSN ) ; if ( find_regno_fusage ( sibling , USE , 3 ) ) return 0 ; } if ( saved_int_regs & 0x7 ) return 0 ; } if ( TARGET_INTERWORK && saved_int_regs != 0 ) return 0 ; if ( iscond && arm_tune_strongarm ) { if ( saved_int_regs != 0 && saved_int_regs != ( 1 << LR_REGNUM ) ) return 0 ; if ( flag_pic && regs_ever_live [ PIC_OFFSET_TABLE_REGNUM ] ) return 0 ; } if ( saved_int_regs && ! ( saved_int_regs & ( 1 << LR_REGNUM ) ) ) return 0 ; if ( TARGET_HARD_FLOAT && TARGET_FPA ) for ( regno = FIRST_FPA_REGNUM ; regno <= LAST_FPA_REGNUM ; regno ++ ) if ( regs_ever_live [ regno ] && ! call_used_regs [ regno ] ) return 0 ; if ( TARGET_HARD_FLOAT && TARGET_VFP ) for ( regno = FIRST_VFP_REGNUM ; regno <= LAST_VFP_REGNUM ; regno ++ ) if ( regs_ever_live [ regno ] && ! call_used_regs [ regno ] ) return 0 ; if ( TARGET_REALLY_IWMMXT ) for ( regno = FIRST_IWMMXT_REGNUM ; regno <= LAST_IWMMXT_REGNUM ; regno ++ ) if ( regs_ever_live [ regno ] && ! call_used_regs [ regno ] ) return 0 ; return 1 ; } </s>

Return true if this function 's epilogue can be output as RTL .


<s> static int vfp_emit_fstmx ( int base_reg , int count ) { rtx par ; rtx dwarf ; rtx tmp , reg ; int i ; if ( count == 2 && ! arm_arch6 ) { if ( base_reg == LAST_VFP_REGNUM - 3 ) base_reg -= 2 ; count ++ ; } par = gen_rtx_PARALLEL ( VOIDmode , rtvec_alloc ( count ) ) ; dwarf = gen_rtx_SEQUENCE ( VOIDmode , rtvec_alloc ( count + 1 ) ) ; reg = gen_rtx_REG ( DFmode , base_reg ) ; base_reg += 2 ; XVECEXP ( par , 0 , 0 ) = gen_rtx_SET ( VOIDmode , gen_frame_mem ( BLKmode , gen_rtx_PRE_DEC ( BLKmode , stack_pointer_rtx ) ) , gen_rtx_UNSPEC ( BLKmode , gen_rtvec ( 1 , reg ) , UNSPEC_PUSH_MULT ) ) ; tmp = gen_rtx_SET ( VOIDmode , stack_pointer_rtx , gen_rtx_PLUS ( SImode , stack_pointer_rtx , GEN_INT ( - ( count * 8 + 4 ) ) ) ) ; RTX_FRAME_RELATED_P ( tmp ) = 1 ; XVECEXP ( dwarf , 0 , 0 ) = tmp ; tmp = gen_rtx_SET ( VOIDmode , gen_frame_mem ( DFmode , stack_pointer_rtx ) , reg ) ; RTX_FRAME_RELATED_P ( tmp ) = 1 ; XVECEXP ( dwarf , 0 , 1 ) = tmp ; for ( i = 1 ; i < count ; i ++ ) { reg = gen_rtx_REG ( DFmode , base_reg ) ; base_reg += 2 ; XVECEXP ( par , 0 , i ) = gen_rtx_USE ( VOIDmode , reg ) ; tmp = gen_rtx_SET ( VOIDmode , gen_frame_mem ( DFmode , gen_rtx_PLUS ( SImode , stack_pointer_rtx , GEN_INT ( i * 8 ) ) ) , reg ) ; RTX_FRAME_RELATED_P ( tmp ) = 1 ; XVECEXP ( dwarf , 0 , i + 1 ) = tmp ; } par = emit_insn ( par ) ; REG_NOTES ( par ) = gen_rtx_EXPR_LIST ( REG_FRAME_RELATED_EXPR , dwarf , REG_NOTES ( par ) ) ; RTX_FRAME_RELATED_P ( par ) = 1 ; return count * 8 + 4 ; } </s>

Emit RTL to save block of VFP register pairs to the stack . Returns the number of bytes pushed .


<s>
const
char
*
vfp_output_fstmx
(
rtx
*
operands
)
{
char
pattern
[
100
]
;
int
p
;
int
base
;
int
i
;
strcpy
(
pattern
,
"fstmfdx\t%m0!, {%P1"
)
;
p
=
strlen
(
pattern
)
;
gcc_assert
(
GET_CODE
(
operands
[
1
]
)
==
REG
)
;
base
=
(
REGNO
(
operands
[
1
]
)
FIRST_VFP_REGNUM
)
/
2
;
for
(
i
=
1
;
i
<
XVECLEN
(
operands
[
2
]
,
0
)
;
i
++
)
{
p
+=
sprintf
(
&
pattern
[
p
]
,
", d%d"
,
base
+
i
)
;
}
strcpy
(
&
pattern
[
p
]
,
"}"
)
;
output_asm_insn
(
pattern
,
operands
)
;
return
""
;
}
</s>

Output the assembly for a store multiple .


<s> enum reg_class vfp_secondary_reload_class ( enum machine_mode mode , rtx x ) { if ( arm_coproc_mem_operand ( x , FALSE ) || s_register_operand ( x , mode ) ) return NO_REGS ; return GENERAL_REGS ; } </s>

Return GENERAL_REGS if a scratch register required to reload x to/from VFP registers . Otherwise return NO_REGS .


<s> static void aapcs_layout_arg ( CUMULATIVE_ARGS * pcum , machine_mode mode , const_tree type , bool named ) { int nregs , nregs2 ; int ncrn ; if ( pcum -> aapcs_arg_processed ) return ; pcum -> aapcs_arg_processed = true ; if ( ! named ) return ; if ( pcum -> pcs_variant != ARM_PCS_AAPCS ) { int slot = aapcs_select_call_coproc ( pcum , mode , type ) ; pcum -> aapcs_cprc_slot = slot ; if ( slot >= 0 ) { if ( ! pcum -> aapcs_cprc_failed [ slot ] ) { if ( aapcs_cp_arg_layout [ slot ] . allocate ( pcum , mode , type ) ) return ; pcum -> aapcs_cprc_failed [ slot ] = true ; pcum -> can_split = false ; } gcc_assert ( pcum -> can_split == false ) ; return ; } } ncrn = pcum -> aapcs_ncrn ; if ( ( ncrn & 1 ) && arm_needs_doubleword_align ( mode , type ) ) ncrn ++ ; nregs = ARM_NUM_REGS2 ( mode , type ) ; gcc_assert ( nregs >= 0 ) ; nregs2 = nregs ? nregs : 1 ; if ( ncrn + nregs2 <= NUM_ARG_REGS ) { pcum -> aapcs_reg = gen_rtx_REG ( mode , ncrn ) ; pcum -> aapcs_next_ncrn = ncrn + nregs ; return ; } if ( ncrn < NUM_ARG_REGS && pcum -> can_split ) { pcum -> aapcs_reg = gen_rtx_REG ( mode , ncrn ) ; pcum -> aapcs_next_ncrn = NUM_ARG_REGS ; pcum -> aapcs_partial = ( NUM_ARG_REGS - ncrn ) * UNITS_PER_WORD ; return ; } pcum -> aapcs_next_ncrn = NUM_ARG_REGS ; return ; } </s>

Lay out a function argument using the AAPCS rules . The rule numbers referred to here are those in the AAPCS .


<s> static bool aapcs_vfp_allocate ( CUMULATIVE_ARGS * pcum , machine_mode mode , const_tree type ATTRIBUTE_UNUSED ) { int shift = GET_MODE_SIZE ( pcum -> aapcs_vfp_rmode ) / GET_MODE_SIZE ( SFmode ) ; unsigned mask = ( 1 << ( shift * pcum -> aapcs_vfp_rcount ) ) - 1 ; int regno ; for ( regno = 0 ; regno < NUM_VFP_ARG_REGS ; regno += shift ) if ( ( ( pcum -> aapcs_vfp_regs_free >> regno ) & mask ) == mask ) { pcum -> aapcs_vfp_reg_alloc = mask << regno ; if ( mode == BLKmode || ( mode == TImode && ! TARGET_NEON ) || ! arm_hard_regno_mode_ok ( FIRST_VFP_REGNUM + regno , mode ) ) { int i ; int rcount = pcum -> aapcs_vfp_rcount ; int rshift = shift ; machine_mode rmode = pcum -> aapcs_vfp_rmode ; rtx par ; if ( ! TARGET_NEON ) { if ( rmode == V2SImode ) rmode = DImode ; else if ( rmode == V4SImode ) { rmode = DImode ; rcount *= 2 ; rshift /= 2 ; } } par = gen_rtx_PARALLEL ( mode , rtvec_alloc ( rcount ) ) ; for ( i = 0 ; i < rcount ; i ++ ) { rtx tmp = gen_rtx_REG ( rmode , FIRST_VFP_REGNUM + regno + i * rshift ) ; tmp = gen_rtx_EXPR_LIST ( VOIDmode , tmp , GEN_INT ( i * GET_MODE_SIZE ( rmode ) ) ) ; XVECEXP ( par , 0 , i ) = tmp ; } pcum -> aapcs_reg = par ; } else pcum -> aapcs_reg = gen_rtx_REG ( mode , FIRST_VFP_REGNUM + regno ) ; return true ; } return false ; } </s>

Implement the allocate field in aapcs_cp_arg_layout . See the comment there for the behaviour of this function .


<s> static rtx aapcs_vfp_allocate_return_reg ( enum arm_pcs pcs_variant ATTRIBUTE_UNUSED , machine_mode mode , const_tree type ATTRIBUTE_UNUSED ) { if ( ! use_vfp_abi ( pcs_variant , false ) ) return NULL ; if ( mode == BLKmode || ( mode == TImode && ! TARGET_NEON ) ) { int count ; machine_mode ag_mode ; int i ; rtx par ; int shift ; aapcs_vfp_is_call_or_return_candidate ( pcs_variant , mode , type , & ag_mode , & count ) ; if ( ! TARGET_NEON ) { if ( ag_mode == V2SImode ) ag_mode = DImode ; else if ( ag_mode == V4SImode ) { ag_mode = DImode ; count *= 2 ; } } shift = GET_MODE_SIZE ( ag_mode ) / GET_MODE_SIZE ( SFmode ) ; par = gen_rtx_PARALLEL ( mode , rtvec_alloc ( count ) ) ; for ( i = 0 ; i < count ; i ++ ) { rtx tmp = gen_rtx_REG ( ag_mode , FIRST_VFP_REGNUM + i * shift ) ; tmp = gen_rtx_EXPR_LIST ( VOIDmode , tmp , GEN_INT ( i * GET_MODE_SIZE ( ag_mode ) ) ) ; XVECEXP ( par , 0 , i ) = tmp ; } return par ; } return gen_rtx_REG ( mode , FIRST_VFP_REGNUM ) ; } </s>

Implement the allocate_return_reg field in aapcs_cp_arg_layout . See the comment there for the behaviour of this function .


<s> static bool aapcs_vfp_is_call_or_return_candidate ( enum arm_pcs pcs_variant , machine_mode mode , const_tree type , machine_mode * base_mode , int * count ) { machine_mode new_mode = VOIDmode ; if ( type ) { int ag_count = aapcs_vfp_sub_candidate ( type , & new_mode ) ; if ( ag_count > 0 && ag_count <= 4 ) * count = ag_count ; else return false ; } else if ( GET_MODE_CLASS ( mode ) == MODE_FLOAT || GET_MODE_CLASS ( mode ) == MODE_VECTOR_INT || GET_MODE_CLASS ( mode ) == MODE_VECTOR_FLOAT ) { * count = 1 ; new_mode = mode ; } else if ( GET_MODE_CLASS ( mode ) == MODE_COMPLEX_FLOAT ) { * count = 2 ; new_mode = ( mode == DCmode ? DFmode : SFmode ) ; } else return false ; if ( ! use_vfp_abi ( pcs_variant , ARM_NUM_REGS ( new_mode ) > 1 ) ) return false ; * base_mode = new_mode ; return true ; } </s>

Return true if an argument whose type is TYPE , or mode is MODE , is suitable for passing or returning in VFP registers for the PCS variant selected . If it is , then * BASE_MODE is updated to contain a machine mode describing each element of the argument 's type and * COUNT to hold the number of such elements .


<s> static bool aarch_macro_fusion_pair_p ( rtx_insn * prev , rtx_insn * curr ) { rtx set_dest ; rtx prev_set = single_set ( prev ) ; rtx curr_set = single_set ( curr ) ; if ( ! prev_set || ! curr_set ) return false ; if ( any_condjump_p ( curr ) ) return false ; if ( ! arm_macro_fusion_p ( ) ) return false ; if ( current_tune -> fuseable_ops & ARM_FUSE_MOVW_MOVT ) { set_dest = SET_DEST ( curr_set ) ; if ( GET_MODE ( set_dest ) != SImode ) return false ; if ( GET_CODE ( set_dest ) == ZERO_EXTRACT ) { if ( CONST_INT_P ( SET_SRC ( curr_set ) ) && CONST_INT_P ( SET_SRC ( prev_set ) ) && REG_P ( XEXP ( set_dest , 0 ) ) && REG_P ( SET_DEST ( prev_set ) ) && REGNO ( XEXP ( set_dest , 0 ) ) == REGNO ( SET_DEST ( prev_set ) ) ) return true ; } else if ( GET_CODE ( SET_SRC ( curr_set ) ) == LO_SUM && REG_P ( SET_DEST ( curr_set ) ) && REG_P ( SET_DEST ( prev_set ) ) && GET_CODE ( SET_SRC ( prev_set ) ) == HIGH && REGNO ( SET_DEST ( curr_set ) ) == REGNO ( SET_DEST ( prev_set ) ) ) return true ; } return false ; } </s>

Implement TARGET_SCHED_MACRO_FUSION_PAIR_P . Return true if PREV and CURR should be kept together during scheduling .


<s> static bool any_sibcall_could_use_r3 ( void ) { edge_iterator ei ; edge e ; if ( ! crtl -> tail_call_emit ) return false ; FOR_EACH_EDGE ( e , ei , EXIT_BLOCK_PTR_FOR_FN ( cfun ) -> preds ) if ( e -> flags & EDGE_SIBCALL ) { rtx call = BB_END ( e -> src ) ; if ( ! CALL_P ( call ) ) call = prev_nonnote_nondebug_insn ( call ) ; gcc_assert ( CALL_P ( call ) && SIBLING_CALL_P ( call ) ) ; if ( find_regno_fusage ( call , USE , 3 ) || is_indirect_tailcall_p ( call ) ) return true ; } return false ; } </s>

Return true if r3 is used by any of the tail call insns in the current function .


<s> static bool arm_9e_rtx_costs ( rtx x , enum rtx_code code , enum rtx_code outer_code , int * total , bool speed ) { machine_mode mode = GET_MODE ( x ) ; if ( TARGET_THUMB1 ) { switch ( code ) { case MULT : if ( arm_arch6m && arm_m_profile_small_mul ) * total = COSTS_N_INSNS ( 32 ) ; else * total = COSTS_N_INSNS ( 3 ) ; return true ; default : * total = thumb1_rtx_costs ( x , code , outer_code ) ; return true ; } } switch ( code ) { case MULT : if ( mode == DImode && ( GET_CODE ( XEXP ( x , 0 ) ) == GET_CODE ( XEXP ( x , 1 ) ) ) && ( GET_CODE ( XEXP ( x , 0 ) ) == ZERO_EXTEND || GET_CODE ( XEXP ( x , 0 ) ) == SIGN_EXTEND ) ) { * total = COSTS_N_INSNS ( 2 ) ; return false ; } if ( mode == DImode ) { * total = COSTS_N_INSNS ( 5 ) ; return false ; } if ( mode == SImode ) { * total = COSTS_N_INSNS ( 2 ) ; return false ; } if ( GET_MODE_CLASS ( mode ) == MODE_FLOAT ) { if ( TARGET_HARD_FLOAT && ( mode == SFmode || ( mode == DFmode && ! TARGET_VFP_SINGLE ) ) ) { * total = COSTS_N_INSNS ( 1 ) ; return false ; } } * total = COSTS_N_INSNS ( 20 ) ; return false ; default : return arm_rtx_costs_1 ( x , outer_code , total , speed ) ; } } </s>


<s> static void arm_add_cfa_adjust_cfa_note ( rtx insn , int size , rtx dest , rtx src ) { rtx dwarf ; RTX_FRAME_RELATED_P ( insn ) = 1 ; dwarf = gen_rtx_SET ( VOIDmode , dest , plus_constant ( Pmode , src , size ) ) ; add_reg_note ( insn , REG_CFA_ADJUST_CFA , dwarf ) ; } </s>

Add a REG_CFA_ADJUST_CFA REG note to INSN . SIZE is the offset to be adjusted . DEST and SRC might be stack_pointer_rtx or hard_frame_pointer_rtx .


<s> static int arm_adjust_cost ( rtx_insn * insn , rtx link , rtx_insn * dep , int cost ) { rtx i_pat , d_pat ; if ( TARGET_THUMB1 && REG_NOTE_KIND ( link ) == 0 && recog_memoized ( insn ) == CODE_FOR_cbranchsi4_insn && recog_memoized ( dep ) >= 0 && get_attr_conds ( dep ) == CONDS_SET ) return 0 ; if ( current_tune -> sched_adjust_cost != NULL ) { if ( ! current_tune -> sched_adjust_cost ( insn , link , dep , & cost ) ) return cost ; } if ( REG_NOTE_KIND ( link ) == REG_DEP_ANTI || REG_NOTE_KIND ( link ) == REG_DEP_OUTPUT ) return 0 ; if ( REG_NOTE_KIND ( link ) == 0 && CALL_P ( insn ) ) return 1 ; if ( ( i_pat = single_set ( insn ) ) != NULL && MEM_P ( SET_SRC ( i_pat ) ) && ( d_pat = single_set ( dep ) ) != NULL && MEM_P ( SET_DEST ( d_pat ) ) ) { rtx src_mem = XEXP ( SET_SRC ( i_pat ) , 0 ) ; if ( ( GET_CODE ( src_mem ) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P ( src_mem ) ) || reg_mentioned_p ( stack_pointer_rtx , src_mem ) || reg_mentioned_p ( frame_pointer_rtx , src_mem ) || reg_mentioned_p ( hard_frame_pointer_rtx , src_mem ) ) return 1 ; } return cost ; } </s>

This function implements the target macro TARGET_SCHED_ADJUST_COST . It corrects the value of COST based on the relationship between INSN and DEP through the dependence LINK . It returns the new value . There is a per-core adjust_cost hook to adjust scheduler costs and the per-core hook can choose to completely override the generic adjust_cost function . Only put bits of code into arm_adjust_cost that are common across all cores .


<s> int arm_apply_result_size ( void ) { int size = 16 ; if ( TARGET_32BIT ) { if ( TARGET_HARD_FLOAT_ABI && TARGET_VFP ) size += 32 ; if ( TARGET_IWMMXT_ABI ) size += 8 ; } return size ; } </s>

Determine the amount of memory needed to store the possible return registers of an untyped call .


<s> static bool arm_array_mode_supported_p ( machine_mode mode , unsigned HOST_WIDE_INT nelems ) { if ( TARGET_NEON && ( VALID_NEON_DREG_MODE ( mode ) || VALID_NEON_QREG_MODE ( mode ) ) && ( nelems >= 2 && nelems <= 4 ) ) return true ; return false ; } </s>

Implements target hook array_mode_supported_p .


<s> static unsigned HOST_WIDE_INT arm_asan_shadow_offset ( void ) { return ( unsigned HOST_WIDE_INT ) 1 << 29 ; } </s>

Implement the TARGET_ASAN_SHADOW_OFFSET hook .


<s> static void arm_asm_init_sections ( void ) { exception_section = get_unnamed_section ( 0 , output_section_asm_op , "\t.handlerdata" ) ; } </s>


<s>
static
void
arm_asm_trampoline_template
(
FILE
*
f
)
{
if
(
TARGET_ARM
)
{
asm_fprintf
(
f
,
"\tldr\t%r, [%r, #0]\n"
,
STATIC_CHAIN_REGNUM
,
PC_REGNUM
)
;
asm_fprintf
(
f
,
"\tldr\t%r, [%r, #0]\n"
,
PC_REGNUM
,
PC_REGNUM
)
;
}
else
if
(
TARGET_THUMB2
)
{
asm_fprintf
(
f
,
"\tldr.w\t%r, [%r, #4]\n"
,
STATIC_CHAIN_REGNUM
,
PC_REGNUM
)
;
asm_fprintf
(
f
,
"\tldr.w\t%r, [%r, #4]\n"
,
PC_REGNUM
,
PC_REGNUM
)
;
}
else
{
ASM_OUTPUT_ALIGN
(
f
,
2
)
;
fprintf
(
f
,
"\t.code\t16\n"
)
;
fprintf
(
f
,
".Ltrampoline_start:\n"
)
;
asm_fprintf
(
f
,
"\tpush\t{r0, r1}\n"
)
;
asm_fprintf
(
f
,
"\tldr\tr0, [%r, #8]\n"
,
PC_REGNUM
)
;
asm_fprintf
(
f
,
"\tmov\t%r, r0\n"
,
STATIC_CHAIN_REGNUM
)
;
asm_fprintf
(
f
,
"\tldr\tr0, [%r, #8]\n"
,
PC_REGNUM
)
;
asm_fprintf
(
f
,
"\tstr\tr0, [%r, #4]\n"
,
SP_REGNUM
)
;
asm_fprintf
(
f
,
"\tpop\t{r0, %r}\n"
,
PC_REGNUM
)
;
}
assemble_aligned_integer
(
UNITS_PER_WORD
,
const0_rtx
)
;
assemble_aligned_integer
(
UNITS_PER_WORD
,
const0_rtx
)
;
}
</s>

Output assembler code for a block containing the constant parts of a trampoline , leaving space for the variable parts . On the ARM , ( if r8 is the static chain regnum , and remembering that referencing pc adds an offset of 8 ) the trampoline looks like : ldr r8 , [ pc , # 0 ] ldr pc , [ pc ] .word static chain value .word function 's address XXX FIXME : When the trampoline returns , r8 will be clobbered .


<s> static bool arm_assemble_integer ( rtx x , unsigned int size , int aligned_p ) { machine_mode mode ; if ( size == UNITS_PER_WORD && aligned_p ) { fputs ( "\t.word\t" , asm_out_file ) ; output_addr_const ( asm_out_file , x ) ; if ( NEED_GOT_RELOC && flag_pic && making_const_table && ( GET_CODE ( x ) == SYMBOL_REF || GET_CODE ( x ) == LABEL_REF ) ) { if ( ! arm_pic_data_is_text_relative || ( GET_CODE ( x ) == SYMBOL_REF && ! SYMBOL_REF_LOCAL_P ( x ) ) ) fputs ( "(GOT)" , asm_out_file ) ; else fputs ( "(GOTOFF)" , asm_out_file ) ; } fputc ( '\n' , asm_out_file ) ; return true ; } mode = GET_MODE ( x ) ; if ( arm_vector_mode_supported_p ( mode ) ) { int i , units ; gcc_assert ( GET_CODE ( x ) == CONST_VECTOR ) ; units = CONST_VECTOR_NUNITS ( x ) ; size = GET_MODE_SIZE ( GET_MODE_INNER ( mode ) ) ; if ( GET_MODE_CLASS ( mode ) == MODE_VECTOR_INT ) for ( i = 0 ; i < units ; i ++ ) { rtx elt = CONST_VECTOR_ELT ( x , i ) ; assemble_integer ( elt , size , i == 0 ? BIGGEST_ALIGNMENT : size * BITS_PER_UNIT , 1 ) ; } else for ( i = 0 ; i < units ; i ++ ) { rtx elt = CONST_VECTOR_ELT ( x , i ) ; REAL_VALUE_TYPE rval ; REAL_VALUE_FROM_CONST_DOUBLE ( rval , elt ) ; assemble_real ( rval , GET_MODE_INNER ( mode ) , i == 0 ? BIGGEST_ALIGNMENT : size * BITS_PER_UNIT ) ; } return true ; } return default_assemble_integer ( x , size , aligned_p ) ; } </s>

Target hook for assembling integer objects . The ARM version needs to handle word-sized values specially .


<s> int arm_attr_length_move_neon ( rtx_insn * insn ) { rtx reg , mem , addr ; int load ; machine_mode mode ; extract_insn_cached ( insn ) ; if ( REG_P ( recog_data . operand [ 0 ] ) && REG_P ( recog_data . operand [ 1 ] ) ) { mode = GET_MODE ( recog_data . operand [ 0 ] ) ; switch ( mode ) { case EImode : case OImode : return 8 ; case CImode : return 12 ; case XImode : return 16 ; default : gcc_unreachable ( ) ; } } load = REG_P ( recog_data . operand [ 0 ] ) ; reg = recog_data . operand [ ! load ] ; mem = recog_data . operand [ load ] ; gcc_assert ( MEM_P ( mem ) ) ; mode = GET_MODE ( reg ) ; addr = XEXP ( mem , 0 ) ; if ( GET_CODE ( addr ) == CONST && GET_CODE ( XEXP ( addr , 0 ) ) == PLUS ) addr = XEXP ( addr , 0 ) ; if ( GET_CODE ( addr ) == LABEL_REF || GET_CODE ( addr ) == PLUS ) { int insns = HARD_REGNO_NREGS ( REGNO ( reg ) , mode ) / 2 ; return insns * 4 ; } else return 4 ; } </s>

Compute and return the length of neon_mov < mode > , where < mode > is one of VSTRUCT modes : EI , OI , CI or XI .


<s> static int arm_barrier_cost ( rtx insn ) { int base_cost = 50 ; rtx next = next_nonnote_insn ( insn ) ; if ( next != NULL && LABEL_P ( next ) ) base_cost -= 20 ; switch ( GET_CODE ( insn ) ) { case CODE_LABEL : return 50 ; case INSN : case CALL_INSN : return base_cost ; case JUMP_INSN : return base_cost - 10 ; default : return base_cost + 10 ; } } </s>

Return the cost of forcibly inserting a barrier after INSN .


<s> static bool arm_block_set_unaligned_vect ( rtx dstbase , unsigned HOST_WIDE_INT length , unsigned HOST_WIDE_INT value , unsigned HOST_WIDE_INT align ) { unsigned int i , j , nelt_v16 , nelt_v8 , nelt_mode ; rtx dst , mem ; rtx val_elt , val_vec , reg ; rtx rval [ MAX_VECT_LEN ] ; rtx ( * gen_func ) ( rtx , rtx ) ; machine_mode mode ; unsigned HOST_WIDE_INT v = value ; gcc_assert ( ( align & 0x3 ) != 0 ) ; nelt_v8 = GET_MODE_NUNITS ( V8QImode ) ; nelt_v16 = GET_MODE_NUNITS ( V16QImode ) ; if ( length >= nelt_v16 ) { mode = V16QImode ; gen_func = gen_movmisalignv16qi ; } else { mode = V8QImode ; gen_func = gen_movmisalignv8qi ; } nelt_mode = GET_MODE_NUNITS ( mode ) ; gcc_assert ( length >= nelt_mode ) ; if ( ! arm_block_set_vect_profit_p ( length , align , mode ) ) return false ; dst = copy_addr_to_reg ( XEXP ( dstbase , 0 ) ) ; mem = adjust_automodify_address ( dstbase , mode , dst , 0 ) ; v = sext_hwi ( v , BITS_PER_WORD ) ; val_elt = GEN_INT ( v ) ; for ( j = 0 ; j < nelt_mode ; j ++ ) rval [ j ] = val_elt ; reg = gen_reg_rtx ( mode ) ; val_vec = gen_rtx_CONST_VECTOR ( mode , gen_rtvec_v ( nelt_mode , rval ) ) ; emit_move_insn ( reg , val_vec ) ; for ( i = 0 ; ( i + nelt_mode <= length ) ; i += nelt_mode ) { emit_insn ( ( * gen_func ) ( mem , reg ) ) ; if ( i + 2 * nelt_mode <= length ) emit_insn ( gen_add2_insn ( dst , GEN_INT ( nelt_mode ) ) ) ; } gcc_assert ( ( i + nelt_v8 ) > length || mode == V16QImode ) ; if ( i + nelt_v8 < length ) { emit_insn ( gen_add2_insn ( dst , GEN_INT ( length - i ) ) ) ; if ( ( length & 1 ) != 0 && align >= 2 ) set_mem_align ( mem , BITS_PER_UNIT ) ; emit_insn ( gen_movmisalignv16qi ( mem , reg ) ) ; } else if ( i < length && i + nelt_v8 >= length ) { if ( mode == V16QImode ) { reg = gen_lowpart ( V8QImode , reg ) ; mem = adjust_automodify_address ( dstbase , V8QImode , dst , 0 ) ; } emit_insn ( gen_add2_insn ( dst , GEN_INT ( ( length - i ) + ( nelt_mode - nelt_v8 ) ) ) ) ; if ( ( length & 1 ) != 0 && align >= 2 ) set_mem_align ( mem , BITS_PER_UNIT ) ; emit_insn ( gen_movmisalignv8qi ( mem , reg ) ) ; } return true ; } </s>

Set a block of memory using vectorization instructions for the unaligned case . We fill the first LENGTH bytes of the memory area starting from DSTBASE with byte constant VALUE . ALIGN is the alignment requirement of memory . Return TRUE if succeeded .


<s> static bool arm_cannot_copy_insn_p ( rtx_insn * insn ) { if ( recog_memoized ( insn ) == CODE_FOR_tlscall ) return true ; subrtx_iterator :: array_type array ; FOR_EACH_SUBRTX ( iter , array , PATTERN ( insn ) , ALL ) { const_rtx x = * iter ; if ( GET_CODE ( x ) == UNSPEC && ( XINT ( x , 1 ) == UNSPEC_PIC_BASE || XINT ( x , 1 ) == UNSPEC_PIC_UNIFIED ) ) return true ; } return false ; } </s>

Must not copy any rtx that uses a pc-relative address .


<s> static bool arm_cannot_force_const_mem ( machine_mode mode ATTRIBUTE_UNUSED , rtx x ) { rtx base , offset ; if ( ARM_OFFSETS_MUST_BE_WITHIN_SECTIONS_P ) { split_const ( x , & base , & offset ) ; if ( GET_CODE ( base ) == SYMBOL_REF && ! offset_within_block_p ( base , INTVAL ( offset ) ) ) return true ; } return arm_tls_referenced_p ( x ) ; } </s>


<s> static unsigned long arm_compute_func_type ( void ) { unsigned long type = ARM_FT_UNKNOWN ; tree a ; tree attr ; gcc_assert ( TREE_CODE ( current_function_decl ) == FUNCTION_DECL ) ; if ( optimize > 0 && ( TREE_NOTHROW ( current_function_decl ) || ! ( flag_unwind_tables || ( flag_exceptions && arm_except_unwind_info ( & global_options ) != UI_SJLJ ) ) ) && TREE_THIS_VOLATILE ( current_function_decl ) ) type |= ARM_FT_VOLATILE ; if ( cfun -> static_chain_decl != NULL ) type |= ARM_FT_NESTED ; attr = DECL_ATTRIBUTES ( current_function_decl ) ; a = lookup_attribute ( "naked" , attr ) ; if ( a != NULL_TREE ) type |= ARM_FT_NAKED ; a = lookup_attribute ( "isr" , attr ) ; if ( a == NULL_TREE ) a = lookup_attribute ( "interrupt" , attr ) ; if ( a == NULL_TREE ) type |= TARGET_INTERWORK ? ARM_FT_INTERWORKED : ARM_FT_NORMAL ; else type |= arm_isr_value ( TREE_VALUE ( a ) ) ; return type ; } </s>

Computes the type of the current function .


<s> static unsigned long arm_compute_save_reg0_reg12_mask ( void ) { unsigned long func_type = arm_current_func_type ( ) ; unsigned long save_reg_mask = 0 ; unsigned int reg ; if ( IS_INTERRUPT ( func_type ) ) { unsigned int max_reg ; if ( ARM_FUNC_TYPE ( func_type ) == ARM_FT_FIQ ) max_reg = 7 ; else max_reg = 12 ; for ( reg = 0 ; reg <= max_reg ; reg ++ ) if ( df_regs_ever_live_p ( reg ) || ( ! crtl -> is_leaf && call_used_regs [ reg ] ) ) save_reg_mask |= ( 1 << reg ) ; if ( flag_pic && ! TARGET_SINGLE_PIC_BASE && arm_pic_register != INVALID_REGNUM && crtl -> uses_pic_offset_table ) save_reg_mask |= 1 << PIC_OFFSET_TABLE_REGNUM ; } else if ( IS_VOLATILE ( func_type ) ) { if ( frame_pointer_needed ) save_reg_mask |= 1 << HARD_FRAME_POINTER_REGNUM ; if ( df_regs_ever_live_p ( ARM_HARD_FRAME_POINTER_REGNUM ) ) save_reg_mask |= 1 << ARM_HARD_FRAME_POINTER_REGNUM ; if ( df_regs_ever_live_p ( THUMB_HARD_FRAME_POINTER_REGNUM ) ) save_reg_mask |= 1 << THUMB_HARD_FRAME_POINTER_REGNUM ; } else { for ( reg = 0 ; reg <= 11 ; reg ++ ) if ( df_regs_ever_live_p ( reg ) && callee_saved_reg_p ( reg ) ) save_reg_mask |= ( 1 << reg ) ; if ( frame_pointer_needed ) save_reg_mask |= 1 << HARD_FRAME_POINTER_REGNUM ; if ( flag_pic && ! TARGET_SINGLE_PIC_BASE && arm_pic_register != INVALID_REGNUM && ( df_regs_ever_live_p ( PIC_OFFSET_TABLE_REGNUM ) || crtl -> uses_pic_offset_table ) ) save_reg_mask |= 1 << PIC_OFFSET_TABLE_REGNUM ; if ( IS_STACKALIGN ( func_type ) ) save_reg_mask |= 1 ; } if ( crtl -> calls_eh_return ) { unsigned int i ; for ( i = 0 ; ; i ++ ) { reg = EH_RETURN_DATA_REGNO ( i ) ; if ( reg == INVALID_REGNUM ) break ; save_reg_mask |= 1 << reg ; } } return save_reg_mask ; } </s>

Compute the register save mask for registers 0 through 12 inclusive . This code is used by arm_compute_save_reg_mask .


<s> static unsigned long arm_compute_save_reg_mask ( void ) { unsigned int save_reg_mask = 0 ; unsigned long func_type = arm_current_func_type ( ) ; unsigned int reg ; if ( IS_NAKED ( func_type ) ) return 0 ; if ( TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM ) save_reg_mask |= ( 1 << ARM_HARD_FRAME_POINTER_REGNUM ) | ( 1 << IP_REGNUM ) | ( 1 << LR_REGNUM ) | ( 1 << PC_REGNUM ) ; save_reg_mask |= arm_compute_save_reg0_reg12_mask ( ) ; if ( df_regs_ever_live_p ( LR_REGNUM ) || ( save_reg_mask && optimize_size && ARM_FUNC_TYPE ( func_type ) == ARM_FT_NORMAL && ! crtl -> tail_call_emit && ! crtl -> calls_eh_return ) ) save_reg_mask |= 1 << LR_REGNUM ; if ( cfun -> machine -> lr_save_eliminated ) save_reg_mask &= ~ ( 1 << LR_REGNUM ) ; if ( TARGET_REALLY_IWMMXT && ( ( bit_count ( save_reg_mask ) + ARM_NUM_INTS ( crtl -> args . pretend_args_size + arm_compute_static_chain_stack_bytes ( ) ) ) % 2 ) != 0 ) { for ( reg = 4 ; reg <= 12 ; reg ++ ) if ( ( save_reg_mask & ( 1 << reg ) ) == 0 ) break ; if ( reg <= 12 ) save_reg_mask |= ( 1 << reg ) ; else { cfun -> machine -> sibcall_blocked = 1 ; save_reg_mask |= ( 1 << 3 ) ; } } if ( TARGET_THUMB2 && IS_NESTED ( func_type ) && flag_pic && ( save_reg_mask & THUMB2_WORK_REGS ) == 0 ) { reg = thumb_find_work_register ( 1 << 4 ) ; if ( ! call_used_regs [ reg ] ) save_reg_mask |= ( 1 << reg ) ; } return save_reg_mask ; } </s>

Compute a bit mask of which registers need to be saved on the stack for the current function . This is used by arm_get_frame_offsets , which may add extra registers .


<s> static int arm_compute_static_chain_stack_bytes ( void ) { if ( TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM && IS_NESTED ( arm_current_func_type ( ) ) && arm_r3_live_at_start_p ( ) && crtl -> args . pretend_args_size == 0 ) return 4 ; return 0 ; } </s>

Compute the number of bytes used to store the static chain register on the stack , above the stack frame . We need to know this accurately to get the alignment of the rest of the stack frame correct .


<s> static int arm_comp_type_attributes ( const_tree type1 , const_tree type2 ) { int l1 , l2 , s1 , s2 ; if ( TREE_CODE ( type1 ) != FUNCTION_TYPE ) return 1 ; l1 = lookup_attribute ( "long_call" , TYPE_ATTRIBUTES ( type1 ) ) != NULL ; l2 = lookup_attribute ( "long_call" , TYPE_ATTRIBUTES ( type2 ) ) != NULL ; s1 = lookup_attribute ( "short_call" , TYPE_ATTRIBUTES ( type1 ) ) != NULL ; s2 = lookup_attribute ( "short_call" , TYPE_ATTRIBUTES ( type2 ) ) != NULL ; if ( l1 | l2 | s1 | s2 ) { if ( ( l1 != l2 ) || ( s1 != s2 ) ) return 0 ; if ( ( l1 & s2 ) || ( l2 & s1 ) ) return 0 ; } l1 = lookup_attribute ( "isr" , TYPE_ATTRIBUTES ( type1 ) ) != NULL ; if ( ! l1 ) l1 = lookup_attribute ( "interrupt" , TYPE_ATTRIBUTES ( type1 ) ) != NULL ; l2 = lookup_attribute ( "isr" , TYPE_ATTRIBUTES ( type2 ) ) != NULL ; if ( ! l2 ) l1 = lookup_attribute ( "interrupt" , TYPE_ATTRIBUTES ( type2 ) ) != NULL ; if ( l1 != l2 ) return 0 ; return 1 ; } </s>

Return 0 if the attributes for two types are incompatible , 1 if they are compatible , and 2 if they are nearly compatible ( which causes a warning to be generated ) .


<s> int arm_const_double_rtx ( rtx x ) { REAL_VALUE_TYPE r ; if ( ! fp_consts_inited ) init_fp_table ( ) ; REAL_VALUE_FROM_CONST_DOUBLE ( r , x ) ; if ( REAL_VALUE_MINUS_ZERO ( r ) ) return 0 ; if ( REAL_VALUES_EQUAL ( r , value_fp0 ) ) return 1 ; return 0 ; } </s>

Return TRUE if rtx X is a valid immediate FP constant .


<s> static bool arm_const_not_ok_for_debug_p ( rtx p ) { tree decl_op0 = NULL ; tree decl_op1 = NULL ; if ( GET_CODE ( p ) == MINUS ) { if ( GET_CODE ( XEXP ( p , 1 ) ) == SYMBOL_REF ) { decl_op1 = SYMBOL_REF_DECL ( XEXP ( p , 1 ) ) ; if ( decl_op1 && GET_CODE ( XEXP ( p , 0 ) ) == SYMBOL_REF && ( decl_op0 = SYMBOL_REF_DECL ( XEXP ( p , 0 ) ) ) ) { if ( ( TREE_CODE ( decl_op1 ) == VAR_DECL || TREE_CODE ( decl_op1 ) == CONST_DECL ) && ( TREE_CODE ( decl_op0 ) == VAR_DECL || TREE_CODE ( decl_op0 ) == CONST_DECL ) ) return ( get_variable_section ( decl_op1 , false ) != get_variable_section ( decl_op0 , false ) ) ; if ( TREE_CODE ( decl_op1 ) == LABEL_DECL && TREE_CODE ( decl_op0 ) == LABEL_DECL ) return ( DECL_CONTEXT ( decl_op1 ) != DECL_CONTEXT ( decl_op0 ) ) ; } return true ; } } return false ; } </s>

This is a temporary fix for PR60655 . Ideally we need to handle most of these cases in the generic part but currently we reject minus ( .. ) ( sym_ref ) . We try to ameliorate the case with minus ( sym_ref1 ) ( sym_ref2 ) where they are in the same section .


<s> static tree arm_convert_to_type ( tree type , tree expr ) { tree fromtype = TREE_TYPE ( expr ) ; if ( ! SCALAR_FLOAT_TYPE_P ( fromtype ) || ! SCALAR_FLOAT_TYPE_P ( type ) ) return NULL_TREE ; if ( ( TYPE_PRECISION ( fromtype ) == 16 && TYPE_PRECISION ( type ) > 32 ) || ( TYPE_PRECISION ( type ) == 16 && TYPE_PRECISION ( fromtype ) > 32 ) ) return convert ( type , convert ( float_type_node , expr ) ) ; return NULL_TREE ; } </s>

Implement TARGET_CONVERT_TO_TYPE . Specifically , this hook implements the peculiarity of the ARM half-precision floating-point C semantics that requires conversions between __fp16 to or from double to do an intermediate conversion to float .


<s> unsigned int arm_dbx_register_number ( unsigned int regno ) { if ( regno < 16 ) return regno ; if ( IS_VFP_REGNUM ( regno ) ) { if ( VFP_REGNO_OK_FOR_SINGLE ( regno ) ) return 64 + regno - FIRST_VFP_REGNUM ; else return 256 + ( regno - FIRST_VFP_REGNUM ) / 2 ; } if ( IS_IWMMXT_GR_REGNUM ( regno ) ) return 104 + regno - FIRST_IWMMXT_GR_REGNUM ; if ( IS_IWMMXT_REGNUM ( regno ) ) return 112 + regno - FIRST_IWMMXT_REGNUM ; gcc_unreachable ( ) ; } </s>

Map internal gcc register numbers to DWARF2 register numbers .


<s> void arm_emit_call_insn ( rtx pat , rtx addr , bool sibcall ) { rtx insn ; insn = emit_call_insn ( pat ) ; if ( TARGET_VXWORKS_RTP && flag_pic && ! sibcall && GET_CODE ( addr ) == SYMBOL_REF && ( SYMBOL_REF_DECL ( addr ) ? ! targetm . binds_local_p ( SYMBOL_REF_DECL ( addr ) ) : ! SYMBOL_REF_LOCAL_P ( addr ) ) ) { require_pic_register ( ) ; use_reg ( & CALL_INSN_FUNCTION_USAGE ( insn ) , cfun -> machine -> pic_reg ) ; } if ( TARGET_AAPCS_BASED ) { rtx * fusage = & CALL_INSN_FUNCTION_USAGE ( insn ) ; clobber_reg ( fusage , gen_rtx_REG ( word_mode , IP_REGNUM ) ) ; } } </s>

Emit a call instruction with pattern PAT . ADDR is the address of the call target .


<s> void arm_emit_fp16_const ( rtx c ) { REAL_VALUE_TYPE r ; long bits ; REAL_VALUE_FROM_CONST_DOUBLE ( r , c ) ; bits = real_to_target ( NULL , & r , HFmode ) ; if ( WORDS_BIG_ENDIAN ) assemble_zeros ( 2 ) ; assemble_integer ( GEN_INT ( bits ) , 2 , BITS_PER_WORD , 1 ) ; if ( ! WORDS_BIG_ENDIAN ) assemble_zeros ( 2 ) ; } </s>

Emit a fp16 constant appropriately padded to occupy a 4-byte word . HFmode constant pool entries are actually loaded with ldr .


<s> static void arm_emit_ldrd_pop ( unsigned long saved_regs_mask ) { int j = 0 ; int offset = 0 ; rtx par = NULL_RTX ; rtx dwarf = NULL_RTX ; rtx tmp , mem ; gcc_assert ( ! ( ( saved_regs_mask & ( 1 << SP_REGNUM ) ) ) ) ; j = 0 ; while ( j <= LAST_ARM_REGNUM ) if ( saved_regs_mask & ( 1 << j ) ) { if ( ( j % 2 ) == 0 && ( saved_regs_mask & ( 1 << ( j + 1 ) ) ) && ( j + 1 ) != PC_REGNUM ) { if ( offset > 0 ) mem = gen_frame_mem ( DImode , plus_constant ( Pmode , stack_pointer_rtx , offset ) ) ; else mem = gen_frame_mem ( DImode , stack_pointer_rtx ) ; tmp = gen_rtx_SET ( DImode , gen_rtx_REG ( DImode , j ) , mem ) ; tmp = emit_insn ( tmp ) ; RTX_FRAME_RELATED_P ( tmp ) = 1 ; dwarf = alloc_reg_note ( REG_CFA_RESTORE , gen_rtx_REG ( SImode , j ) , NULL_RTX ) ; dwarf = alloc_reg_note ( REG_CFA_RESTORE , gen_rtx_REG ( SImode , j + 1 ) , dwarf ) ; REG_NOTES ( tmp ) = dwarf ; offset += 8 ; j += 2 ; } else if ( j != PC_REGNUM ) { if ( offset > 0 ) mem = gen_frame_mem ( SImode , plus_constant ( Pmode , stack_pointer_rtx , offset ) ) ; else mem = gen_frame_mem ( SImode , stack_pointer_rtx ) ; tmp = gen_rtx_SET ( SImode , gen_rtx_REG ( SImode , j ) , mem ) ; tmp = emit_insn ( tmp ) ; RTX_FRAME_RELATED_P ( tmp ) = 1 ; REG_NOTES ( tmp ) = alloc_reg_note ( REG_CFA_RESTORE , gen_rtx_REG ( SImode , j ) , NULL_RTX ) ; offset += 4 ; j += 1 ; } else j ++ ; } else j ++ ; if ( offset > 0 ) { tmp = gen_rtx_SET ( Pmode , stack_pointer_rtx , plus_constant ( Pmode , stack_pointer_rtx , offset ) ) ; tmp = emit_insn ( tmp ) ; arm_add_cfa_adjust_cfa_note ( tmp , offset , stack_pointer_rtx , stack_pointer_rtx ) ; offset = 0 ; } if ( saved_regs_mask & ( 1 << PC_REGNUM ) ) { par = gen_rtx_PARALLEL ( VOIDmode , rtvec_alloc ( 2 ) ) ; XVECEXP ( par , 0 , 0 ) = ret_rtx ; tmp = gen_rtx_SET ( SImode , gen_rtx_REG ( SImode , PC_REGNUM ) , gen_frame_mem ( SImode , gen_rtx_POST_INC ( SImode , stack_pointer_rtx ) ) ) ; RTX_FRAME_RELATED_P ( tmp ) = 1 ; XVECEXP ( par , 0 , 1 ) = tmp ; par = emit_jump_insn ( par ) ; dwarf = alloc_reg_note ( REG_CFA_RESTORE , gen_rtx_REG ( SImode , PC_REGNUM ) , NULL_RTX ) ; REG_NOTES ( par ) = dwarf ; arm_add_cfa_adjust_cfa_note ( par , UNITS_PER_WORD , stack_pointer_rtx , stack_pointer_rtx ) ; } } </s>

LDRD in ARM mode needs consecutive registers as operands . This function emits LDRD whenever possible , otherwise it emits single-word loads . It uses offset addressing and then generates one separate stack udpate . This provides more scheduling freedom , compared to writeback on every load . However , if the function returns using load into PC directly ( i.e. , if PC is in SAVED_REGS_MASK ) , the stack needs to be updated before the last load . TODO : Add a peephole optimization to recognize the new epilogue sequence as an LDM instruction whenever possible . TODO : Add peephole optimization to merge the load at stack-offset zero with the stack update instruction using load with writeback in post-index addressing mode .


<s> static void arm_emit_load_exclusive ( machine_mode mode , rtx rval , rtx mem , bool acq ) { rtx ( * gen ) ( rtx , rtx ) ; if ( acq ) { switch ( mode ) { case QImode : gen = gen_arm_load_acquire_exclusiveqi ; break ; case HImode : gen = gen_arm_load_acquire_exclusivehi ; break ; case SImode : gen = gen_arm_load_acquire_exclusivesi ; break ; case DImode : gen = gen_arm_load_acquire_exclusivedi ; break ; default : gcc_unreachable ( ) ; } } else { switch ( mode ) { case QImode : gen = gen_arm_load_exclusiveqi ; break ; case HImode : gen = gen_arm_load_exclusivehi ; break ; case SImode : gen = gen_arm_load_exclusivesi ; break ; case DImode : gen = gen_arm_load_exclusivedi ; break ; default : gcc_unreachable ( ) ; } } emit_insn ( gen ( rval , mem ) ) ; } </s>

Emit the load-exclusive and store-exclusive instructions . Use acquire and release versions if necessary .


<s> static void arm_emit_multi_reg_pop ( unsigned long saved_regs_mask ) { int num_regs = 0 ; int i , j ; rtx par ; rtx dwarf = NULL_RTX ; rtx tmp , reg ; bool return_in_pc = saved_regs_mask & ( 1 << PC_REGNUM ) ; int offset_adj ; int emit_update ; offset_adj = return_in_pc ? 1 : 0 ; for ( i = 0 ; i <= LAST_ARM_REGNUM ; i ++ ) if ( saved_regs_mask & ( 1 << i ) ) num_regs ++ ; gcc_assert ( num_regs && num_regs <= 16 ) ; emit_update = ( saved_regs_mask & ( 1 << SP_REGNUM ) ) ? 0 : 1 ; par = gen_rtx_PARALLEL ( VOIDmode , rtvec_alloc ( num_regs + emit_update + offset_adj ) ) ; if ( return_in_pc ) XVECEXP ( par , 0 , 0 ) = ret_rtx ; if ( emit_update ) { tmp = gen_rtx_SET ( VOIDmode , stack_pointer_rtx , plus_constant ( Pmode , stack_pointer_rtx , 4 * num_regs ) ) ; RTX_FRAME_RELATED_P ( tmp ) = 1 ; XVECEXP ( par , 0 , offset_adj ) = tmp ; } for ( j = 0 , i = 0 ; j < num_regs ; i ++ ) if ( saved_regs_mask & ( 1 << i ) ) { reg = gen_rtx_REG ( SImode , i ) ; if ( ( num_regs == 1 ) && emit_update && ! return_in_pc ) { tmp = gen_frame_mem ( SImode , gen_rtx_POST_INC ( Pmode , stack_pointer_rtx ) ) ; tmp = emit_insn ( gen_rtx_SET ( VOIDmode , reg , tmp ) ) ; REG_NOTES ( tmp ) = alloc_reg_note ( REG_CFA_RESTORE , reg , dwarf ) ; return ; } tmp = gen_rtx_SET ( VOIDmode , reg , gen_frame_mem ( SImode , plus_constant ( Pmode , stack_pointer_rtx , 4 * j ) ) ) ; RTX_FRAME_RELATED_P ( tmp ) = 1 ; XVECEXP ( par , 0 , j + emit_update + offset_adj ) = tmp ; if ( i != PC_REGNUM ) dwarf = alloc_reg_note ( REG_CFA_RESTORE , reg , dwarf ) ; j ++ ; } if ( return_in_pc ) par = emit_jump_insn ( par ) ; else par = emit_insn ( par ) ; REG_NOTES ( par ) = dwarf ; if ( ! return_in_pc ) arm_add_cfa_adjust_cfa_note ( par , UNITS_PER_WORD * num_regs , stack_pointer_rtx , stack_pointer_rtx ) ; } </s>

Generate and emit an insn pattern that we will recognize as a pop_multi . SAVED_REGS_MASK shows which registers need to be restored . Unfortunately , since this insn does not reflect very well the actual semantics of the operation , we need to annotate the insn for the benefit of DWARF2 frame unwind information .


<s> static void arm_emit_vfp_multi_reg_pop ( int first_reg , int num_regs , rtx base_reg ) { int i , j ; rtx par ; rtx dwarf = NULL_RTX ; rtx tmp , reg ; gcc_assert ( num_regs && num_regs <= 32 ) ; if ( num_regs == 2 && ! arm_arch6 ) { if ( first_reg == 15 ) first_reg -- ; num_regs ++ ; } if ( num_regs > 16 ) { arm_emit_vfp_multi_reg_pop ( first_reg , 16 , base_reg ) ; arm_emit_vfp_multi_reg_pop ( first_reg + 16 , num_regs - 16 , base_reg ) ; return ; } par = gen_rtx_PARALLEL ( VOIDmode , rtvec_alloc ( num_regs + 1 ) ) ; tmp = gen_rtx_SET ( VOIDmode , base_reg , plus_constant ( Pmode , base_reg , 8 * num_regs ) ) ; RTX_FRAME_RELATED_P ( tmp ) = 1 ; XVECEXP ( par , 0 , 0 ) = tmp ; for ( j = 0 , i = first_reg ; j < num_regs ; i += 2 ) { reg = gen_rtx_REG ( DFmode , i ) ; tmp = gen_rtx_SET ( VOIDmode , reg , gen_frame_mem ( DFmode , plus_constant ( Pmode , base_reg , 8 * j ) ) ) ; RTX_FRAME_RELATED_P ( tmp ) = 1 ; XVECEXP ( par , 0 , j + 1 ) = tmp ; dwarf = alloc_reg_note ( REG_CFA_RESTORE , reg , dwarf ) ; j ++ ; } par = emit_insn ( par ) ; REG_NOTES ( par ) = dwarf ; if ( TARGET_VFP && REGNO ( base_reg ) == IP_REGNUM ) { RTX_FRAME_RELATED_P ( par ) = 1 ; add_reg_note ( par , REG_CFA_DEF_CFA , hard_frame_pointer_rtx ) ; } else arm_add_cfa_adjust_cfa_note ( par , 2 * UNITS_PER_WORD * num_regs , base_reg , base_reg ) ; } </s>

Generate and emit an insn pattern that we will recognize as a pop_multi of NUM_REGS consecutive VFP regs , starting at FIRST_REG . Unfortunately , since this insn does not reflect very well the actual semantics of the operation , we need to annotate the insn for the benefit of DWARF2 frame unwind information .


<s> static bool arm_evpc_neon_vext ( struct expand_vec_perm_d * d ) { unsigned int i , nelt = d -> nelt ; rtx ( * gen ) ( rtx , rtx , rtx , rtx ) ; rtx offset ; unsigned int location ; unsigned int next = d -> perm [ 0 ] + 1 ; if ( BYTES_BIG_ENDIAN ) return false ; for ( i = 1 ; i < nelt ; next ++ , i ++ ) { if ( next == 2 * nelt ) return false ; if ( d -> one_vector_p && ( next == nelt ) ) { if ( ( nelt == 2 ) && ( d -> vmode != V2DImode ) ) return false ; else next = 0 ; } if ( d -> perm [ i ] != next ) return false ; } location = d -> perm [ 0 ] ; switch ( d -> vmode ) { case V16QImode : gen = gen_neon_vextv16qi ; break ; case V8QImode : gen = gen_neon_vextv8qi ; break ; case V4HImode : gen = gen_neon_vextv4hi ; break ; case V8HImode : gen = gen_neon_vextv8hi ; break ; case V2SImode : gen = gen_neon_vextv2si ; break ; case V4SImode : gen = gen_neon_vextv4si ; break ; case V2SFmode : gen = gen_neon_vextv2sf ; break ; case V4SFmode : gen = gen_neon_vextv4sf ; break ; case V2DImode : gen = gen_neon_vextv2di ; break ; default : return false ; } if ( d -> testing_p ) return true ; offset = GEN_INT ( location ) ; emit_insn ( gen ( d -> target , d -> op0 , d -> op1 , offset ) ) ; return true ; } </s>


<s> static bool arm_evpc_neon_vrev ( struct expand_vec_perm_d * d ) { unsigned int i , j , diff , nelt = d -> nelt ; rtx ( * gen ) ( rtx , rtx ) ; if ( ! d -> one_vector_p ) return false ; diff = d -> perm [ 0 ] ; switch ( diff ) { case 7 : switch ( d -> vmode ) { case V16QImode : gen = gen_neon_vrev64v16qi ; break ; case V8QImode : gen = gen_neon_vrev64v8qi ; break ; default : return false ; } break ; case 3 : switch ( d -> vmode ) { case V16QImode : gen = gen_neon_vrev32v16qi ; break ; case V8QImode : gen = gen_neon_vrev32v8qi ; break ; case V8HImode : gen = gen_neon_vrev64v8hi ; break ; case V4HImode : gen = gen_neon_vrev64v4hi ; break ; default : return false ; } break ; case 1 : switch ( d -> vmode ) { case V16QImode : gen = gen_neon_vrev16v16qi ; break ; case V8QImode : gen = gen_neon_vrev16v8qi ; break ; case V8HImode : gen = gen_neon_vrev32v8hi ; break ; case V4HImode : gen = gen_neon_vrev32v4hi ; break ; case V4SImode : gen = gen_neon_vrev64v4si ; break ; case V2SImode : gen = gen_neon_vrev64v2si ; break ; case V4SFmode : gen = gen_neon_vrev64v4sf ; break ; case V2SFmode : gen = gen_neon_vrev64v2sf ; break ; default : return false ; } break ; default : return false ; } for ( i = 0 ; i < nelt ; i += diff + 1 ) for ( j = 0 ; j <= diff ; j += 1 ) { gcc_assert ( i + j < nelt ) ; if ( d -> perm [ i + j ] != i + diff - j ) return false ; } if ( d -> testing_p ) return true ; emit_insn ( gen ( d -> target , d -> op0 ) ) ; return true ; } </s>


<s> static bool arm_evpc_neon_vtbl ( struct expand_vec_perm_d * d ) { rtx rperm [ MAX_VECT_LEN ] , sel ; machine_mode vmode = d -> vmode ; unsigned int i , nelt = d -> nelt ; if ( BYTES_BIG_ENDIAN ) return false ; if ( d -> testing_p ) return true ; if ( vmode != V8QImode && vmode != V16QImode ) return false ; for ( i = 0 ; i < nelt ; ++ i ) rperm [ i ] = GEN_INT ( d -> perm [ i ] ) ; sel = gen_rtx_CONST_VECTOR ( vmode , gen_rtvec_v ( nelt , rperm ) ) ; sel = force_reg ( vmode , sel ) ; arm_expand_vec_perm_1 ( d -> target , d -> op0 , d -> op1 , sel ) ; return true ; } </s>

The NEON VTBL instruction is a fully variable permuation that 's even stronger than what we expose via VEC_PERM_EXPR . What it does n't do is mask the index operand as VEC_PERM_EXPR requires . Therefore we can do slightly better by expanding this as a constant where we do n't have to apply a mask .


<s> static bool arm_evpc_neon_vtrn ( struct expand_vec_perm_d * d ) { unsigned int i , odd , mask , nelt = d -> nelt ; rtx out0 , out1 , in0 , in1 , x ; rtx ( * gen ) ( rtx , rtx , rtx , rtx ) ; if ( GET_MODE_UNIT_SIZE ( d -> vmode ) >= 8 ) return false ; if ( d -> perm [ 0 ] == 0 ) odd = 0 ; else if ( d -> perm [ 0 ] == 1 ) odd = 1 ; else return false ; mask = ( d -> one_vector_p ? nelt - 1 : 2 * nelt - 1 ) ; for ( i = 0 ; i < nelt ; i += 2 ) { if ( d -> perm [ i ] != i + odd ) return false ; if ( d -> perm [ i + 1 ] != ( ( i + nelt + odd ) & mask ) ) return false ; } if ( d -> testing_p ) return true ; switch ( d -> vmode ) { case V16QImode : gen = gen_neon_vtrnv16qi_internal ; break ; case V8QImode : gen = gen_neon_vtrnv8qi_internal ; break ; case V8HImode : gen = gen_neon_vtrnv8hi_internal ; break ; case V4HImode : gen = gen_neon_vtrnv4hi_internal ; break ; case V4SImode : gen = gen_neon_vtrnv4si_internal ; break ; case V2SImode : gen = gen_neon_vtrnv2si_internal ; break ; case V2SFmode : gen = gen_neon_vtrnv2sf_internal ; break ; case V4SFmode : gen = gen_neon_vtrnv4sf_internal ; break ; default : gcc_unreachable ( ) ; } in0 = d -> op0 ; in1 = d -> op1 ; if ( BYTES_BIG_ENDIAN ) { x = in0 , in0 = in1 , in1 = x ; odd = ! odd ; } out0 = d -> target ; out1 = gen_reg_rtx ( d -> vmode ) ; if ( odd ) x = out0 , out0 = out1 , out1 = x ; emit_insn ( gen ( out0 , in0 , in1 , out1 ) ) ; return true ; } </s>


<s> static bool arm_evpc_neon_vuzp ( struct expand_vec_perm_d * d ) { unsigned int i , odd , mask , nelt = d -> nelt ; rtx out0 , out1 , in0 , in1 , x ; rtx ( * gen ) ( rtx , rtx , rtx , rtx ) ; if ( GET_MODE_UNIT_SIZE ( d -> vmode ) >= 8 ) return false ; if ( d -> perm [ 0 ] == 0 ) odd = 0 ; else if ( d -> perm [ 0 ] == 1 ) odd = 1 ; else return false ; mask = ( d -> one_vector_p ? nelt - 1 : 2 * nelt - 1 ) ; for ( i = 0 ; i < nelt ; i ++ ) { unsigned elt = ( i * 2 + odd ) & mask ; if ( d -> perm [ i ] != elt ) return false ; } if ( d -> testing_p ) return true ; switch ( d -> vmode ) { case V16QImode : gen = gen_neon_vuzpv16qi_internal ; break ; case V8QImode : gen = gen_neon_vuzpv8qi_internal ; break ; case V8HImode : gen = gen_neon_vuzpv8hi_internal ; break ; case V4HImode : gen = gen_neon_vuzpv4hi_internal ; break ; case V4SImode : gen = gen_neon_vuzpv4si_internal ; break ; case V2SImode : gen = gen_neon_vuzpv2si_internal ; break ; case V2SFmode : gen = gen_neon_vuzpv2sf_internal ; break ; case V4SFmode : gen = gen_neon_vuzpv4sf_internal ; break ; default : gcc_unreachable ( ) ; } in0 = d -> op0 ; in1 = d -> op1 ; if ( BYTES_BIG_ENDIAN ) { x = in0 , in0 = in1 , in1 = x ; odd = ! odd ; } out0 = d -> target ; out1 = gen_reg_rtx ( d -> vmode ) ; if ( odd ) x = out0 , out0 = out1 , out1 = x ; emit_insn ( gen ( out0 , in0 , in1 , out1 ) ) ; return true ; } </s>


<s> static bool arm_evpc_neon_vzip ( struct expand_vec_perm_d * d ) { unsigned int i , high , mask , nelt = d -> nelt ; rtx out0 , out1 , in0 , in1 , x ; rtx ( * gen ) ( rtx , rtx , rtx , rtx ) ; if ( GET_MODE_UNIT_SIZE ( d -> vmode ) >= 8 ) return false ; high = nelt / 2 ; if ( d -> perm [ 0 ] == high ) ; else if ( d -> perm [ 0 ] == 0 ) high = 0 ; else return false ; mask = ( d -> one_vector_p ? nelt - 1 : 2 * nelt - 1 ) ; for ( i = 0 ; i < nelt / 2 ; i ++ ) { unsigned elt = ( i + high ) & mask ; if ( d -> perm [ i * 2 ] != elt ) return false ; elt = ( elt + nelt ) & mask ; if ( d -> perm [ i * 2 + 1 ] != elt ) return false ; } if ( d -> testing_p ) return true ; switch ( d -> vmode ) { case V16QImode : gen = gen_neon_vzipv16qi_internal ; break ; case V8QImode : gen = gen_neon_vzipv8qi_internal ; break ; case V8HImode : gen = gen_neon_vzipv8hi_internal ; break ; case V4HImode : gen = gen_neon_vzipv4hi_internal ; break ; case V4SImode : gen = gen_neon_vzipv4si_internal ; break ; case V2SImode : gen = gen_neon_vzipv2si_internal ; break ; case V2SFmode : gen = gen_neon_vzipv2sf_internal ; break ; case V4SFmode : gen = gen_neon_vzipv4sf_internal ; break ; default : gcc_unreachable ( ) ; } in0 = d -> op0 ; in1 = d -> op1 ; if ( BYTES_BIG_ENDIAN ) { x = in0 , in0 = in1 , in1 = x ; high = ! high ; } out0 = d -> target ; out1 = gen_reg_rtx ( d -> vmode ) ; if ( high ) x = out0 , out0 = out1 , out1 = x ; emit_insn ( gen ( out0 , in0 , in1 , out1 ) ) ; return true ; } </s>


<s> void arm_expand_compare_and_swap ( rtx operands [ ] ) { rtx bval , rval , mem , oldval , newval , is_weak , mod_s , mod_f , x ; machine_mode mode ; rtx ( * gen ) ( rtx , rtx , rtx , rtx , rtx , rtx , rtx ) ; bval = operands [ 0 ] ; rval = operands [ 1 ] ; mem = operands [ 2 ] ; oldval = operands [ 3 ] ; newval = operands [ 4 ] ; is_weak = operands [ 5 ] ; mod_s = operands [ 6 ] ; mod_f = operands [ 7 ] ; mode = GET_MODE ( mem ) ; if ( TARGET_HAVE_LDACQ && INTVAL ( mod_f ) == MEMMODEL_ACQUIRE && INTVAL ( mod_s ) == MEMMODEL_RELEASE ) mod_s = GEN_INT ( MEMMODEL_ACQ_REL ) ; switch ( mode ) { case QImode : case HImode : rval = gen_reg_rtx ( SImode ) ; oldval = convert_modes ( SImode , mode , oldval , true ) ; case SImode : if ( ! arm_add_operand ( oldval , SImode ) ) oldval = force_reg ( SImode , oldval ) ; break ; case DImode : if ( ! cmpdi_operand ( oldval , mode ) ) oldval = force_reg ( mode , oldval ) ; break ; default : gcc_unreachable ( ) ; } switch ( mode ) { case QImode : gen = gen_atomic_compare_and_swapqi_1 ; break ; case HImode : gen = gen_atomic_compare_and_swaphi_1 ; break ; case SImode : gen = gen_atomic_compare_and_swapsi_1 ; break ; case DImode : gen = gen_atomic_compare_and_swapdi_1 ; break ; default : gcc_unreachable ( ) ; } emit_insn ( gen ( rval , mem , oldval , newval , is_weak , mod_s , mod_f ) ) ; if ( mode == QImode || mode == HImode ) emit_move_insn ( operands [ 1 ] , gen_lowpart ( mode , rval ) ) ; x = gen_rtx_REG ( CCmode , CC_REGNUM ) ; x = gen_rtx_EQ ( SImode , x , const0_rtx ) ; emit_insn ( gen_rtx_SET ( VOIDmode , bval , x ) ) ; } </s>


<s> bool arm_expand_vec_perm_const ( rtx target , rtx op0 , rtx op1 , rtx sel ) { struct expand_vec_perm_d d ; int i , nelt , which ; d . target = target ; d . op0 = op0 ; d . op1 = op1 ; d . vmode = GET_MODE ( target ) ; gcc_assert ( VECTOR_MODE_P ( d . vmode ) ) ; d . nelt = nelt = GET_MODE_NUNITS ( d . vmode ) ; d . testing_p = false ; for ( i = which = 0 ; i < nelt ; ++ i ) { rtx e = XVECEXP ( sel , 0 , i ) ; int ei = INTVAL ( e ) & ( 2 * nelt - 1 ) ; which |= ( ei < nelt ? 1 : 2 ) ; d . perm [ i ] = ei ; } switch ( which ) { default : gcc_unreachable ( ) ; case 3 : d . one_vector_p = false ; if ( ! rtx_equal_p ( op0 , op1 ) ) break ; case 2 : for ( i = 0 ; i < nelt ; ++ i ) d . perm [ i ] &= nelt - 1 ; d . op0 = op1 ; d . one_vector_p = true ; break ; case 1 : d . op1 = op0 ; d . one_vector_p = true ; break ; } return arm_expand_vec_perm_const_1 ( & d ) ; } </s>


<s> static bool arm_fastmul_rtx_costs ( rtx x , enum rtx_code code , enum rtx_code outer_code , int * total , bool speed ) { machine_mode mode = GET_MODE ( x ) ; if ( TARGET_THUMB1 ) { * total = thumb1_rtx_costs ( x , code , outer_code ) ; return true ; } switch ( code ) { case MULT : if ( mode == DImode && ( GET_CODE ( XEXP ( x , 0 ) ) == GET_CODE ( XEXP ( x , 1 ) ) ) && ( GET_CODE ( XEXP ( x , 0 ) ) == ZERO_EXTEND || GET_CODE ( XEXP ( x , 0 ) ) == SIGN_EXTEND ) ) { * total = COSTS_N_INSNS ( 2 ) ; return false ; } if ( mode == DImode ) { * total = COSTS_N_INSNS ( 5 ) ; return false ; } if ( CONST_INT_P ( XEXP ( x , 1 ) ) ) { unsigned HOST_WIDE_INT i = ( INTVAL ( XEXP ( x , 1 ) ) & ( unsigned HOST_WIDE_INT ) 0xffffffff ) ; int cost , const_ok = const_ok_for_arm ( i ) ; int j , booth_unit_size ; cost = const_ok ? 4 : 8 ; booth_unit_size = 8 ; for ( j = 0 ; i && j < 32 ; j += booth_unit_size ) { i >>= booth_unit_size ; cost ++ ; } * total = COSTS_N_INSNS ( cost ) ; return false ; } if ( mode == SImode ) { * total = COSTS_N_INSNS ( 4 ) ; return false ; } if ( GET_MODE_CLASS ( mode ) == MODE_FLOAT ) { if ( TARGET_HARD_FLOAT && ( mode == SFmode || ( mode == DFmode && ! TARGET_VFP_SINGLE ) ) ) { * total = COSTS_N_INSNS ( 1 ) ; return false ; } } * total = COSTS_N_INSNS ( 20 ) ; return false ; default : return arm_rtx_costs_1 ( x , outer_code , total , speed ) ; } } </s>

RTX cost for cores with a fast multiply unit ( M variants ) .


<s> bool arm_frame_pointer_required ( void ) { return ( cfun -> has_nonlocal_label || SUBTARGET_FRAME_POINTER_REQUIRED || ( TARGET_ARM && TARGET_APCS_FRAME && ! leaf_function_p ( ) ) ) ; } </s>


<s> static rtx arm_function_arg ( cumulative_args_t pcum_v , machine_mode mode , const_tree type , bool named ) { CUMULATIVE_ARGS * pcum = get_cumulative_args ( pcum_v ) ; int nregs ; if ( mode == VOIDmode ) return const0_rtx ; if ( pcum -> pcs_variant <= ARM_PCS_AAPCS_LOCAL ) { aapcs_layout_arg ( pcum , mode , type , named ) ; return pcum -> aapcs_reg ; } if ( TARGET_IWMMXT_ABI && arm_vector_mode_supported_p ( mode ) && pcum -> named_count > pcum -> nargs + 1 ) { if ( pcum -> iwmmxt_nregs <= 9 ) return gen_rtx_REG ( mode , pcum -> iwmmxt_nregs + FIRST_IWMMXT_REGNUM ) ; else { pcum -> can_split = false ; return NULL_RTX ; } } if ( pcum -> nregs & 1 && ARM_DOUBLEWORD_ALIGN && arm_needs_doubleword_align ( mode , type ) ) pcum -> nregs ++ ; if ( pcum -> can_split ) nregs = 1 ; else nregs = ARM_NUM_REGS2 ( mode , type ) ; if ( ! named || pcum -> nregs + nregs > NUM_ARG_REGS ) return NULL_RTX ; return gen_rtx_REG ( mode , pcum -> nregs ) ; } </s>

Determine where to put an argument to a function . Value is zero to push the argument on the stack , or a hard register in which to store the argument . MODE is the argument 's machine mode . TYPE is the data type of the argument ( as a tree ) . This is null for libcalls where that information may not be available . CUM is a variable of type CUMULATIVE_ARGS which gives info about the preceding args and about the function being called . NAMED is nonzero if this argument is a named parameter ( otherwise it is an extra parameter matching an ellipsis ) . On the ARM , normally the first 16 bytes are passed in registers r0-r3 ; all other arguments are passed on the stack . If ( NAMED == 0 ) ( which happens only in assign_parms , since TARGET_SETUP_INCOMING_VARARGS is defined ) , say it is passed in the stack ( function_prologue will indeed make it pass in the stack if necessary ) .


<s> static void arm_function_arg_advance ( cumulative_args_t pcum_v , machine_mode mode , const_tree type , bool named ) { CUMULATIVE_ARGS * pcum = get_cumulative_args ( pcum_v ) ; if ( pcum -> pcs_variant <= ARM_PCS_AAPCS_LOCAL ) { aapcs_layout_arg ( pcum , mode , type , named ) ; if ( pcum -> aapcs_cprc_slot >= 0 ) { aapcs_cp_arg_layout [ pcum -> aapcs_cprc_slot ] . advance ( pcum , mode , type ) ; pcum -> aapcs_cprc_slot = - 1 ; } pcum -> aapcs_arg_processed = false ; pcum -> aapcs_ncrn = pcum -> aapcs_next_ncrn ; pcum -> aapcs_reg = NULL_RTX ; pcum -> aapcs_partial = 0 ; } else { pcum -> nargs += 1 ; if ( arm_vector_mode_supported_p ( mode ) && pcum -> named_count > pcum -> nargs && TARGET_IWMMXT_ABI ) pcum -> iwmmxt_nregs += 1 ; else pcum -> nregs += ARM_NUM_REGS2 ( mode , type ) ; } } </s>

Update the data in PCUM to advance over an argument of mode MODE and data type TYPE . ( TYPE is null for libcalls where that information may not be available . )


<s> static bool arm_function_ok_for_sibcall ( tree decl , tree exp ) { unsigned long func_type ; if ( cfun -> machine -> sibcall_blocked ) return false ; if ( TARGET_THUMB1 ) return false ; if ( TARGET_VXWORKS_RTP && flag_pic && ! targetm . binds_local_p ( decl ) ) return false ; if ( TARGET_INTERWORK && decl && TREE_PUBLIC ( decl ) && ! TREE_ASM_WRITTEN ( decl ) ) return false ; func_type = arm_current_func_type ( ) ; if ( IS_INTERRUPT ( func_type ) ) return false ; if ( ! VOID_TYPE_P ( TREE_TYPE ( DECL_RESULT ( cfun -> decl ) ) ) ) { rtx a , b ; a = arm_function_value ( TREE_TYPE ( exp ) , decl , false ) ; b = arm_function_value ( TREE_TYPE ( DECL_RESULT ( cfun -> decl ) ) , cfun -> decl , false ) ; if ( ! rtx_equal_p ( a , b ) ) return false ; } if ( IS_STACKALIGN ( func_type ) ) return false ; if ( TARGET_AAPCS_BASED && arm_abi == ARM_ABI_AAPCS && decl && DECL_WEAK ( decl ) ) return false ; return true ; } </s>

Return non-zero if it is ok to make a tail-call to DECL .


<s> static rtx arm_function_value ( const_tree type , const_tree func , bool outgoing ATTRIBUTE_UNUSED ) { machine_mode mode ; int unsignedp ATTRIBUTE_UNUSED ; rtx r ATTRIBUTE_UNUSED ; mode = TYPE_MODE ( type ) ; if ( TARGET_AAPCS_BASED ) return aapcs_allocate_return_reg ( mode , type , func ) ; if ( INTEGRAL_TYPE_P ( type ) ) mode = arm_promote_function_mode ( type , mode , & unsignedp , func , 1 ) ; if ( arm_return_in_msb ( type ) ) { HOST_WIDE_INT size = int_size_in_bytes ( type ) ; if ( size % UNITS_PER_WORD != 0 ) { size += UNITS_PER_WORD - size % UNITS_PER_WORD ; mode = mode_for_size ( size * BITS_PER_UNIT , MODE_INT , 0 ) ; } } return arm_libcall_value_1 ( mode ) ; } </s>

Define how to find the value returned by a function .


<s> static bool arm_function_value_regno_p ( const unsigned int regno ) { if ( regno == ARG_REGISTER ( 1 ) || ( TARGET_32BIT && TARGET_AAPCS_BASED && TARGET_VFP && TARGET_HARD_FLOAT && regno == FIRST_VFP_REGNUM ) || ( TARGET_IWMMXT_ABI && regno == FIRST_IWMMXT_REGNUM ) ) return true ; return false ; } </s>


<s> rtx arm_gen_compare_reg ( enum rtx_code code , rtx x , rtx y , rtx scratch ) { machine_mode mode ; rtx cc_reg ; int dimode_comparison = GET_MODE ( x ) == DImode || GET_MODE ( y ) == DImode ; if ( dimode_comparison && ! REG_P ( x ) ) x = force_reg ( DImode , x ) ; mode = SELECT_CC_MODE ( code , x , y ) ; cc_reg = gen_rtx_REG ( mode , CC_REGNUM ) ; if ( dimode_comparison && mode != CC_CZmode ) { rtx clobber , set ; if ( mode == CC_Zmode && y != const0_rtx ) { gcc_assert ( ! reload_completed ) ; x = expand_binop ( DImode , xor_optab , x , y , NULL_RTX , 0 , OPTAB_WIDEN ) ; y = const0_rtx ; } if ( reload_completed ) gcc_assert ( scratch != NULL && GET_MODE ( scratch ) == SImode ) ; else scratch = gen_rtx_SCRATCH ( SImode ) ; clobber = gen_rtx_CLOBBER ( VOIDmode , scratch ) ; set = gen_rtx_SET ( VOIDmode , cc_reg , gen_rtx_COMPARE ( mode , x , y ) ) ; emit_insn ( gen_rtx_PARALLEL ( VOIDmode , gen_rtvec ( 2 , set , clobber ) ) ) ; } else emit_set_insn ( cc_reg , gen_rtx_COMPARE ( mode , x , y ) ) ; return cc_reg ; } </s>

X and Y are two things to compare using CODE . Emit the compare insn and return the rtx for register 0 in the proper mode . FP means this is a floating point compare : I do n't think that it is needed on the arm .


<s> static rtx arm_gen_load_multiple_1 ( int count , int * regs , rtx * mems , rtx basereg , HOST_WIDE_INT wback_offset ) { int i = 0 , j ; rtx result ; if ( ! multiple_operation_profitable_p ( false , count , 0 ) ) { rtx seq ; start_sequence ( ) ; for ( i = 0 ; i < count ; i ++ ) emit_move_insn ( gen_rtx_REG ( SImode , regs [ i ] ) , mems [ i ] ) ; if ( wback_offset != 0 ) emit_move_insn ( basereg , plus_constant ( Pmode , basereg , wback_offset ) ) ; seq = get_insns ( ) ; end_sequence ( ) ; return seq ; } result = gen_rtx_PARALLEL ( VOIDmode , rtvec_alloc ( count + ( wback_offset != 0 ? 1 : 0 ) ) ) ; if ( wback_offset != 0 ) { XVECEXP ( result , 0 , 0 ) = gen_rtx_SET ( VOIDmode , basereg , plus_constant ( Pmode , basereg , wback_offset ) ) ; i = 1 ; count ++ ; } for ( j = 0 ; i < count ; i ++ , j ++ ) XVECEXP ( result , 0 , i ) = gen_rtx_SET ( VOIDmode , gen_rtx_REG ( SImode , regs [ j ] ) , mems [ j ] ) ; return result ; } </s>

Generate a load-multiple instruction . COUNT is the number of loads in the instruction ; REGS and MEMS are arrays containing the operands . BASEREG is the base register to be used in addressing the memory operands . WBACK_OFFSET is nonzero if the instruction should update the base register .


<s> static rtx arm_gen_store_multiple_1 ( int count , int * regs , rtx * mems , rtx basereg , HOST_WIDE_INT wback_offset ) { int i = 0 , j ; rtx result ; if ( GET_CODE ( basereg ) == PLUS ) basereg = XEXP ( basereg , 0 ) ; if ( ! multiple_operation_profitable_p ( false , count , 0 ) ) { rtx seq ; start_sequence ( ) ; for ( i = 0 ; i < count ; i ++ ) emit_move_insn ( mems [ i ] , gen_rtx_REG ( SImode , regs [ i ] ) ) ; if ( wback_offset != 0 ) emit_move_insn ( basereg , plus_constant ( Pmode , basereg , wback_offset ) ) ; seq = get_insns ( ) ; end_sequence ( ) ; return seq ; } result = gen_rtx_PARALLEL ( VOIDmode , rtvec_alloc ( count + ( wback_offset != 0 ? 1 : 0 ) ) ) ; if ( wback_offset != 0 ) { XVECEXP ( result , 0 , 0 ) = gen_rtx_SET ( VOIDmode , basereg , plus_constant ( Pmode , basereg , wback_offset ) ) ; i = 1 ; count ++ ; } for ( j = 0 ; i < count ; i ++ , j ++ ) XVECEXP ( result , 0 , i ) = gen_rtx_SET ( VOIDmode , mems [ j ] , gen_rtx_REG ( SImode , regs [ j ] ) ) ; return result ; } </s>

Generate a store-multiple instruction . COUNT is the number of stores in the instruction ; REGS and MEMS are arrays containing the operands . BASEREG is the base register to be used in addressing the memory operands . WBACK_OFFSET is nonzero if the instruction should update the base register .


"variadic functions must use the base AAPCS variant"

Get the PCS variant to use for this call . TYPE is the function 's type specification , DECL is the specific declartion . DECL may be null if the call could be indirect or if this is a library call .


<s> static int arm_get_vfp_saved_size ( void ) { unsigned int regno ; int count ; int saved ; saved = 0 ; if ( TARGET_HARD_FLOAT && TARGET_VFP ) { count = 0 ; for ( regno = FIRST_VFP_REGNUM ; regno < LAST_VFP_REGNUM ; regno += 2 ) { if ( ( ! df_regs_ever_live_p ( regno ) || call_used_regs [ regno ] ) && ( ! df_regs_ever_live_p ( regno + 1 ) || call_used_regs [ regno + 1 ] ) ) { if ( count > 0 ) { if ( count == 2 && ! arm_arch6 ) count ++ ; saved += count * 8 ; } count = 0 ; } else count ++ ; } if ( count > 0 ) { if ( count == 2 && ! arm_arch6 ) count ++ ; saved += count * 8 ; } } return saved ; } </s>

Return the number of bytes required to save VFP registers .


<s> int arm_hard_regno_mode_ok ( unsigned int regno , machine_mode mode ) { if ( GET_MODE_CLASS ( mode ) == MODE_CC ) return ( regno == CC_REGNUM || ( TARGET_HARD_FLOAT && TARGET_VFP && regno == VFPCC_REGNUM ) ) ; if ( regno == CC_REGNUM && GET_MODE_CLASS ( mode ) != MODE_CC ) return false ; if ( TARGET_THUMB1 ) return ( ARM_NUM_REGS ( mode ) < 2 ) || ( regno < LAST_LO_REGNUM ) ; if ( TARGET_HARD_FLOAT && TARGET_VFP && IS_VFP_REGNUM ( regno ) ) { if ( mode == SFmode || mode == SImode ) return VFP_REGNO_OK_FOR_SINGLE ( regno ) ; if ( mode == DFmode ) return VFP_REGNO_OK_FOR_DOUBLE ( regno ) ; if ( mode == HFmode ) return TARGET_FP16 && VFP_REGNO_OK_FOR_SINGLE ( regno ) ; if ( TARGET_NEON ) return ( VALID_NEON_DREG_MODE ( mode ) && VFP_REGNO_OK_FOR_DOUBLE ( regno ) ) || ( VALID_NEON_QREG_MODE ( mode ) && NEON_REGNO_OK_FOR_QUAD ( regno ) ) || ( mode == TImode && NEON_REGNO_OK_FOR_NREGS ( regno , 2 ) ) || ( mode == EImode && NEON_REGNO_OK_FOR_NREGS ( regno , 3 ) ) || ( mode == OImode && NEON_REGNO_OK_FOR_NREGS ( regno , 4 ) ) || ( mode == CImode && NEON_REGNO_OK_FOR_NREGS ( regno , 6 ) ) || ( mode == XImode && NEON_REGNO_OK_FOR_NREGS ( regno , 8 ) ) ; return FALSE ; } if ( TARGET_REALLY_IWMMXT ) { if ( IS_IWMMXT_GR_REGNUM ( regno ) ) return mode == SImode ; if ( IS_IWMMXT_REGNUM ( regno ) ) return VALID_IWMMXT_REG_MODE ( mode ) ; } if ( regno <= LAST_ARM_REGNUM ) { if ( ARM_NUM_REGS ( mode ) > 4 ) return FALSE ; if ( TARGET_THUMB2 ) return TRUE ; return ! ( TARGET_LDRD && GET_MODE_SIZE ( mode ) > 4 && ( regno & 1 ) != 0 ) ; } if ( regno == FRAME_POINTER_REGNUM || regno == ARG_POINTER_REGNUM ) return GET_MODE_CLASS ( mode ) == MODE_INT ; return FALSE ; } </s>

Returns true if REGNO is a valid register for holding a quantity of type MODE .


<s> static struct machine_function * arm_init_machine_status ( void ) { struct machine_function * machine ; machine = ggc_cleared_alloc < machine_function > ( ) ; machine -> func_type = ARM_FT_UNKNOWN ; return machine ; } </s>

Functions to save and restore machine-specific function data .


"function parameters cannot have __fp16 type"


<s>
static
const
char
*
arm_invalid_return_type
(
const_tree
t
)
{
if
(
SCALAR_FLOAT_TYPE_P
(
t
)
&&
TYPE_PRECISION
(
t
)
==
16
)
return
N_
(
"functions cannot return __fp16 type"
)
;
return
;
}
</s>

arm
16
"functions cannot return __fp16 type"


<s> static int arm_issue_rate ( void ) { switch ( arm_tune ) { case xgene1 : return 4 ; case cortexa15 : case cortexa57 : case exynosm1 : return 3 ; case cortexm7 : case cortexr4 : case cortexr4f : case cortexr5 : case genericv7a : case cortexa5 : case cortexa7 : case cortexa8 : case cortexa9 : case cortexa12 : case cortexa17 : case cortexa53 : case fa726te : case marvell_pj4 : return 2 ; default : return 1 ; } } </s>

Most ARM cores are single issue , but some newer ones can dual issue . The scheduler descriptions rely on this being correct .


<s> int arm_legitimate_address_outer_p ( machine_mode mode , rtx x , RTX_CODE outer , int strict_p ) { bool use_ldrd ; enum rtx_code code = GET_CODE ( x ) ; if ( arm_address_register_rtx_p ( x , strict_p ) ) return 1 ; use_ldrd = ( TARGET_LDRD && ( mode == DImode || ( mode == DFmode && ( TARGET_SOFT_FLOAT || TARGET_VFP ) ) ) ) ; if ( code == POST_INC || code == PRE_DEC || ( ( code == PRE_INC || code == POST_DEC ) && ( use_ldrd || GET_MODE_SIZE ( mode ) <= 4 ) ) ) return arm_address_register_rtx_p ( XEXP ( x , 0 ) , strict_p ) ; else if ( ( code == POST_MODIFY || code == PRE_MODIFY ) && arm_address_register_rtx_p ( XEXP ( x , 0 ) , strict_p ) && GET_CODE ( XEXP ( x , 1 ) ) == PLUS && rtx_equal_p ( XEXP ( XEXP ( x , 1 ) , 0 ) , XEXP ( x , 0 ) ) ) { rtx addend = XEXP ( XEXP ( x , 1 ) , 1 ) ; if ( use_ldrd && GET_CODE ( x ) == POST_MODIFY && REG_P ( addend ) ) return 0 ; return ( ( use_ldrd || GET_MODE_SIZE ( mode ) <= 4 ) && arm_legitimate_index_p ( mode , addend , outer , strict_p ) ) ; } else if ( reload_completed && ( code == LABEL_REF || ( code == CONST && GET_CODE ( XEXP ( x , 0 ) ) == PLUS && GET_CODE ( XEXP ( XEXP ( x , 0 ) , 0 ) ) == LABEL_REF && CONST_INT_P ( XEXP ( XEXP ( x , 0 ) , 1 ) ) ) ) ) return 1 ; else if ( mode == TImode || ( TARGET_NEON && VALID_NEON_STRUCT_MODE ( mode ) ) ) return 0 ; else if ( code == PLUS ) { rtx xop0 = XEXP ( x , 0 ) ; rtx xop1 = XEXP ( x , 1 ) ; return ( ( arm_address_register_rtx_p ( xop0 , strict_p ) && ( ( CONST_INT_P ( xop1 ) && arm_legitimate_index_p ( mode , xop1 , outer , strict_p ) ) || ( ! strict_p && will_be_in_index_register ( xop1 ) ) ) ) || ( arm_address_register_rtx_p ( xop1 , strict_p ) && arm_legitimate_index_p ( mode , xop0 , outer , strict_p ) ) ) ; } else if ( GET_CODE ( x ) == MINUS ) { rtx xop0 = XEXP ( x , 0 ) ; rtx xop1 = XEXP ( x , 1 ) ; return ( arm_address_register_rtx_p ( xop0 , strict_p ) && arm_legitimate_index_p ( mode , xop1 , outer , strict_p ) ) ; } else if ( GET_MODE_CLASS ( mode ) != MODE_FLOAT && code == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P ( x ) && ! ( flag_pic && symbol_mentioned_p ( get_pool_constant ( x ) ) && ! pcrel_constant_p ( get_pool_constant ( x ) ) ) ) return 1 ; return 0 ; } </s>

Return nonzero if X is a valid ARM state address operand .


<s> void arm_load_pic_register ( unsigned long saved_regs ATTRIBUTE_UNUSED ) { rtx l1 , labelno , pic_tmp , pic_rtx , pic_reg ; if ( crtl -> uses_pic_offset_table == 0 || TARGET_SINGLE_PIC_BASE ) return ; gcc_assert ( flag_pic ) ; pic_reg = cfun -> machine -> pic_reg ; if ( TARGET_VXWORKS_RTP ) { pic_rtx = gen_rtx_SYMBOL_REF ( Pmode , VXWORKS_GOTT_BASE ) ; pic_rtx = gen_rtx_CONST ( Pmode , pic_rtx ) ; emit_insn ( gen_pic_load_addr_32bit ( pic_reg , pic_rtx ) ) ; emit_insn ( gen_rtx_SET ( Pmode , pic_reg , gen_rtx_MEM ( Pmode , pic_reg ) ) ) ; pic_tmp = gen_rtx_SYMBOL_REF ( Pmode , VXWORKS_GOTT_INDEX ) ; emit_insn ( gen_pic_offset_arm ( pic_reg , pic_reg , pic_tmp ) ) ; } else { labelno = GEN_INT ( pic_labelno ++ ) ; l1 = gen_rtx_UNSPEC ( Pmode , gen_rtvec ( 1 , labelno ) , UNSPEC_PIC_LABEL ) ; l1 = gen_rtx_CONST ( VOIDmode , l1 ) ; pic_rtx = plus_constant ( Pmode , l1 , TARGET_ARM ? 8 : 4 ) ; pic_rtx = gen_rtx_UNSPEC ( Pmode , gen_rtvec ( 1 , pic_rtx ) , UNSPEC_GOTSYM_OFF ) ; pic_rtx = gen_rtx_CONST ( Pmode , pic_rtx ) ; if ( TARGET_32BIT ) { emit_insn ( gen_pic_load_addr_unified ( pic_reg , pic_rtx , labelno ) ) ; } else { if ( arm_pic_register != INVALID_REGNUM && REGNO ( pic_reg ) > LAST_LO_REGNUM ) { pic_tmp = gen_rtx_REG ( SImode , thumb_find_work_register ( saved_regs ) ) ; emit_insn ( gen_pic_load_addr_thumb1 ( pic_tmp , pic_rtx ) ) ; emit_insn ( gen_movsi ( pic_offset_table_rtx , pic_tmp ) ) ; emit_insn ( gen_pic_add_dot_plus_four ( pic_reg , pic_reg , labelno ) ) ; } else if ( arm_pic_register != INVALID_REGNUM && arm_pic_register > LAST_LO_REGNUM && REGNO ( pic_reg ) <= LAST_LO_REGNUM ) { emit_insn ( gen_pic_load_addr_unified ( pic_reg , pic_rtx , labelno ) ) ; emit_move_insn ( gen_rtx_REG ( Pmode , arm_pic_register ) , pic_reg ) ; emit_use ( gen_rtx_REG ( Pmode , arm_pic_register ) ) ; } else emit_insn ( gen_pic_load_addr_unified ( pic_reg , pic_rtx , labelno ) ) ; } } emit_use ( pic_reg ) ; } </s>

Generate code to load the PIC register . In thumb mode SCRATCH is a low register .


<s> int arm_max_const_double_inline_cost ( ) { if ( arm_disable_literal_pool ) return 99 ; return ( ( optimize_size || arm_ld_sched ) ? 3 : 4 ) ; } </s>

Return maximum allowed cost of synthesizing a 64-bit constant VAL inline . Returns the number of insns needed , or 99 if we always want to synthesize the value .


<s> bool arm_modes_tieable_p ( machine_mode mode1 , machine_mode mode2 ) { if ( GET_MODE_CLASS ( mode1 ) == GET_MODE_CLASS ( mode2 ) ) return true ; if ( TARGET_NEON && ( VALID_NEON_DREG_MODE ( mode1 ) || VALID_NEON_QREG_MODE ( mode1 ) || VALID_NEON_STRUCT_MODE ( mode1 ) ) && ( VALID_NEON_DREG_MODE ( mode2 ) || VALID_NEON_QREG_MODE ( mode2 ) || VALID_NEON_STRUCT_MODE ( mode2 ) ) ) return true ; return false ; } </s>


<s> static int arm_movmemqi_unaligned ( rtx * operands ) { HOST_WIDE_INT length = INTVAL ( operands [ 2 ] ) ; if ( optimize_size ) { bool src_aligned = MEM_ALIGN ( operands [ 1 ] ) >= BITS_PER_WORD ; bool dst_aligned = MEM_ALIGN ( operands [ 0 ] ) >= BITS_PER_WORD ; unsigned int interleave_factor = ( src_aligned || dst_aligned ) ? 2 : 1 ; HOST_WIDE_INT bytes_per_iter = ( src_aligned || dst_aligned ) ? 8 : 4 ; if ( length > 12 ) arm_block_move_unaligned_loop ( operands [ 0 ] , operands [ 1 ] , length , interleave_factor , bytes_per_iter ) ; else arm_block_move_unaligned_straight ( operands [ 0 ] , operands [ 1 ] , length , interleave_factor ) ; } else { if ( length > 32 ) arm_block_move_unaligned_loop ( operands [ 0 ] , operands [ 1 ] , length , 4 , 16 ) ; else arm_block_move_unaligned_straight ( operands [ 0 ] , operands [ 1 ] , length , 4 ) ; } return 1 ; } </s>

Emit a block move when either the source or destination is unaligned ( not aligned to a four-byte boundary ) . This may need further tuning depending on core type , optimize_size setting , etc .


<s> static bool arm_must_pass_in_stack ( machine_mode mode , const_tree type ) { if ( TARGET_AAPCS_BASED ) return must_pass_in_stack_var_size ( mode , type ) ; else return must_pass_in_stack_var_size_or_pad ( mode , type ) ; } </s>

Return true if a type must be passed in memory . For AAPCS , small aggregates ( padded to the size of a word ) should be passed in a register .


<s> static bool arm_needs_doubleword_align ( machine_mode mode , const_tree type ) { return ( GET_MODE_ALIGNMENT ( mode ) > PARM_BOUNDARY || ( type && TYPE_ALIGN ( type ) > PARM_BOUNDARY ) ) ; } </s>

Return 1 if double word alignment is required for argument passing . Return -1 if double word alignment used to be required for argument passing before PR77728 ABI fix , but is not required anymore . Return 0 if double word alignment is not required and was n't requried before either .


"\t%@ Function supports interworking.\n"

"\t%@ Fast Interrupt Service Routine.\n"

"\t%@ Naked Function: prologue and epilogue provided by programmer.\n"

"\t%@ Volatile: function does not return.\n"

"\t%@ Nested: function declared inside another function.\n"

"\t%@ Stack Align: May be called with mis-aligned SP.\n"

"\t%@ args = %d, pretend = %d, frame = %wd\n"

"\t%@ frame_needed = %d, uses_anonymous_args = %d\n"

Place some comments into the assembler stream describing the current function .


<s>
const
char
*
arm_output_iwmmxt_tinsr
(
rtx
*
operands
)
{
int
mask
=
INTVAL
(
operands
[
3
]
)
;
int
i
;
char
templ
[
50
]
;
int
units
=
mode_nunits
[
GET_MODE
(
operands
[
0
]
)
]
;
gcc_assert
(
(
mask
&
(
mask
1
)
)
==
0
)
;
for
(
i
=
0
;
i
<
units
;
++
i
)
{
if
(
(
mask
&
0x01
)
==
1
)
{
break
;
}
mask
>>=
1
;
}
gcc_assert
(
i
<
units
)
;
{
switch
(
GET_MODE
(
operands
[
0
]
)
)
{
case
V8QImode
:
sprintf
(
templ
,
"tinsrb%%?\t%%0, %%2, #%d"
,
i
)
;
break
;
case
V4HImode
:
sprintf
(
templ
,
"tinsrh%%?\t%%0, %%2, #%d"
,
i
)
;
break
;
case
V2SImode
:
sprintf
(
templ
,
"tinsrw%%?\t%%0, %%2, #%d"
,
i
)
;
break
;
default
:
gcc_unreachable
(
)
;
break
;
}
output_asm_insn
(
templ
,
operands
)
;
}
return
""
;
}
</s>

Output assembly for a WMMX tinsr instruction .


<s>
void
arm_output_multireg_pop
(
rtx
*
operands
,
bool
return_pc
,
rtx
cond
,
bool
reverse
,
bool
update
)
{
int
i
;
char
pattern
[
100
]
;
int
offset
;
const
char
*
conditional
;
int
num_saves
=
XVECLEN
(
operands
[
0
]
,
0
)
;
unsigned
int
regno
;
unsigned
int
regno_base
=
REGNO
(
operands
[
1
]
)
;
offset
=
0
;
offset
+=
update
1
:
0
;
offset
+=
return_pc
1
:
0
;
for
(
i
=
offset
;
i
<
num_saves
;
i
++
)
{
regno
=
REGNO
(
XEXP
(
XVECEXP
(
operands
[
0
]
,
0
,
i
)
,
0
)
)
;
gcc_assert
(
(
regno
!=
SP_REGNUM
)
||
(
regno_base
==
SP_REGNUM
)
)
;
if
(
regno
==
regno_base
)
gcc_assert
(
!
update
)
;
}
conditional
=
reverse
"%?%D0"
:
"%?%d0"
;
if
(
(
regno_base
==
SP_REGNUM
)
&&
TARGET_UNIFIED_ASM
)
{
gcc_assert
(
update
)
;
sprintf
(
pattern
,
"pop%s\t{"
,
conditional
)
;
}
else
{
if
(
regno_base
==
SP_REGNUM
)
sprintf
(
pattern
,
"ldm%sfd\t"
,
conditional
)
;
else
if
(
TARGET_UNIFIED_ASM
)
sprintf
(
pattern
,
"ldmia%s\t"
,
conditional
)
;
else
sprintf
(
pattern
,
"ldm%sia\t"
,
conditional
)
;
strcat
(
pattern
,
reg_names
[
regno_base
]
)
;
if
(
update
)
strcat
(
pattern
,
"!, {"
)
;
else
strcat
(
pattern
,
", {"
)
;
}
strcat
(
pattern
,
reg_names
[
REGNO
(
XEXP
(
XVECEXP
(
operands
[
0
]
,
0
,
offset
)
,
0
)
)
]
)
;
for
(
i
=
offset
+
1
;
i
<
num_saves
;
i
++
)
{
strcat
(
pattern
,
", "
)
;
strcat
(
pattern
,
reg_names
[
REGNO
(
XEXP
(
XVECEXP
(
operands
[
0
]
,
0
,
i
)
,
0
)
)
]
)
;
}
strcat
(
pattern
,
"}"
)
;
if
(
IS_INTERRUPT
(
arm_current_func_type
(
)
)
&&
return_pc
)
strcat
(
pattern
,
"^"
)
;
output_asm_insn
(
pattern
,
&
cond
)
;
}
</s>

OPERANDS [ 0 ] is the entire list of insns that constitute pop , OPERANDS [ 1 ] is the base register , RETURN_PC is true iff return insn is in the list , UPDATE is true iff the list contains explicit update of base register .


<s>
const
char
*
arm_output_shift
(
rtx
*
operands
,
int
set_flags
)
{
char
pattern
[
100
]
;
static
const
char
flag_chars
[
3
]
=
{
'?'
,
'.'
,
'!'
}
;
const
char
*
shift
;
HOST_WIDE_INT
val
;
char
c
;
c
=
flag_chars
[
set_flags
]
;
if
(
TARGET_UNIFIED_ASM
)
{
shift
=
shift_op
(
operands
[
3
]
,
&
val
)
;
if
(
shift
)
{
if
(
val
!=
1
)
operands
[
2
]
=
GEN_INT
(
val
)
;
sprintf
(
pattern
,
"%s%%%c\t%%0, %%1, %%2"
,
shift
,
c
)
;
}
else
sprintf
(
pattern
,
"mov%%%c\t%%0, %%1"
,
c
)
;
}
else
sprintf
(
pattern
,
"mov%%%c\t%%0, %%1%%S3"
,
c
)
;
output_asm_insn
(
pattern
,
operands
)
;
return
""
;
}
</s>

Output assembly for a shift instruction . SET_FLAGS determines how the instruction modifies the condition codes . 0 - Do not set condition codes . 1 - Set condition codes . 2 - Use smallest instruction .


<s> bool arm_pad_arg_upward ( machine_mode mode ATTRIBUTE_UNUSED , const_tree type ) { if ( ! TARGET_AAPCS_BASED ) return DEFAULT_FUNCTION_ARG_PADDING ( mode , type ) == upward ; if ( type && BYTES_BIG_ENDIAN && INTEGRAL_TYPE_P ( type ) ) return false ; return true ; } </s>

For use by FUNCTION_ARG_PADDING ( MODE , TYPE ) . Return true if an argument passed on the stack should be padded upwards , i.e . if the least-significant byte has useful data . For legacy APCS ABIs we use the default . For AAPCS based ABIs small aggregate types are placed in the lowest memory address .


<s> static bool arm_pass_by_reference ( cumulative_args_t cum ATTRIBUTE_UNUSED , machine_mode mode ATTRIBUTE_UNUSED , const_tree type , bool named ATTRIBUTE_UNUSED ) { return type && TREE_CODE ( TYPE_SIZE ( type ) ) != INTEGER_CST ; } </s>

Variable sized types are passed by reference . This is a GCC extension to the ARM ABI .


<s> static rtx arm_pic_static_addr ( rtx orig , rtx reg ) { rtx l1 , labelno , offset_rtx , insn ; gcc_assert ( flag_pic ) ; labelno = GEN_INT ( pic_labelno ++ ) ; l1 = gen_rtx_UNSPEC ( Pmode , gen_rtvec ( 1 , labelno ) , UNSPEC_PIC_LABEL ) ; l1 = gen_rtx_CONST ( VOIDmode , l1 ) ; offset_rtx = plus_constant ( Pmode , l1 , TARGET_ARM ? 8 : 4 ) ; offset_rtx = gen_rtx_UNSPEC ( Pmode , gen_rtvec ( 2 , orig , offset_rtx ) , UNSPEC_SYMBOL_OFFSET ) ; offset_rtx = gen_rtx_CONST ( Pmode , offset_rtx ) ; insn = emit_insn ( gen_pic_load_addr_unified ( reg , offset_rtx , labelno ) ) ; return insn ; } </s>

Generate code to load the address of a static var when flag_pic is set .


<s> static machine_mode arm_preferred_simd_mode ( machine_mode mode ) { if ( TARGET_NEON ) switch ( mode ) { case SFmode : return TARGET_NEON_VECTORIZE_DOUBLE ? V2SFmode : V4SFmode ; case SImode : return TARGET_NEON_VECTORIZE_DOUBLE ? V2SImode : V4SImode ; case HImode : return TARGET_NEON_VECTORIZE_DOUBLE ? V4HImode : V8HImode ; case QImode : return TARGET_NEON_VECTORIZE_DOUBLE ? V8QImode : V16QImode ; case DImode : if ( ! TARGET_NEON_VECTORIZE_DOUBLE ) return V2DImode ; break ; default : ; } if ( TARGET_REALLY_IWMMXT ) switch ( mode ) { case SImode : return V2SImode ; case HImode : return V4HImode ; case QImode : return V8QImode ; default : ; } return word_mode ; } </s>

Use the option -mvectorize-with-neon-double to override the use of quardword registers when autovectorizing for Neon , at least until multiple vector widths are supported properly by the middle-end .


This function is used to print CPU tuning information as comment in assembler file . Pointers are not printed for now .


<s> static tree arm_promoted_type ( const_tree t ) { if ( SCALAR_FLOAT_TYPE_P ( t ) && TYPE_PRECISION ( t ) == 16 ) return float_type_node ; return NULL_TREE ; } </s>


<s> enum reg_class arm_regno_class ( int regno ) { if ( regno == PC_REGNUM ) return NO_REGS ; if ( TARGET_THUMB1 ) { if ( regno == STACK_POINTER_REGNUM ) return STACK_REG ; if ( regno == CC_REGNUM ) return CC_REG ; if ( regno < 8 ) return LO_REGS ; return HI_REGS ; } if ( TARGET_THUMB2 && regno < 8 ) return LO_REGS ; if ( regno <= LAST_ARM_REGNUM || regno == FRAME_POINTER_REGNUM || regno == ARG_POINTER_REGNUM ) return TARGET_THUMB2 ? HI_REGS : GENERAL_REGS ; if ( regno == CC_REGNUM || regno == VFPCC_REGNUM ) return TARGET_THUMB2 ? CC_REG : NO_REGS ; if ( IS_VFP_REGNUM ( regno ) ) { if ( regno <= D7_VFP_REGNUM ) return VFP_D0_D7_REGS ; else if ( regno <= LAST_LO_VFP_REGNUM ) return VFP_LO_REGS ; else return VFP_HI_REGS ; } if ( IS_IWMMXT_REGNUM ( regno ) ) return IWMMXT_REGS ; if ( IS_IWMMXT_GR_REGNUM ( regno ) ) return IWMMXT_GR_REGS ; return NO_REGS ; } </s>

For efficiency and historical reasons LO_REGS , HI_REGS and CC_REGS are not used in arm mode .


<s>
static
bool
arm_rtx_costs
(
rtx
x
,
int
code
,
int
outer_code
,
int
opno
ATTRIBUTE_UNUSED
,
int
*
total
,
bool
speed
)
{
bool
result
;
if
(
TARGET_OLD_RTX_COSTS
||
(
!
current_tune
->
insn_extra_cost
&&
!
TARGET_NEW_GENERIC_COSTS
)
)
{
if
(
!
speed
)
result
=
arm_size_rtx_costs
(
x
,
(
enum
rtx_code
)
code
,
(
enum
rtx_code
)
outer_code
,
total
)
;
else
result
=
current_tune
->
rtx_costs
(
x
,
(
enum
rtx_code
)
code
,
(
enum
rtx_code
)
outer_code
,
total
,
speed
)
;
}
else
{
if
(
current_tune
->
insn_extra_cost
)
result
=
arm_new_rtx_costs
(
x
,
(
enum
rtx_code
)
code
,
(
enum
rtx_code
)
outer_code
,
current_tune
->
insn_extra_cost
,
total
,
speed
)
;
else
result
=
arm_new_rtx_costs
(
x
,
(
enum
rtx_code
)
code
,
(
enum
rtx_code
)
outer_code
,
&
generic_extra_costs
,
total
,
speed
)
;
}
if
(
dump_file
&&
(
dump_flags
&
TDF_DETAILS
)
)
{
print_rtl_single
(
dump_file
,
x
)
;
fprintf
(
dump_file
,
"\n%s cost: %d (%s)\n"
,
speed
"Hot"
:
"Cold"
,
*
total
,
result
"final"
:
"partial"
)
;
}
return
result
;
}
</s>

arm
"\n%s cost: %d (%s)\n"
"Hot"
"Cold"
"final"
"partial"


<s> static int arm_save_coproc_regs ( void ) { int saved_size = 0 ; unsigned reg ; unsigned start_reg ; rtx insn ; for ( reg = LAST_IWMMXT_REGNUM ; reg >= FIRST_IWMMXT_REGNUM ; reg -- ) if ( df_regs_ever_live_p ( reg ) && ! call_used_regs [ reg ] ) { insn = gen_rtx_PRE_DEC ( Pmode , stack_pointer_rtx ) ; insn = gen_rtx_MEM ( V2SImode , insn ) ; insn = emit_set_insn ( insn , gen_rtx_REG ( V2SImode , reg ) ) ; RTX_FRAME_RELATED_P ( insn ) = 1 ; saved_size += 8 ; } if ( TARGET_HARD_FLOAT && TARGET_VFP ) { start_reg = FIRST_VFP_REGNUM ; for ( reg = FIRST_VFP_REGNUM ; reg < LAST_VFP_REGNUM ; reg += 2 ) { if ( ( ! df_regs_ever_live_p ( reg ) || call_used_regs [ reg ] ) && ( ! df_regs_ever_live_p ( reg + 1 ) || call_used_regs [ reg + 1 ] ) ) { if ( start_reg != reg ) saved_size += vfp_emit_fstmd ( start_reg , ( reg - start_reg ) / 2 ) ; start_reg = reg + 2 ; } } if ( start_reg != reg ) saved_size += vfp_emit_fstmd ( start_reg , ( reg - start_reg ) / 2 ) ; } return saved_size ; } </s>

Emit RTL to save coprocessor registers on function entry . Returns the number of bytes pushed .


<s> static bool arm_scalar_mode_supported_p ( machine_mode mode ) { if ( mode == HFmode ) return ( arm_fp16_format != ARM_FP16_FORMAT_NONE ) ; else if ( ALL_FIXED_POINT_MODE_P ( mode ) ) return true ; else return default_scalar_mode_supported_p ( mode ) ; } </s>

Implement TARGET_SCALAR_MODE_SUPPORTED_P . This simply adds HFmode as a supported mode ; even though we do n't implement arithmetic on this type directly , it 's supported by optabs conversions , much the way the double-word arithmetic is special-cased in the default hook .


<s> static int arm_sched_reorder ( FILE * file , int verbose , rtx_insn * * ready , int * n_readyp , int clock ) { switch ( arm_tune ) { case cortexa7 : cortexa7_sched_reorder ( file , verbose , ready , n_readyp , clock ) ; break ; default : break ; } return arm_issue_rate ( ) ; } </s>


<s> static void arm_setup_incoming_varargs ( cumulative_args_t pcum_v , machine_mode mode , tree type , int * pretend_size , int second_time ATTRIBUTE_UNUSED ) { CUMULATIVE_ARGS * pcum = get_cumulative_args ( pcum_v ) ; int nregs ; cfun -> machine -> uses_anonymous_args = 1 ; if ( pcum -> pcs_variant <= ARM_PCS_AAPCS_LOCAL ) { nregs = pcum -> aapcs_ncrn ; if ( ( nregs & 1 ) && arm_needs_doubleword_align ( mode , type ) ) nregs ++ ; } else nregs = pcum -> nregs ; if ( nregs < NUM_ARG_REGS ) * pretend_size = ( NUM_ARG_REGS - nregs ) * UNITS_PER_WORD ; } </s>

Worker function for TARGET_SETUP_INCOMING_VARARGS . On the ARM , PRETEND_SIZE is set in order to have the prologue push the last named arg and all anonymous args onto the stack . XXX I know the prologue should n't be pushing registers , but it is faster that way .


<s> static bool arm_slowmul_rtx_costs ( rtx x , enum rtx_code code , enum rtx_code outer_code , int * total , bool speed ) { machine_mode mode = GET_MODE ( x ) ; if ( TARGET_THUMB ) { * total = thumb1_rtx_costs ( x , code , outer_code ) ; return true ; } switch ( code ) { case MULT : if ( GET_MODE_CLASS ( mode ) == MODE_FLOAT || mode == DImode ) { * total = COSTS_N_INSNS ( 20 ) ; return false ; } if ( CONST_INT_P ( XEXP ( x , 1 ) ) ) { unsigned HOST_WIDE_INT i = ( INTVAL ( XEXP ( x , 1 ) ) & ( unsigned HOST_WIDE_INT ) 0xffffffff ) ; int cost , const_ok = const_ok_for_arm ( i ) ; int j , booth_unit_size ; cost = const_ok ? 4 : 8 ; booth_unit_size = 2 ; for ( j = 0 ; i && j < 32 ; j += booth_unit_size ) { i >>= booth_unit_size ; cost ++ ; } * total = COSTS_N_INSNS ( cost ) ; * total += rtx_cost ( XEXP ( x , 0 ) , code , 0 , speed ) ; return true ; } * total = COSTS_N_INSNS ( 20 ) ; return false ; default : return arm_rtx_costs_1 ( x , outer_code , total , speed ) ; ; } } </s>

RTX costs for cores with a slow MUL implementation . Thumb-2 is not supported on any `` slowmul '' cores , so it can be ignored .


<s> void arm_split_atomic_op ( enum rtx_code code , rtx old_out , rtx new_out , rtx mem , rtx value , rtx model_rtx , rtx cond ) { enum memmodel model = ( enum memmodel ) INTVAL ( model_rtx ) ; machine_mode mode = GET_MODE ( mem ) ; machine_mode wmode = ( mode == DImode ? DImode : SImode ) ; rtx_code_label * label ; rtx x ; bool use_acquire = TARGET_HAVE_LDACQ && ! ( model == MEMMODEL_RELAXED || model == MEMMODEL_CONSUME || model == MEMMODEL_RELEASE ) ; bool use_release = TARGET_HAVE_LDACQ && ! ( model == MEMMODEL_RELAXED || model == MEMMODEL_CONSUME || model == MEMMODEL_ACQUIRE ) ; if ( ! ( use_acquire || use_release ) ) arm_pre_atomic_barrier ( model ) ; label = gen_label_rtx ( ) ; emit_label ( label ) ; if ( new_out ) new_out = gen_lowpart ( wmode , new_out ) ; if ( old_out ) old_out = gen_lowpart ( wmode , old_out ) ; else old_out = new_out ; value = simplify_gen_subreg ( wmode , value , mode , 0 ) ; arm_emit_load_exclusive ( mode , old_out , mem , use_acquire ) ; switch ( code ) { case SET : new_out = value ; break ; case NOT : x = gen_rtx_AND ( wmode , old_out , value ) ; emit_insn ( gen_rtx_SET ( VOIDmode , new_out , x ) ) ; x = gen_rtx_NOT ( wmode , new_out ) ; emit_insn ( gen_rtx_SET ( VOIDmode , new_out , x ) ) ; break ; case MINUS : if ( CONST_INT_P ( value ) ) { value = GEN_INT ( - INTVAL ( value ) ) ; code = PLUS ; } case PLUS : if ( mode == DImode ) { emit_move_insn ( new_out , old_out ) ; if ( code == PLUS ) x = gen_adddi3 ( new_out , new_out , value ) ; else x = gen_subdi3 ( new_out , new_out , value ) ; emit_insn ( x ) ; break ; } default : x = gen_rtx_fmt_ee ( code , wmode , old_out , value ) ; emit_insn ( gen_rtx_SET ( VOIDmode , new_out , x ) ) ; break ; } arm_emit_store_exclusive ( mode , cond , mem , gen_lowpart ( mode , new_out ) , use_release ) ; x = gen_rtx_NE ( VOIDmode , cond , const0_rtx ) ; emit_unlikely_jump ( gen_cbranchsi4 ( x , cond , const0_rtx , label ) ) ; if ( ! ( use_acquire || use_release ) ) arm_post_atomic_barrier ( model ) ; } </s>

Split an atomic operation pattern . Operation is given by CODE and is one of PLUS , MINUS , IOR , XOR , SET ( for an exchange operation ) or NOT ( for a nand operation ) . Operation is performed on the content at MEM and on VALUE following the memory model MODEL_RTX . The content at MEM before and after the operation is returned in OLD_OUT and NEW_OUT respectively while the success of the operation is returned in COND . Using a scratch register or an operand register for these determines what result is returned for that pattern .


<s> void arm_split_compare_and_swap ( rtx operands [ ] ) { rtx rval , mem , oldval , newval , scratch ; machine_mode mode ; enum memmodel mod_s , mod_f ; bool is_weak ; rtx_code_label * label1 , * label2 ; rtx x , cond ; rval = operands [ 0 ] ; mem = operands [ 1 ] ; oldval = operands [ 2 ] ; newval = operands [ 3 ] ; is_weak = ( operands [ 4 ] != const0_rtx ) ; mod_s = ( enum memmodel ) INTVAL ( operands [ 5 ] ) ; mod_f = ( enum memmodel ) INTVAL ( operands [ 6 ] ) ; scratch = operands [ 7 ] ; mode = GET_MODE ( mem ) ; bool use_acquire = TARGET_HAVE_LDACQ && ! ( mod_s == MEMMODEL_RELAXED || mod_s == MEMMODEL_CONSUME || mod_s == MEMMODEL_RELEASE ) ; bool use_release = TARGET_HAVE_LDACQ && ! ( mod_s == MEMMODEL_RELAXED || mod_s == MEMMODEL_CONSUME || mod_s == MEMMODEL_ACQUIRE ) ; if ( ! ( use_acquire || use_release ) ) arm_pre_atomic_barrier ( mod_s ) ; label1 = NULL ; if ( ! is_weak ) { label1 = gen_label_rtx ( ) ; emit_label ( label1 ) ; } label2 = gen_label_rtx ( ) ; arm_emit_load_exclusive ( mode , rval , mem , use_acquire ) ; cond = arm_gen_compare_reg ( NE , rval , oldval , scratch ) ; x = gen_rtx_NE ( VOIDmode , cond , const0_rtx ) ; x = gen_rtx_IF_THEN_ELSE ( VOIDmode , x , gen_rtx_LABEL_REF ( Pmode , label2 ) , pc_rtx ) ; emit_unlikely_jump ( gen_rtx_SET ( VOIDmode , pc_rtx , x ) ) ; arm_emit_store_exclusive ( mode , scratch , mem , newval , use_release ) ; cond = gen_rtx_REG ( CCmode , CC_REGNUM ) ; x = gen_rtx_COMPARE ( CCmode , scratch , const0_rtx ) ; emit_insn ( gen_rtx_SET ( VOIDmode , cond , x ) ) ; if ( ! is_weak ) { x = gen_rtx_NE ( VOIDmode , cond , const0_rtx ) ; x = gen_rtx_IF_THEN_ELSE ( VOIDmode , x , gen_rtx_LABEL_REF ( Pmode , label1 ) , pc_rtx ) ; emit_unlikely_jump ( gen_rtx_SET ( VOIDmode , pc_rtx , x ) ) ; } if ( mod_f != MEMMODEL_RELAXED ) emit_label ( label2 ) ; if ( ! ( use_acquire || use_release ) ) arm_post_atomic_barrier ( mod_s ) ; if ( mod_f == MEMMODEL_RELAXED ) emit_label ( label2 ) ; } </s>

Split a compare and swap pattern . It is IMPLEMENTATION DEFINED whether another memory store between the load-exclusive and store-exclusive can reset the monitor from Exclusive to Open state . This means we must wait until after reload to split the pattern , lest we get a register spill in the middle of the atomic sequence .


<s> bool arm_tls_referenced_p ( rtx x ) { if ( ! TARGET_HAVE_TLS ) return false ; subrtx_iterator :: array_type array ; FOR_EACH_SUBRTX ( iter , array , x , ALL ) { const_rtx x = * iter ; if ( GET_CODE ( x ) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL ( x ) != 0 ) return true ; if ( GET_CODE ( x ) == UNSPEC && XINT ( x , 1 ) == UNSPEC_TLS ) iter . skip_subrtxes ( ) ; } return false ; } </s>

Return TRUE if X contains any TLS symbol references .


<s> static rtx arm_trampoline_adjust_address ( rtx addr ) { if ( TARGET_THUMB ) addr = expand_simple_binop ( Pmode , IOR , addr , const1_rtx , NULL , 0 , OPTAB_LIB_WIDEN ) ; return addr ; } </s>

Thumb trampolines should be entered in thumb mode , so set the bottom bit of the address .


<s> static void arm_trampoline_init ( rtx m_tramp , tree fndecl , rtx chain_value ) { rtx fnaddr , mem , a_tramp ; emit_block_move ( m_tramp , assemble_trampoline_template ( ) , GEN_INT ( TRAMPOLINE_SIZE ) , BLOCK_OP_NORMAL ) ; mem = adjust_address ( m_tramp , SImode , TARGET_32BIT ? 8 : 12 ) ; emit_move_insn ( mem , chain_value ) ; mem = adjust_address ( m_tramp , SImode , TARGET_32BIT ? 12 : 16 ) ; fnaddr = XEXP ( DECL_RTL ( fndecl ) , 0 ) ; emit_move_insn ( mem , fnaddr ) ; a_tramp = XEXP ( m_tramp , 0 ) ; emit_library_call ( gen_rtx_SYMBOL_REF ( Pmode , "__clear_cache" ) , LCT_NORMAL , VOIDmode , 2 , a_tramp , Pmode , plus_constant ( Pmode , a_tramp , TRAMPOLINE_SIZE ) , Pmode ) ; } </s>

Emit RTL insns to initialize the variable parts of a trampoline .


<s> bool arm_validize_comparison ( rtx * comparison , rtx * op1 , rtx * op2 ) { enum rtx_code code = GET_CODE ( * comparison ) ; int code_int ; machine_mode mode = ( GET_MODE ( * op1 ) == VOIDmode ) ? GET_MODE ( * op2 ) : GET_MODE ( * op1 ) ; gcc_assert ( GET_MODE ( * op1 ) != VOIDmode || GET_MODE ( * op2 ) != VOIDmode ) ; if ( code == UNEQ || code == LTGT ) return false ; code_int = ( int ) code ; arm_canonicalize_comparison ( & code_int , op1 , op2 , 0 ) ; PUT_CODE ( * comparison , ( enum rtx_code ) code_int ) ; switch ( mode ) { case SImode : if ( ! arm_add_operand ( * op1 , mode ) ) * op1 = force_reg ( mode , * op1 ) ; if ( ! arm_add_operand ( * op2 , mode ) ) * op2 = force_reg ( mode , * op2 ) ; return true ; case DImode : if ( ! cmpdi_operand ( * op1 , mode ) ) * op1 = force_reg ( mode , * op1 ) ; if ( ! cmpdi_operand ( * op2 , mode ) ) * op2 = force_reg ( mode , * op2 ) ; return true ; case SFmode : case DFmode : if ( ! arm_float_compare_operand ( * op1 , mode ) ) * op1 = force_reg ( mode , * op1 ) ; if ( ! arm_float_compare_operand ( * op2 , mode ) ) * op2 = force_reg ( mode , * op2 ) ; return true ; default : break ; } return false ; } </s>

Returns true if a valid comparison operation and makes the operands in a form that is valid .


<s> static bool arm_vectorize_vec_perm_const_ok ( machine_mode vmode , const unsigned char * sel ) { struct expand_vec_perm_d d ; unsigned int i , nelt , which ; bool ret ; d . vmode = vmode ; d . nelt = nelt = GET_MODE_NUNITS ( d . vmode ) ; d . testing_p = true ; memcpy ( d . perm , sel , nelt ) ; for ( i = which = 0 ; i < nelt ; ++ i ) { unsigned char e = d . perm [ i ] ; gcc_assert ( e < 2 * nelt ) ; which |= ( e < nelt ? 1 : 2 ) ; } if ( which == 2 ) for ( i = 0 ; i < nelt ; ++ i ) d . perm [ i ] -= nelt ; d . one_vector_p = ( which != 3 ) ; d . target = gen_raw_REG ( d . vmode , LAST_VIRTUAL_REGISTER + 1 ) ; d . op1 = d . op0 = gen_raw_REG ( d . vmode , LAST_VIRTUAL_REGISTER + 2 ) ; if ( ! d . one_vector_p ) d . op1 = gen_raw_REG ( d . vmode , LAST_VIRTUAL_REGISTER + 3 ) ; start_sequence ( ) ; ret = arm_expand_vec_perm_const_1 ( & d ) ; end_sequence ( ) ; return ret ; } </s>


<s> bool arm_vector_mode_supported_p ( machine_mode mode ) { if ( TARGET_NEON && ( mode == V2SFmode || mode == V4SImode || mode == V8HImode || mode == V4HFmode || mode == V16QImode || mode == V4SFmode || mode == V2DImode ) ) return true ; if ( ( TARGET_NEON || TARGET_IWMMXT ) && ( ( mode == V2SImode ) || ( mode == V4HImode ) || ( mode == V8QImode ) ) ) return true ; if ( TARGET_INT_SIMD && ( mode == V4UQQmode || mode == V4QQmode || mode == V2UHQmode || mode == V2HQmode || mode == V2UHAmode || mode == V2HAmode ) ) return true ; return false ; } </s>

Implements target hook vector_mode_supported_p .


<s> static bool arm_xscale_rtx_costs ( rtx x , enum rtx_code code , enum rtx_code outer_code , int * total , bool speed ) { machine_mode mode = GET_MODE ( x ) ; if ( TARGET_THUMB ) { * total = thumb1_rtx_costs ( x , code , outer_code ) ; return true ; } switch ( code ) { case COMPARE : if ( GET_CODE ( XEXP ( x , 0 ) ) != MULT ) return arm_rtx_costs_1 ( x , outer_code , total , speed ) ; * total = COSTS_N_INSNS ( 3 ) ; return false ; case MULT : if ( mode == DImode && ( GET_CODE ( XEXP ( x , 0 ) ) == GET_CODE ( XEXP ( x , 1 ) ) ) && ( GET_CODE ( XEXP ( x , 0 ) ) == ZERO_EXTEND || GET_CODE ( XEXP ( x , 0 ) ) == SIGN_EXTEND ) ) { * total = COSTS_N_INSNS ( 2 ) ; return false ; } if ( mode == DImode ) { * total = COSTS_N_INSNS ( 5 ) ; return false ; } if ( CONST_INT_P ( XEXP ( x , 1 ) ) ) { unsigned HOST_WIDE_INT i = INTVAL ( XEXP ( x , 1 ) ) ; int cost = 1 ; unsigned HOST_WIDE_INT masked_const ; if ( i & 0x80000000 ) i = ~ i ; i &= ( unsigned HOST_WIDE_INT ) 0xffffffff ; masked_const = i & 0xffff8000 ; if ( masked_const != 0 ) { cost ++ ; masked_const = i & 0xf8000000 ; if ( masked_const != 0 ) cost ++ ; } * total = COSTS_N_INSNS ( cost ) ; return false ; } if ( mode == SImode ) { * total = COSTS_N_INSNS ( 3 ) ; return false ; } * total = COSTS_N_INSNS ( 20 ) ; return false ; default : return arm_rtx_costs_1 ( x , outer_code , total , speed ) ; } } </s>

RTX cost for XScale CPUs . Thumb-2 is not supported on any xscale cores , so it can be ignored .


<s> static void bounds_check ( rtx operand , HOST_WIDE_INT low , HOST_WIDE_INT high , const char * err ) { HOST_WIDE_INT lane ; gcc_assert ( CONST_INT_P ( operand ) ) ; lane = INTVAL ( operand ) ; if ( lane < low || lane >= high ) error ( err ) ; } </s>

Ensure OPERAND lies between LOW ( inclusive ) and HIGH ( exclusive ) . Raise ERR if it does n't . FIXME : NEON bounds checks occur late in compilation , so reported source locations are bogus .