<s> void m32c_register_pragmas ( void ) { c_register_pragma ( "GCC" , "memregs" , m32c_pragma_memregs ) ; } </s>


<s> void m32c_register_pragmas ( void ) { c_register_pragma ( "GCC" , "memregs" , m32c_pragma_memregs ) ; c_register_pragma ( NULL , "ADDRESS" , m32c_pragma_address ) ; c_register_pragma ( NULL , "address" , m32c_pragma_address ) ; if ( TARGET_A16 ) c_register_addr_space ( "__far" , ADDR_SPACE_FAR ) ; else c_register_addr_space ( "__far" , ADDR_SPACE_GENERIC ) ; } </s>


<s> static int classes_intersect ( int class1 , int class2 ) { return class_contents [ class1 ] [ 0 ] & class_contents [ class2 ] [ 0 ] ; } </s>

Returns TRUE If there are any registers that exist in both register classes .


<s>
static
int
class_can_hold_mode
(
int
class
,
enum
machine_mode
mode
)
{
static
char
results
[
LIM_REG_CLASSES
]
[
MAX_MACHINE_MODE
]
;
if
(
results
[
class
]
[
mode
]
==
0
)
{
int
r
,
n
,
i
;
results
[
class
]
[
mode
]
=
1
;
for
(
r
=
0
;
r
<
FIRST_PSEUDO_REGISTER
;
r
++
)
if
(
class_contents
[
class
]
[
0
]
&
(
1
<<
r
)
&&
HARD_REGNO_MODE_OK
(
r
,
mode
)
)
{
int
ok
=
1
;
n
=
HARD_REGNO_NREGS
(
r
,
mode
)
;
for
(
i
=
1
;
i
<
n
;
i
++
)
if
(
!
(
class_contents
[
class
]
[
0
]
&
(
1
<<
(
r
+
i
)
)
)
)
ok
=
0
;
if
(
ok
)
{
results
[
class
]
[
mode
]
=
2
;
break
;
}
}
}
fprintf
(
stderr
,
"class %s can hold %s? %s\n"
,
class_names
[
class
]
,
mode_name
[
mode
]
,
(
results
[
class
]
[
mode
]
==
2
)
"yes"
:
"no"
)
;
return
results
[
class
]
[
mode
]
==
2
;
}
</s>

Used by m32c_register_move_cost to determine if a move is impossibly expensive .


<s>
int
m32c_cannot_change_mode_class
(
enum
machine_mode
from
,
enum
machine_mode
to
,
int
rclass
)
{
fprintf
(
stderr
,
"cannot change from %s to %s in %s\n"
,
mode_name
[
from
]
,
mode_name
[
to
]
,
class_names
[
rclass
]
)
;
if
(
to
==
QImode
)
return
(
class_contents
[
rclass
]
[
0
]
&
0x1ffa
)
;
if
(
class_contents
[
rclass
]
[
0
]
&
0x0005
&&
GET_MODE_SIZE
(
from
)
>
1
)
return
0
;
if
(
GET_MODE_SIZE
(
from
)
>
2
)
return
0
;
return
1
;
}
</s>

Implements CANNOT_CHANGE_MODE_CLASS . Only r0 and r1 can change to QI ( r0l , r1l ) because the chip does n't support QI ops on other registers ( well , it does on a0/a1 but if we let gcc do that , reload suffers ) . Otherwise , we allow changes to larger modes .


<s> int m32c_class_likely_spilled_p ( int regclass ) { if ( regclass == A_REGS ) return 1 ; return reg_class_size [ regclass ] == 1 ; } </s>

Implements CLASS_LIKELY_SPILLED_P . A_REGS is needed for address reloads .


<s> int m32c_class_max_nregs ( int regclass , enum machine_mode mode ) { int rn , max = 0 ; for ( rn = 0 ; rn < FIRST_PSEUDO_REGISTER ; rn ++ ) if ( class_contents [ regclass ] [ 0 ] & ( 1 << rn ) ) { int n = m32c_hard_regno_nregs ( rn , mode ) ; if ( max < n ) max = n ; } return max ; } </s>

Implements CLASS_MAX_NREGS . We calculate this according to its documented meaning , to avoid potential inconsistencies with actual class definitions .


<s> void m32c_conditional_register_usage ( void ) { int memregs ; int i ; if ( 0 <= target_memregs && target_memregs <= 16 ) { for ( i = target_memregs / 2 ; i < 8 ; i ++ ) { fixed_regs [ MEM0_REGNO + i ] = 1 ; CLEAR_HARD_REG_BIT ( reg_class_contents [ MEM_REGS ] , MEM0_REGNO + i ) ; } } if ( TARGET_A24 ) { call_used_regs [ R1_REGNO ] = 0 ; call_used_regs [ R2_REGNO ] = 0 ; call_used_regs [ R3_REGNO ] = 0 ; call_used_regs [ A0_REGNO ] = 0 ; call_used_regs [ A1_REGNO ] = 0 ; } } </s>

Implements CONDITIONAL_REGISTER_USAGE . We adjust the number of available memregs , and select which registers need to be preserved across calls based on the chip family .


<s> int m32c_const_ok_for_constraint_p ( HOST_WIDE_INT value , char c ATTRIBUTE_UNUSED , const char * str ) { if ( memcmp ( str , "Is3" , 3 ) == 0 ) { return ( - 8 <= value && value <= 7 ) ; } if ( memcmp ( str , "IS1" , 3 ) == 0 ) { return ( - 128 <= value && value <= 127 ) ; } if ( memcmp ( str , "IS2" , 3 ) == 0 ) { return ( - 32768 <= value && value <= 32767 ) ; } if ( memcmp ( str , "IU2" , 3 ) == 0 ) { return ( 0 <= value && value <= 65535 ) ; } if ( memcmp ( str , "IU3" , 3 ) == 0 ) { return ( 0 <= value && value <= 0x00ffffff ) ; } if ( memcmp ( str , "In4" , 3 ) == 0 ) { return ( - 8 <= value && value && value <= 8 ) ; } if ( memcmp ( str , "In5" , 3 ) == 0 ) { return ( - 16 <= value && value && value <= 16 ) ; } if ( memcmp ( str , "IM2" , 3 ) == 0 ) { return ( - 65536 <= value && value && value <= - 1 ) ; } if ( memcmp ( str , "Ilb" , 3 ) == 0 ) { int b = exact_log2 ( value ) ; return ( b >= 1 && b <= 8 ) ; } if ( memcmp ( str , "Ilw" , 3 ) == 0 ) { int b = exact_log2 ( value ) ; return ( b >= 1 && b <= 16 ) ; } return 0 ; } </s>

Implements CONST_OK_FOR_CONSTRAINT_P . Currently , all constant constraints start with ' I ' , with the next two characters indicating the type and size of the range allowed .


<s> int m32c_eh_return_data_regno ( int n ) { switch ( n ) { case 0 : return A0_REGNO ; case 1 : return A1_REGNO ; default : return INVALID_REGNUM ; } } </s>

Implements EH_RETURN_DATA_REGNO . Choose registers able to hold pointers .


<s> rtx m32c_eh_return_stackadj_rtx ( void ) { if ( ! cfun -> machine -> eh_stack_adjust ) { rtx sa ; sa = gen_reg_rtx ( Pmode ) ; cfun -> machine -> eh_stack_adjust = sa ; } return cfun -> machine -> eh_stack_adjust ; } </s>

Implements EH_RETURN_STACKADJ_RTX . Saved and used later in m32c_emit_eh_epilogue .


<s> void m32c_emit_epilogue ( void ) { if ( m32c_pushm_popm ( PP_justcount ) > 0 || cfun -> machine -> is_interrupt ) emit_insn ( gen_epilogue_start ( ) ) ; m32c_pushm_popm ( PP_popm ) ; if ( cfun -> machine -> is_interrupt ) { enum machine_mode spmode = TARGET_A16 ? HImode : PSImode ; emit_move_insn ( gen_rtx_REG ( spmode , A0_REGNO ) , gen_rtx_REG ( spmode , FP_REGNO ) ) ; emit_move_insn ( gen_rtx_REG ( spmode , SP_REGNO ) , gen_rtx_REG ( spmode , A0_REGNO ) ) ; if ( TARGET_A16 ) emit_insn ( gen_pophi_16 ( gen_rtx_REG ( HImode , FP_REGNO ) ) ) ; else emit_insn ( gen_poppsi ( gen_rtx_REG ( PSImode , FP_REGNO ) ) ) ; emit_insn ( gen_popm ( GEN_INT ( cfun -> machine -> intr_pushm ) ) ) ; emit_jump_insn ( gen_epilogue_reit ( GEN_INT ( TARGET_A16 ? 4 : 6 ) ) ) ; } else if ( cfun -> machine -> use_rts ) emit_jump_insn ( gen_epilogue_rts ( ) ) ; else emit_jump_insn ( gen_epilogue_exitd ( GEN_INT ( TARGET_A16 ? 2 : 4 ) ) ) ; emit_barrier ( ) ; } </s>

Likewise , for the epilogue . The only exception is that , for interrupts , we must manually unwind the frame as the REIT opcode does n't do that .


<s> void m32c_emit_prologue ( void ) { int frame_size , extra_frame_size = 0 , reg_save_size ; int complex_prologue = 0 ; cfun -> machine -> is_leaf = m32c_leaf_function_p ( ) ; if ( interrupt_p ( cfun -> decl ) ) { cfun -> machine -> is_interrupt = 1 ; complex_prologue = 1 ; } reg_save_size = m32c_pushm_popm ( PP_justcount ) ; if ( interrupt_p ( cfun -> decl ) ) emit_insn ( gen_pushm ( GEN_INT ( cfun -> machine -> intr_pushm ) ) ) ; frame_size = m32c_initial_elimination_offset ( FB_REGNO , SP_REGNO ) - reg_save_size ; if ( frame_size == 0 && ! cfun -> machine -> is_interrupt && ! m32c_function_needs_enter ( ) ) cfun -> machine -> use_rts = 1 ; if ( frame_size > 254 ) { extra_frame_size = frame_size - 254 ; frame_size = 254 ; } if ( cfun -> machine -> use_rts == 0 ) F ( emit_insn ( m32c_all_frame_related ( TARGET_A16 ? gen_prologue_enter_16 ( GEN_INT ( frame_size ) ) : gen_prologue_enter_24 ( GEN_INT ( frame_size ) ) ) ) ) ; if ( extra_frame_size ) { complex_prologue = 1 ; if ( TARGET_A16 ) F ( emit_insn ( gen_addhi3 ( gen_rtx_REG ( HImode , SP_REGNO ) , gen_rtx_REG ( HImode , SP_REGNO ) , GEN_INT ( - extra_frame_size ) ) ) ) ; else F ( emit_insn ( gen_addpsi3 ( gen_rtx_REG ( PSImode , SP_REGNO ) , gen_rtx_REG ( PSImode , SP_REGNO ) , GEN_INT ( - extra_frame_size ) ) ) ) ; } complex_prologue += m32c_pushm_popm ( PP_pushm ) ; if ( complex_prologue ) emit_insn ( gen_prologue_end ( ) ) ; } </s>

Emits the prologue . See the frame layout comment earlier in this file . We can reserve up to 256 bytes with the ENTER opcode , beyond that we manually update sp .


<s> int m32c_extra_address_constraint ( char c , const char * str ATTRIBUTE_UNUSED ) { return c == 'A' ; } </s>

Implements EXTRA_ADDRESS_CONSTRAINT . We reserve ' A ' strings for these , but do n't currently define any .


<s>
int
m32c_extra_constraint_p
(
rtx
value
,
char
c
,
const
char
*
str
)
{
int
rv
=
m32c_extra_constraint_p2
(
value
,
c
,
str
)
;
fprintf
(
stderr
,
"\nconstraint %.*s: %d\n"
,
CONSTRAINT_LEN
(
c
,
str
)
,
str
,
rv
)
;
debug_rtx
(
value
)
;
return
rv
;
}
</s>

This is for when we 're debugging the above .


<s> int m32c_extra_constraint_p2 ( rtx value , char c ATTRIBUTE_UNUSED , const char * str ) { encode_pattern ( value ) ; if ( memcmp ( str , "Sd" , 2 ) == 0 ) { rtx r ; if ( GET_CODE ( value ) == MEM && CONSTANT_P ( XEXP ( value , 0 ) ) ) return 1 ; if ( RTX_IS ( "ms" ) || RTX_IS ( "m+si" ) ) return 1 ; if ( RTX_IS ( "mr" ) ) r = patternr [ 1 ] ; else if ( RTX_IS ( "m+ri" ) || RTX_IS ( "m+rs" ) || RTX_IS ( "m+r+si" ) ) r = patternr [ 2 ] ; else return 0 ; if ( REGNO ( r ) == SP_REGNO ) return 0 ; return m32c_legitimate_address_p ( GET_MODE ( value ) , XEXP ( value , 0 ) , 1 ) ; } else if ( memcmp ( str , "Sa" , 2 ) == 0 ) { rtx r ; if ( RTX_IS ( "mr" ) ) r = patternr [ 1 ] ; else if ( RTX_IS ( "m+ri" ) ) r = patternr [ 2 ] ; else return 0 ; return ( IS_REG ( r , A0_REGNO ) || IS_REG ( r , A1_REGNO ) ) ; } else if ( memcmp ( str , "Si" , 2 ) == 0 ) { return ( RTX_IS ( "mi" ) || RTX_IS ( "ms" ) || RTX_IS ( "m+si" ) ) ; } else if ( memcmp ( str , "Ss" , 2 ) == 0 ) { return ( ( RTX_IS ( "mr" ) && ( IS_REG ( patternr [ 1 ] , SP_REGNO ) ) ) || ( RTX_IS ( "m+ri" ) && ( IS_REG ( patternr [ 2 ] , SP_REGNO ) ) ) ) ; } else if ( memcmp ( str , "Sf" , 2 ) == 0 ) { return ( ( RTX_IS ( "mr" ) && ( IS_REG ( patternr [ 1 ] , FB_REGNO ) ) ) || ( RTX_IS ( "m+ri" ) && ( IS_REG ( patternr [ 2 ] , FB_REGNO ) ) ) ) ; } else if ( memcmp ( str , "Sb" , 2 ) == 0 ) { return ( ( RTX_IS ( "mr" ) && ( IS_REG ( patternr [ 1 ] , SB_REGNO ) ) ) || ( RTX_IS ( "m+ri" ) && ( IS_REG ( patternr [ 2 ] , SB_REGNO ) ) ) ) ; } else if ( memcmp ( str , "S1" , 2 ) == 0 ) { return r1h_operand ( value , QImode ) ; } gcc_assert ( str [ 0 ] != 'S' ) ; if ( memcmp ( str , "Rpa" , 2 ) == 0 ) return GET_CODE ( value ) == PARALLEL ; return 0 ; } </s>

Implements EXTRA_CONSTRAINT_STR ( see next function too ) . 'S ' is for memory constraints , plus `` Rpa '' for PARALLEL rtx 's we use for call return values .


<s> int m32c_extra_memory_constraint ( char c , const char * str ATTRIBUTE_UNUSED ) { return c == 'S' ; } </s>

Implements EXTRA_MEMORY_CONSTRAINT . Currently , we only use strings starting with 'S ' .


<s>
rtx
m32c_function_arg
(
CUMULATIVE_ARGS
*
ca
,
enum
machine_mode
mode
,
tree
type
,
int
named
)
{
rtx
rv
=
NULL_RTX
;
fprintf
(
stderr
,
"func_arg %d (%s, %d)\n"
,
ca
->
parm_num
,
mode_name
[
mode
]
,
named
)
;
debug_tree
(
type
)
;
if
(
mode
==
VOIDmode
)
return
GEN_INT
(
0
)
;
if
(
ca
->
force_mem
||
!
named
)
{
fprintf
(
stderr
,
"func arg: force %d named %d, mem\n"
,
ca
->
force_mem
,
named
)
;
return
NULL_RTX
;
}
if
(
type
&&
INTEGRAL_TYPE_P
(
type
)
&&
POINTER_TYPE_P
(
type
)
)
return
NULL_RTX
;
switch
(
ca
->
parm_num
)
{
case
1
:
if
(
GET_MODE_SIZE
(
mode
)
==
1
||
GET_MODE_SIZE
(
mode
)
==
2
)
rv
=
gen_rtx_REG
(
mode
,
TARGET_A16
R1_REGNO
:
R0_REGNO
)
;
break
;
case
2
:
if
(
TARGET_A16
&&
GET_MODE_SIZE
(
mode
)
==
2
)
rv
=
gen_rtx_REG
(
mode
,
R2_REGNO
)
;
break
;
}
debug_rtx
(
rv
)
;
return
rv
;
}
</s>

Implements FUNCTION_ARG . Arguments are passed partly in registers , partly on stack . If our function returns a struct , a pointer to a buffer for it is at the top of the stack ( last thing pushed ) . The first few real arguments may be in registers as follows : R8C/M16C : arg1 in r1 if it 's QI or HI ( else it 's pushed on stack ) arg2 in r2 if it 's HI ( else pushed on stack ) rest on stack M32C : arg1 in r0 if it 's QI or HI ( else it 's pushed on stack ) rest on stack Structs are not passed in registers , even if they fit . Only integer and pointer types are passed in registers . Note that when arg1 does n't fit in r1 , arg2 may still be passed in r2 if it fits .


<s> void m32c_function_arg_advance ( CUMULATIVE_ARGS * ca , enum machine_mode mode ATTRIBUTE_UNUSED , tree type ATTRIBUTE_UNUSED , int named ATTRIBUTE_UNUSED ) { if ( ca -> force_mem ) ca -> force_mem = 0 ; ca -> parm_num ++ ; } </s>

Implements FUNCTION_ARG_ADVANCE . force_mem is set for functions returning structures , so we always reset that . Otherwise , we only need to know the sequence number of the argument to know what to do with it .


<s> static bool m32c_function_needs_enter ( void ) { rtx insn ; struct sequence_stack * seq ; rtx sp = gen_rtx_REG ( Pmode , SP_REGNO ) ; rtx fb = gen_rtx_REG ( Pmode , FB_REGNO ) ; insn = get_insns ( ) ; for ( seq = cfun -> emit -> sequence_stack ; seq ; insn = seq -> first , seq = seq -> next ) ; while ( insn ) { if ( reg_mentioned_p ( sp , insn ) ) return true ; if ( reg_mentioned_p ( fb , insn ) ) return true ; insn = NEXT_INSN ( insn ) ; } return false ; } </s>

Returns TRUE if the current function needs to use the ENTER/EXIT opcodes . If the function does n't need the frame base or stack pointer , it can use the simpler RTS opcode .


<s> rtx m32c_function_value ( tree valtype , tree func ATTRIBUTE_UNUSED ) { enum machine_mode mode = TYPE_MODE ( valtype ) ; return m32c_libcall_value ( mode ) ; } </s>

Implements FUNCTION_VALUE . Functions and libcalls have the same conventions .


<s> int m32c_hard_regno_nregs ( int regno , enum machine_mode mode ) { if ( regno == FLG_REGNO && mode == CCmode ) return 1 ; if ( regno >= FIRST_PSEUDO_REGISTER ) return ( ( GET_MODE_SIZE ( mode ) + UNITS_PER_WORD - 1 ) / UNITS_PER_WORD ) ; if ( regno >= MEM0_REGNO && regno <= MEM7_REGNO ) return ( GET_MODE_SIZE ( mode ) + 1 ) / 2 ; if ( GET_MODE_SIZE ( mode ) <= 1 ) return nregs_table [ regno ] . qi_regs ; if ( GET_MODE_SIZE ( mode ) <= 2 ) return nregs_table [ regno ] . hi_regs ; if ( regno == A0_REGNO && mode == PSImode && TARGET_A16 ) return 2 ; if ( ( GET_MODE_SIZE ( mode ) <= 3 || mode == PSImode ) && TARGET_A24 ) return nregs_table [ regno ] . pi_regs ; if ( GET_MODE_SIZE ( mode ) <= 4 ) return nregs_table [ regno ] . si_regs ; if ( GET_MODE_SIZE ( mode ) <= 8 ) return nregs_table [ regno ] . di_regs ; return 0 ; } </s>

Implements HARD_REGNO_NREGS . This is complicated by the fact that different registers are different sizes from each other , * and * may be different sizes in different chip families .


<s> int m32c_hard_regno_ok ( int regno , enum machine_mode mode ) { return m32c_hard_regno_nregs ( regno , mode ) != 0 ; } </s>

Implements HARD_REGNO_MODE_OK . The above function does the work already ; just test its return value .


<s> void m32c_initialize_trampoline ( rtx tramp , rtx function , rtx chainval ) { if ( TARGET_A16 ) { emit_move_insn ( A0 ( HImode , 0 ) , GEN_INT ( 0xc475 - 0x10000 ) ) ; emit_move_insn ( A0 ( HImode , 2 ) , chainval ) ; emit_move_insn ( A0 ( QImode , 4 ) , GEN_INT ( 0xfc - 0x100 ) ) ; emit_move_insn ( A0 ( HImode , 5 ) , function ) ; emit_move_insn ( A0 ( QImode , 7 ) , GEN_INT ( 0x00 ) ) ; } else { emit_move_insn ( A0 ( QImode , 0 ) , GEN_INT ( 0xbc - 0x100 ) ) ; emit_move_insn ( A0 ( PSImode , 1 ) , chainval ) ; emit_move_insn ( A0 ( QImode , 4 ) , GEN_INT ( 0xcc - 0x100 ) ) ; emit_move_insn ( A0 ( PSImode , 5 ) , function ) ; } } </s>


<s> void m32c_init_cumulative_args ( CUMULATIVE_ARGS * ca , tree fntype ATTRIBUTE_UNUSED , rtx libname ATTRIBUTE_UNUSED , tree fndecl ATTRIBUTE_UNUSED , int n_named_args ATTRIBUTE_UNUSED ) { ca -> force_mem = 0 ; ca -> parm_num = 1 ; } </s>


<s> static struct machine_function * m32c_init_machine_status ( void ) { struct machine_function * machine ; machine = ( machine_function * ) ggc_alloc_cleared ( sizeof ( machine_function ) ) ; return machine ; } </s>

The usual ; we set up our machine_function data .


<s> static int m32c_leaf_function_p ( void ) { rtx saved_first , saved_last ; struct sequence_stack * seq ; int rv ; saved_first = cfun -> emit -> x_first_insn ; saved_last = cfun -> emit -> x_last_insn ; for ( seq = cfun -> emit -> sequence_stack ; seq && seq -> next ; seq = seq -> next ) ; if ( seq ) { cfun -> emit -> x_first_insn = seq -> first ; cfun -> emit -> x_last_insn = seq -> last ; } rv = leaf_function_p ( ) ; cfun -> emit -> x_first_insn = saved_first ; cfun -> emit -> x_last_insn = saved_last ; return rv ; } </s>

Returns TRUE if the current function is a leaf , and thus we can determine which registers an interrupt function really needs to save . The logic below is mostly about finding the insn sequence that 's the function , versus any sequence that might be open for the current insn .


<s>
int
m32c_legitimate_address_p
(
enum
machine_mode
mode
,
rtx
x
,
int
strict
)
{
int
mode_adjust
;
if
(
CONSTANT_P
(
x
)
)
return
1
;
mode_adjust
=
GET_MODE_SIZE
(
mode
)
1
;
if
(
GET_CODE
(
x
)
==
PRE_DEC
||
GET_CODE
(
x
)
==
POST_INC
||
GET_CODE
(
x
)
==
PRE_MODIFY
)
{
return
(
GET_CODE
(
XEXP
(
x
,
0
)
)
==
REG
&&
REGNO
(
XEXP
(
x
,
0
)
)
==
SP_REGNO
)
;
}
if
(
TARGET_A24
&&
GET_CODE
(
x
)
==
MEM
&&
GET_CODE
(
XEXP
(
x
,
0
)
)
!=
PLUS
)
{
fprintf
(
stderr
,
"double indirect\n"
)
;
x
=
XEXP
(
x
,
0
)
;
}
encode_pattern
(
x
)
;
if
(
RTX_IS
(
"r"
)
)
{
switch
(
REGNO
(
patternr
[
0
]
)
)
{
case
A0_REGNO
:
case
A1_REGNO
:
case
SB_REGNO
:
case
FB_REGNO
:
case
SP_REGNO
:
return
1
;
default
:
if
(
IS_PSEUDO
(
patternr
[
0
]
,
strict
)
)
return
1
;
return
0
;
}
}
if
(
RTX_IS
(
"+ri"
)
)
{
int
rn
=
REGNO
(
patternr
[
1
]
)
;
HOST_WIDE_INT
offs
=
INTVAL
(
patternr
[
2
]
)
;
switch
(
rn
)
{
case
A0_REGNO
:
case
A1_REGNO
:
case
SB_REGNO
:
if
(
TARGET_A16
)
return
(
offs
>=
65536
&&
offs
<=
65535
mode_adjust
)
;
if
(
rn
==
SB_REGNO
)
return
(
offs
>=
0
&&
offs
<=
65535
mode_adjust
)
;
return
(
offs
>=
16777216
&&
offs
<=
16777215
)
;
case
FB_REGNO
:
if
(
TARGET_A16
)
return
(
offs
>=
128
&&
offs
<=
127
mode_adjust
)
;
return
(
offs
>=
65536
&&
offs
<=
65535
mode_adjust
)
;
case
SP_REGNO
:
return
(
offs
>=
128
&&
offs
<=
127
mode_adjust
)
;
default
:
if
(
IS_PSEUDO
(
patternr
[
1
]
,
strict
)
)
return
1
;
return
0
;
}
}
if
(
RTX_IS
(
"+rs"
)
||
RTX_IS
(
"+r+si"
)
)
{
rtx
reg
=
patternr
[
1
]
;
switch
(
REGNO
(
reg
)
)
{
case
A0_REGNO
:
case
A1_REGNO
:
return
1
;
default
:
if
(
IS_PSEUDO
(
reg
,
strict
)
)
return
1
;
return
0
;
}
}
return
0
;
}
</s>

Used by GO_IF_LEGITIMATE_ADDRESS . The r8c/m32c family supports a wide range of non-orthogonal addressing modes , including the ability to double-indirect on * some * of them . Not all insns support all modes , either , but we rely on predicates and constraints to deal with that .


<s> int m32c_legitimate_constant_p ( rtx x ATTRIBUTE_UNUSED ) { return 1 ; } </s>

Implements LEGITIMATE_CONSTANT_P . We split large constants anyway , so we can allow anything .


"\nm32c_legitimize_reload_address for mode %s\n"

Implements LEGITIMIZE_RELOAD_ADDRESS . See comment above .


<s> rtx m32c_libcall_value ( enum machine_mode mode ) { if ( mode == DFmode ) { rtx rv ; rv = gen_rtx_PARALLEL ( mode , rtvec_alloc ( 4 ) ) ; XVECEXP ( rv , 0 , 0 ) = gen_rtx_EXPR_LIST ( VOIDmode , gen_rtx_REG ( HImode , R0_REGNO ) , GEN_INT ( 0 ) ) ; XVECEXP ( rv , 0 , 1 ) = gen_rtx_EXPR_LIST ( VOIDmode , gen_rtx_REG ( HImode , R1_REGNO ) , GEN_INT ( 2 ) ) ; XVECEXP ( rv , 0 , 2 ) = gen_rtx_EXPR_LIST ( VOIDmode , gen_rtx_REG ( HImode , R2_REGNO ) , GEN_INT ( 4 ) ) ; XVECEXP ( rv , 0 , 3 ) = gen_rtx_EXPR_LIST ( VOIDmode , gen_rtx_REG ( HImode , R3_REGNO ) , GEN_INT ( 6 ) ) ; return rv ; } if ( TARGET_A24 && GET_MODE_SIZE ( mode ) > 2 ) { rtx rv ; rv = gen_rtx_PARALLEL ( mode , rtvec_alloc ( 1 ) ) ; XVECEXP ( rv , 0 , 0 ) = gen_rtx_EXPR_LIST ( VOIDmode , gen_rtx_REG ( mode , R0_REGNO ) , GEN_INT ( 0 ) ) ; return rv ; } if ( GET_MODE_SIZE ( mode ) > 2 ) return gen_rtx_REG ( mode , MEM0_REGNO ) ; return gen_rtx_REG ( mode , R0_REGNO ) ; } </s>

Implements LIBCALL_VALUE . Most values are returned in $ r0 , or some combination of registers starting there ( r2r0 for longs , r3r1r2r0 for long long , r3r2r1r0 for doubles ) , except that that ABI currently does n't work because it ends up using all available general registers and gcc often ca n't compile it . So , instead , we return anything bigger than 16 bits in `` mem0 '' ( effectively , a memory location ) .


<s>
int
m32c_limit_reload_class
(
enum
machine_mode
mode
,
int
rclass
)
{
fprintf
(
stderr
,
"limit_reload_class for %s: %s ->"
,
mode_name
[
mode
]
,
class_names
[
rclass
]
)
;
if
(
mode
==
QImode
)
rclass
=
reduce_class
(
rclass
,
HL_REGS
,
rclass
)
;
else
if
(
mode
==
HImode
)
rclass
=
reduce_class
(
rclass
,
HI_REGS
,
rclass
)
;
else
if
(
mode
==
SImode
)
rclass
=
reduce_class
(
rclass
,
SI_REGS
,
rclass
)
;
if
(
rclass
!=
A_REGS
)
rclass
=
reduce_class
(
rclass
,
DI_REGS
,
rclass
)
;
fprintf
(
stderr
,
" %s\n"
,
class_names
[
rclass
]
)
;
return
rclass
;
}
</s>

Implements LIMIT_RELOAD_CLASS . We basically want to avoid using address registers for reloads since they 're needed for address reloads .

m32c
"limit_reload_class for %s: %s ->"
" %s\n"


<s> int m32c_memory_move_cost ( enum machine_mode mode ATTRIBUTE_UNUSED , int reg_class ATTRIBUTE_UNUSED , int in ATTRIBUTE_UNUSED ) { return COSTS_N_INSNS ( 10 ) ; } </s>


<s> int m32c_modes_tieable_p ( enum machine_mode m1 , enum machine_mode m2 ) { if ( GET_MODE_SIZE ( m1 ) == GET_MODE_SIZE ( m2 ) ) return 1 ; if ( m1 == QImode || m2 == QImode ) return 0 ; return 1 ; } </s>

Implements MODES_TIEABLE_P . In general , modes are n't tieable since registers are all different sizes . However , since most modes are bigger than our registers anyway , it 's easier to implement this function that way , leaving QImode as the only unique case .


<s> int m32c_mode_dependent_address ( rtx addr ) { if ( GET_CODE ( addr ) == POST_INC || GET_CODE ( addr ) == PRE_DEC ) return 1 ; return 0 ; } </s>


<s>
bool
m32c_mov_ok
(
rtx
*
operands
,
enum
machine_mode
mode
ATTRIBUTE_UNUSED
)
{
rtx
op0
=
operands
[
0
]
;
rtx
op1
=
operands
[
1
]
;
if
(
TARGET_A24
)
return
true
;
fprintf
(
stderr
,
"m32c_mov_ok %s\n"
,
mode_name
[
mode
]
)
;
debug_rtx
(
op0
)
;
debug_rtx
(
op1
)
;
if
(
GET_CODE
(
op0
)
==
SUBREG
)
op0
=
XEXP
(
op0
,
0
)
;
if
(
GET_CODE
(
op1
)
==
SUBREG
)
op1
=
XEXP
(
op1
,
0
)
;
if
(
GET_CODE
(
op0
)
==
MEM
&&
GET_CODE
(
op1
)
==
MEM
&&
!
reload_completed
)
{
fprintf
(
stderr
,
" - no, mem to mem\n"
)
;
return
false
;
}
fprintf
(
stderr
,
" - ok\n"
)
;
return
true
;
}
</s>

Returns TRUE if we support a move between the first two operands . At the moment , we just want to discourage mem to mem moves until after reload , because reload has a hard time with our limited number of address registers , and we can get into a situation where we need three of them when we only have two .


<s>
void
m32c_output_reg_pop
(
FILE
*
s
,
int
regno
)
{
if
(
regno
==
FLG_REGNO
)
fprintf
(
s
,
"\tpopc\tflg\n"
)
;
else
fprintf
(
s
,
"\tpop.%c\t%s"
,
" bwll"
[
reg_push_size
(
regno
)
]
,
reg_names
[
regno
]
)
;
}
</s>


<s>
void
m32c_output_reg_push
(
FILE
*
s
,
int
regno
)
{
if
(
regno
==
FLG_REGNO
)
fprintf
(
s
,
"\tpushc\tflg\n"
)
;
else
fprintf
(
s
,
"\tpush.%c\t%s"
,
" bwll"
[
reg_push_size
(
regno
)
]
,
reg_names
[
regno
]
)
;
}
</s>

Implements ASM_OUTPUT_REG_PUSH . Control registers are pushed differently than general registers .


<s>
void
m32c_override_options
(
void
)
{
if
(
target_memregs_set
)
{
if
(
target_memregs
<
0
||
target_memregs
>
16
)
error
(
"invalid target memregs value '%d'"
,
target_memregs
)
;
}
else
target_memregs
=
"16"
;
}
</s>

Implements OVERRIDE_OPTIONS . We limit memregs to 0 .. 16 , and provide a default .


<s> int m32c_preferred_output_reload_class ( rtx x , int rclass ) { return m32c_preferred_reload_class ( x , rclass ) ; } </s>


<s>
int
m32c_preferred_reload_class
(
rtx
x
,
int
rclass
)
{
int
newclass
=
rclass
;
fprintf
(
stderr
,
"\npreferred_reload_class for %s is "
,
class_names
[
rclass
]
)
;
if
(
rclass
==
NO_REGS
)
rclass
=
GET_MODE
(
x
)
==
QImode
HL_REGS
:
R03_REGS
;
if
(
classes_intersect
(
rclass
,
CR_REGS
)
)
{
switch
(
GET_MODE
(
x
)
)
{
case
QImode
:
newclass
=
HL_REGS
;
break
;
default
:
break
;
}
}
else
if
(
newclass
==
QI_REGS
&&
GET_MODE_SIZE
(
GET_MODE
(
x
)
)
>
2
)
newclass
=
SI_REGS
;
else
if
(
GET_MODE_SIZE
(
GET_MODE
(
x
)
)
>
4
&&
~
class_contents
[
rclass
]
[
0
]
&
0x000f
)
newclass
=
DI_REGS
;
rclass
=
reduce_class
(
rclass
,
newclass
,
rclass
)
;
if
(
GET_MODE
(
x
)
==
QImode
)
rclass
=
reduce_class
(
rclass
,
HL_REGS
,
rclass
)
;
fprintf
(
stderr
,
"%s\n"
,
class_names
[
rclass
]
)
;
debug_rtx
(
x
)
;
if
(
GET_CODE
(
x
)
==
MEM
&&
GET_CODE
(
XEXP
(
x
,
0
)
)
==
PLUS
&&
GET_CODE
(
XEXP
(
XEXP
(
x
,
0
)
,
0
)
)
==
PLUS
)
fprintf
(
stderr
,
"Glorm!\n"
)
;
return
rclass
;
}
</s>

Implements PREFERRED_RELOAD_CLASS . In general , prefer general registers of the appropriate size .


<s> int m32c_prepare_move ( rtx * operands , enum machine_mode mode ) { if ( TARGET_A16 && mode == PSImode ) return m32c_split_move ( operands , mode , 1 ) ; if ( ( GET_CODE ( operands [ 0 ] ) == MEM ) && ( GET_CODE ( XEXP ( operands [ 0 ] , 0 ) ) == PRE_MODIFY ) ) { rtx pmv = XEXP ( operands [ 0 ] , 0 ) ; rtx dest_reg = XEXP ( pmv , 0 ) ; rtx dest_mod = XEXP ( pmv , 1 ) ; emit_insn ( gen_rtx_SET ( Pmode , dest_reg , dest_mod ) ) ; operands [ 0 ] = gen_rtx_MEM ( mode , dest_reg ) ; } if ( ! no_new_pseudos && MEM_P ( operands [ 0 ] ) && MEM_P ( operands [ 1 ] ) ) operands [ 1 ] = copy_to_mode_reg ( mode , operands [ 1 ] ) ; return 0 ; } </s>

Used to emit move instructions . We split some moves , and avoid mem-mem moves .


<s> int m32c_prepare_shift ( rtx * operands , int scale , int bits ) { rtx temp ; if ( GET_CODE ( operands [ 2 ] ) == CONST_INT && INTVAL ( operands [ 2 ] ) <= ( 1 << ( bits - 1 ) ) && INTVAL ( operands [ 2 ] ) >= - ( 1 << ( bits - 1 ) ) ) { operands [ 2 ] = GEN_INT ( scale * INTVAL ( operands [ 2 ] ) ) ; return 0 ; } if ( scale < 0 ) { temp = gen_reg_rtx ( QImode ) ; if ( GET_CODE ( operands [ 2 ] ) == CONST_INT ) temp = GEN_INT ( - INTVAL ( operands [ 2 ] ) ) ; else emit_move_insn ( temp , gen_rtx_NEG ( QImode , operands [ 2 ] ) ) ; } else temp = operands [ 2 ] ; operands [ 2 ] = temp ; return 0 ; } </s>

The m32c only has one shift , but it takes a signed count . GCC does n't want this , so we fake it by negating any shift count when we 're pretending to shift the other way .


<s> void m32c_print_operand_address ( FILE * stream , rtx address ) { gcc_assert ( GET_CODE ( address ) == MEM ) ; m32c_print_operand ( stream , XEXP ( address , 0 ) , 0 ) ; } </s>

Implements PRINT_OPERAND_ADDRESS . Nothing unusual here .


<s> int m32c_print_operand_punct_valid_p ( int c ) { if ( c == '&' || c == '!' ) return 1 ; return 0 ; } </s>

Implements PRINT_OPERAND_PUNCT_VALID_P . See m32c_print_operand above for descriptions of what these do .


<s> int m32c_push_rounding ( int n ) { if ( TARGET_R8C || TARGET_M16C ) return n ; return ( n + 1 ) & ~ 1 ; } </s>

Implements PUSH_ROUNDING . The R8C and M16C have byte stacks , the M32C has word stacks .


"register_move_cost %s from %s to %s = %d\n"

Implements REGISTER_MOVE_COST . We make impossible moves prohibitively expensive , like trying to put QIs in r2/r3 ( there are no opcodes to do that ) . We also discourage use of mem * registers since they 're really memory .


<s> enum machine_mode m32c_regno_reg_class ( int regno ) { switch ( regno ) { case R0_REGNO : return R0_REGS ; case R1_REGNO : return R1_REGS ; case R2_REGNO : return R2_REGS ; case R3_REGNO : return R3_REGS ; case A0_REGNO : case A1_REGNO : return A_REGS ; case SB_REGNO : return SB_REGS ; case FB_REGNO : return FB_REGS ; case SP_REGNO : return SP_REGS ; case FLG_REGNO : return FLG_REGS ; default : if ( IS_MEM_REGNO ( regno ) ) return MEM_REGS ; return ALL_REGS ; } } </s>


<s> int m32c_reg_class_from_constraint ( char c ATTRIBUTE_UNUSED , const char * s ) { if ( memcmp ( s , "Rsp" , 3 ) == 0 ) return SP_REGS ; if ( memcmp ( s , "Rfb" , 3 ) == 0 ) return FB_REGS ; if ( memcmp ( s , "Rsb" , 3 ) == 0 ) return SB_REGS ; if ( memcmp ( s , "Rcr" , 3 ) == 0 && TARGET_A16 ) return CR_REGS ; if ( memcmp ( s , "Rcl" , 3 ) == 0 && TARGET_A24 ) return CR_REGS ; if ( memcmp ( s , "R0w" , 3 ) == 0 ) return R0_REGS ; if ( memcmp ( s , "R1w" , 3 ) == 0 ) return R1_REGS ; if ( memcmp ( s , "R2w" , 3 ) == 0 ) return R2_REGS ; if ( memcmp ( s , "R3w" , 3 ) == 0 ) return R3_REGS ; if ( memcmp ( s , "R02" , 3 ) == 0 ) return R02_REGS ; if ( memcmp ( s , "R03" , 3 ) == 0 ) return R03_REGS ; if ( memcmp ( s , "Rdi" , 3 ) == 0 ) return DI_REGS ; if ( memcmp ( s , "Rhl" , 3 ) == 0 ) return HL_REGS ; if ( memcmp ( s , "R23" , 3 ) == 0 ) return R23_REGS ; if ( memcmp ( s , "Raa" , 3 ) == 0 ) return A_REGS ; if ( memcmp ( s , "Raw" , 3 ) == 0 && TARGET_A16 ) return A_REGS ; if ( memcmp ( s , "Ral" , 3 ) == 0 && TARGET_A24 ) return A_REGS ; if ( memcmp ( s , "Rqi" , 3 ) == 0 ) return QI_REGS ; if ( memcmp ( s , "Rad" , 3 ) == 0 ) return AD_REGS ; if ( memcmp ( s , "Rsi" , 3 ) == 0 ) return SI_REGS ; if ( memcmp ( s , "Rhi" , 3 ) == 0 ) return HI_REGS ; if ( memcmp ( s , "Rhc" , 3 ) == 0 ) return HC_REGS ; if ( memcmp ( s , "Rra" , 3 ) == 0 ) return RA_REGS ; if ( memcmp ( s , "Rfl" , 3 ) == 0 ) return FLG_REGS ; if ( memcmp ( s , "Rmm" , 3 ) == 0 ) { if ( fixed_regs [ MEM0_REGNO ] ) return NO_REGS ; return MEM_REGS ; } if ( memcmp ( s , "Rpi" , 3 ) == 0 ) { if ( TARGET_A16 ) return HI_REGS ; else return RA_REGS ; } if ( memcmp ( s , "Rpa" , 3 ) == 0 ) return NO_REGS ; return NO_REGS ; } </s>

Implements REG_CLASS_FROM_CONSTRAINT . Note that some constraints only match for certain chip families .


<s> rtx m32c_return_addr_rtx ( int count ) { enum machine_mode mode ; int offset ; rtx ra_mem ; if ( count ) return NULL_RTX ; if ( TARGET_A24 ) { mode = SImode ; offset = 4 ; } else { mode = HImode ; offset = 2 ; } ra_mem = gen_rtx_MEM ( mode , plus_constant ( gen_rtx_REG ( Pmode , FP_REGNO ) , offset ) ) ; return copy_to_mode_reg ( mode , ra_mem ) ; } </s>

Implements RETURN_ADDR_RTX . Note that R8C and M16C push 24 bits ( yes , THREE bytes ) onto the stack for the return address , but we do n't support pointers bigger than 16 bits on those chips . This will likely wreak havoc with exception unwinding . FIXME .


<s>
int
m32c_secondary_reload_class
(
int
rclass
,
enum
machine_mode
mode
,
rtx
x
)
{
int
cc
=
class_contents
[
rclass
]
[
0
]
;
fprintf
(
stderr
,
"\nsecondary reload class %s %s\n"
,
class_names
[
rclass
]
,
mode_name
[
mode
]
)
;
debug_rtx
(
x
)
;
if
(
mode
==
QImode
&&
GET_CODE
(
x
)
==
MEM
&&
(
cc
&
~
class_contents
[
R23_REGS
]
[
0
]
)
==
0
)
return
QI_REGS
;
if
(
classes_intersect
(
rclass
,
CR_REGS
)
&&
GET_CODE
(
x
)
==
REG
&&
REGNO
(
x
)
>=
SB_REGNO
&&
REGNO
(
x
)
<=
SP_REGNO
)
return
TARGET_A16
HI_REGS
:
A_REGS
;
return
NO_REGS
;
}
</s>

Implements SECONDARY_RELOAD_CLASS . QImode have to be reloaded in r0 or r1 , as those are the only real QImode registers . CR regs get reloaded through appropriately sized general or address registers .


<s>
static
rtx
m32c_subreg
(
enum
machine_mode
outer
,
rtx
x
,
enum
machine_mode
inner
,
int
byte
)
{
int
r
,
nr
=
1
;
if
(
GET_CODE
(
x
)
==
SUBREG
&&
SUBREG_BYTE
(
x
)
==
0
&&
GET_CODE
(
SUBREG_REG
(
x
)
)
==
MEM
&&
(
GET_MODE_SIZE
(
GET_MODE
(
x
)
)
==
GET_MODE_SIZE
(
GET_MODE
(
SUBREG_REG
(
x
)
)
)
)
)
{
rtx
oldx
=
x
;
x
=
gen_rtx_MEM
(
GET_MODE
(
x
)
,
XEXP
(
SUBREG_REG
(
x
)
,
0
)
)
;
MEM_COPY_ATTRIBUTES
(
x
,
SUBREG_REG
(
oldx
)
)
;
}
if
(
GET_CODE
(
x
)
==
MEM
&&
(
GET_CODE
(
XEXP
(
x
,
0
)
)
==
PRE_DEC
||
GET_CODE
(
XEXP
(
x
,
0
)
)
==
POST_INC
)
)
return
gen_rtx_MEM
(
outer
,
XEXP
(
x
,
0
)
)
;
if
(
GET_CODE
(
x
)
==
SUBREG
&&
GET_CODE
(
XEXP
(
x
,
0
)
)
==
MEM
&&
(
GET_CODE
(
XEXP
(
XEXP
(
x
,
0
)
,
0
)
)
==
PRE_DEC
||
GET_CODE
(
XEXP
(
XEXP
(
x
,
0
)
,
0
)
)
==
POST_INC
)
)
return
gen_rtx_MEM
(
outer
,
XEXP
(
XEXP
(
x
,
0
)
,
0
)
)
;
if
(
GET_CODE
(
x
)
!=
REG
)
return
simplify_gen_subreg
(
outer
,
x
,
inner
,
byte
)
;
r
=
REGNO
(
x
)
;
if
(
r
>=
FIRST_PSEUDO_REGISTER
||
r
==
AP_REGNO
)
return
simplify_gen_subreg
(
outer
,
x
,
inner
,
byte
)
;
if
(
IS_MEM_REGNO
(
r
)
)
return
simplify_gen_subreg
(
outer
,
x
,
inner
,
byte
)
;
if
(
byte
==
0
)
nr
=
r
;
else
if
(
outer
==
HImode
)
{
if
(
r
==
R0_REGNO
&&
byte
==
2
)
nr
=
R2_REGNO
;
else
if
(
r
==
R0_REGNO
&&
byte
==
4
)
nr
=
R1_REGNO
;
else
if
(
r
==
R0_REGNO
&&
byte
==
6
)
nr
=
R3_REGNO
;
else
if
(
r
==
R1_REGNO
&&
byte
==
2
)
nr
=
R3_REGNO
;
else
if
(
r
==
A0_REGNO
&&
byte
==
2
)
nr
=
A1_REGNO
;
}
else
if
(
outer
==
SImode
)
{
if
(
r
==
R0_REGNO
&&
byte
==
0
)
nr
=
R0_REGNO
;
else
if
(
r
==
R0_REGNO
&&
byte
==
4
)
nr
=
R1_REGNO
;
}
if
(
nr
==
1
)
{
fprintf
(
stderr
,
"m32c_subreg %s %s %d\n"
,
mode_name
[
outer
]
,
mode_name
[
inner
]
,
byte
)
;
debug_rtx
(
x
)
;
gcc_unreachable
(
)
;
}
return
gen_rtx_REG
(
outer
,
nr
)
;
}
</s>

Subregs are non-orthogonal for us , because our registers are all different sizes .


<s> static int need_to_save ( int regno ) { if ( fixed_regs [ regno ] ) return 0 ; if ( cfun -> calls_eh_return ) return 1 ; if ( regno == FP_REGNO ) return 0 ; if ( cfun -> machine -> is_interrupt && ( ! cfun -> machine -> is_leaf || regno == A0_REGNO ) ) return 1 ; if ( regs_ever_live [ regno ] && ( ! call_used_regs [ regno ] || cfun -> machine -> is_interrupt ) ) return 1 ; return 0 ; } </s>

Returns true if the given register needs to be saved by the current function .


<s> static int reduce_class ( int original_class , int limiting_class , int returned_if_empty ) { int cc = class_contents [ original_class ] [ 0 ] ; int i , best = NO_REGS ; int best_size = 0 ; if ( original_class == limiting_class ) return original_class ; if ( ! class_sizes ) { int r ; class_sizes = ( int * ) xmalloc ( LIM_REG_CLASSES * sizeof ( int ) ) ; for ( i = 0 ; i < LIM_REG_CLASSES ; i ++ ) { class_sizes [ i ] = 0 ; for ( r = 0 ; r < FIRST_PSEUDO_REGISTER ; r ++ ) if ( class_contents [ i ] [ 0 ] & ( 1 << r ) ) class_sizes [ i ] ++ ; } } cc &= class_contents [ limiting_class ] [ 0 ] ; for ( i = 0 ; i < LIM_REG_CLASSES ; i ++ ) { int ic = class_contents [ i ] [ 0 ] ; if ( ( ~ cc & ic ) == 0 ) if ( best_size < class_sizes [ i ] ) { best = i ; best_size = class_sizes [ i ] ; } } if ( best == NO_REGS ) return returned_if_empty ; return best ; } </s>

Given two register classes , find the largest intersection between them . If there is no intersection , return RETURNED_IF_EMPTY instead .


<s> static const char * reg_name_with_mode ( int regno , enum machine_mode mode ) { int mlen = GET_MODE_SIZE ( mode ) ; if ( regno == R0_REGNO && mlen == 1 ) return "r0l" ; if ( regno == R0_REGNO && ( mlen == 3 || mlen == 4 ) ) return "r2r0" ; if ( regno == R0_REGNO && mlen == 6 ) return "r2r1r0" ; if ( regno == R0_REGNO && mlen == 8 ) return "r3r1r2r0" ; if ( regno == R1_REGNO && mlen == 1 ) return "r1l" ; if ( regno == R1_REGNO && ( mlen == 3 || mlen == 4 ) ) return "r3r1" ; if ( regno == A0_REGNO && TARGET_A16 && ( mlen == 3 || mlen == 4 ) ) return "a1a0" ; return reg_names [ regno ] ; } </s>

Since register names indicate the mode they 're used in , we need a way to determine which name to refer to the register with . Called by print_operand ( ) .


<s>
static
bool
class_can_hold_mode
(
reg_class_t
rclass
,
machine_mode
mode
)
{
static
char
results
[
LIM_REG_CLASSES
]
[
MAX_MACHINE_MODE
]
;
if
(
results
[
(
int
)
rclass
]
[
mode
]
==
0
)
{
int
r
;
results
[
rclass
]
[
mode
]
=
1
;
for
(
r
=
0
;
r
<
FIRST_PSEUDO_REGISTER
;
r
++
)
if
(
in_hard_reg_set_p
(
reg_class_contents
[
(
int
)
rclass
]
,
mode
,
r
)
&&
HARD_REGNO_MODE_OK
(
r
,
mode
)
)
{
results
[
rclass
]
[
mode
]
=
2
;
break
;
}
}
fprintf
(
stderr
,
"class %s can hold %s? %s\n"
,
class_names
[
(
int
)
rclass
]
,
mode_name
[
mode
]
,
(
results
[
rclass
]
[
mode
]
==
2
)
"yes"
:
"no"
)
;
return
results
[
(
int
)
rclass
]
[
mode
]
==
2
;
}
</s>

Used by m32c_register_move_cost to determine if a move is impossibly expensive .


<s> static hashval_t hash ( const char * str ) { return htab_hash_string ( str ) ; } </s>

Hash function for builtin functions with up to 3 arguments and a return type .


<s>
int
m32c_cannot_change_mode_class
(
machine_mode
from
,
machine_mode
to
,
int
rclass
)
{
int
rn
;
fprintf
(
stderr
,
"cannot change from %s to %s in %s\n"
,
mode_name
[
from
]
,
mode_name
[
to
]
,
class_names
[
rclass
]
)
;
for
(
rn
=
0
;
rn
<
FIRST_PSEUDO_REGISTER
;
rn
++
)
if
(
class_contents
[
rclass
]
[
0
]
&
(
1
<<
rn
)
)
if
(
!
m32c_hard_regno_ok
(
rn
,
to
)
)
return
1
;
if
(
to
==
QImode
)
return
(
class_contents
[
rclass
]
[
0
]
&
0x1ffa
)
;
if
(
class_contents
[
rclass
]
[
0
]
&
0x0005
&&
GET_MODE_SIZE
(
from
)
>
1
)
return
0
;
if
(
GET_MODE_SIZE
(
from
)
>
2
)
return
0
;
return
1
;
}
</s>

Implements CANNOT_CHANGE_MODE_CLASS . Only r0 and r1 can change to QI ( r0l , r1l ) because the chip does n't support QI ops on other registers ( well , it does on a0/a1 but if we let gcc do that , reload suffers ) . Otherwise , we allow changes to larger modes .


<s> void m32c_conditional_register_usage ( void ) { int i ; if ( 0 <= target_memregs && target_memregs <= 16 ) { for ( i = ( target_memregs + 1 ) / 2 ; i < 8 ; i ++ ) { fixed_regs [ MEM0_REGNO + i ] = 1 ; CLEAR_HARD_REG_BIT ( reg_class_contents [ MEM_REGS ] , MEM0_REGNO + i ) ; } } if ( TARGET_A24 ) { call_used_regs [ R1_REGNO ] = 0 ; call_used_regs [ R2_REGNO ] = 0 ; call_used_regs [ R3_REGNO ] = 0 ; call_used_regs [ A0_REGNO ] = 0 ; call_used_regs [ A1_REGNO ] = 0 ; } } </s>

Implements CONDITIONAL_REGISTER_USAGE . We adjust the number of available memregs , and select which registers need to be preserved across calls based on the chip family .


"%<bank_switch%> has no effect on non-interrupt functions"

Emits the prologue . See the frame layout comment earlier in this file . We can reserve up to 256 bytes with the ENTER opcode , beyond that we manually update sp .


<s> int m32c_expand_movmemhi ( rtx * operands ) { rtx desta , srca , count ; rtx desto , srco , counto ; desta = XEXP ( operands [ 0 ] , 0 ) ; srca = XEXP ( operands [ 1 ] , 0 ) ; count = operands [ 2 ] ; desto = gen_reg_rtx ( Pmode ) ; srco = gen_reg_rtx ( Pmode ) ; counto = gen_reg_rtx ( HImode ) ; if ( GET_CODE ( desta ) != REG || REGNO ( desta ) < FIRST_PSEUDO_REGISTER ) desta = copy_to_mode_reg ( Pmode , desta ) ; if ( GET_CODE ( srca ) != REG || REGNO ( srca ) < FIRST_PSEUDO_REGISTER ) srca = copy_to_mode_reg ( Pmode , srca ) ; if ( GET_CODE ( count ) == CONST_INT && ! ( INTVAL ( count ) & 1 ) && ( INTVAL ( count ) > 1 ) ) { count = copy_to_mode_reg ( HImode , GEN_INT ( INTVAL ( count ) / 2 ) ) ; if ( TARGET_A16 ) emit_insn ( gen_movmemhi_whi_op ( desto , srco , counto , desta , srca , count ) ) ; else emit_insn ( gen_movmemhi_wpsi_op ( desto , srco , counto , desta , srca , count ) ) ; return 1 ; } if ( GET_CODE ( count ) != REG || REGNO ( count ) < FIRST_PSEUDO_REGISTER ) count = copy_to_mode_reg ( HImode , count ) ; if ( TARGET_A16 ) emit_insn ( gen_movmemhi_bhi_op ( desto , srco , counto , desta , srca , count ) ) ; else emit_insn ( gen_movmemhi_bpsi_op ( desto , srco , counto , desta , srca , count ) ) ; return 1 ; } </s>

This is a memcpy ( ) opcode . All operands are implied , so we need to arrange for them to be in the right registers . The opcode wants addresses , not [ mem ] syntax . $ 0 is the destination ( MEM : BLK ) , $ 1 is the source ( MEM : BLK ) , and $ 2 the count ( HI ) .


<s>
static
rtx
m32c_function_arg
(
cumulative_args_t
ca_v
,
machine_mode
mode
,
const_tree
type
,
bool
named
)
{
CUMULATIVE_ARGS
*
ca
=
get_cumulative_args
(
ca_v
)
;
rtx
rv
=
NULL_RTX
;
fprintf
(
stderr
,
"func_arg %d (%s, %d)\n"
,
ca
->
parm_num
,
mode_name
[
mode
]
,
named
)
;
debug_tree
(
(
tree
)
type
)
;
if
(
mode
==
VOIDmode
)
return
GEN_INT
(
0
)
;
if
(
ca
->
force_mem
||
!
named
)
{
fprintf
(
stderr
,
"func arg: force %d named %d, mem\n"
,
ca
->
force_mem
,
named
)
;
return
NULL_RTX
;
}
if
(
type
&&
INTEGRAL_TYPE_P
(
type
)
&&
POINTER_TYPE_P
(
type
)
)
return
NULL_RTX
;
if
(
type
&&
AGGREGATE_TYPE_P
(
type
)
)
return
NULL_RTX
;
switch
(
ca
->
parm_num
)
{
case
1
:
if
(
GET_MODE_SIZE
(
mode
)
==
1
||
GET_MODE_SIZE
(
mode
)
==
2
)
rv
=
gen_rtx_REG
(
mode
,
TARGET_A16
R1_REGNO
:
R0_REGNO
)
;
break
;
case
2
:
if
(
TARGET_A16
&&
GET_MODE_SIZE
(
mode
)
==
2
)
rv
=
gen_rtx_REG
(
mode
,
R2_REGNO
)
;
break
;
}
debug_rtx
(
rv
)
;
return
rv
;
}
</s>

Implements FUNCTION_ARG . Arguments are passed partly in registers , partly on stack . If our function returns a struct , a pointer to a buffer for it is at the top of the stack ( last thing pushed ) . The first few real arguments may be in registers as follows : R8C/M16C : arg1 in r1 if it 's QI or HI ( else it 's pushed on stack ) arg2 in r2 if it 's HI ( else pushed on stack ) rest on stack M32C : arg1 in r0 if it 's QI or HI ( else it 's pushed on stack ) rest on stack Structs are not passed in registers , even if they fit . Only integer and pointer types are passed in registers . Note that when arg1 does n't fit in r1 , arg2 may still be passed in r2 if it fits .


<s> static void m32c_function_arg_advance ( cumulative_args_t ca_v , machine_mode mode ATTRIBUTE_UNUSED , const_tree type ATTRIBUTE_UNUSED , bool named ATTRIBUTE_UNUSED ) { CUMULATIVE_ARGS * ca = get_cumulative_args ( ca_v ) ; if ( ca -> force_mem ) ca -> force_mem = 0 ; else ca -> parm_num ++ ; } </s>

Implements FUNCTION_ARG_ADVANCE . force_mem is set for functions returning structures , so we always reset that . Otherwise , we only need to know the sequence number of the argument to know what to do with it .


<s> static bool m32c_function_needs_enter ( void ) { rtx_insn * insn ; struct sequence_stack * seq ; rtx sp = gen_rtx_REG ( Pmode , SP_REGNO ) ; rtx fb = gen_rtx_REG ( Pmode , FB_REGNO ) ; insn = get_insns ( ) ; for ( seq = crtl -> emit . sequence_stack ; seq ; insn = seq -> first , seq = seq -> next ) ; while ( insn ) { if ( reg_mentioned_p ( sp , insn ) ) return true ; if ( reg_mentioned_p ( fb , insn ) ) return true ; insn = NEXT_INSN ( insn ) ; } return false ; } </s>

Returns TRUE if the current function needs to use the ENTER/EXIT opcodes . If the function does n't need the frame base or stack pointer , it can use the simpler RTS opcode .


<s> int m32c_hard_regno_nregs ( int regno , machine_mode mode ) { int rv = m32c_hard_regno_nregs_1 ( regno , mode ) ; return rv ? rv : 1 ; } </s>

Implements HARD_REGNO_NREGS . This is complicated by the fact that different registers are different sizes from each other , * and * may be different sizes in different chip families .


<s> static int m32c_hard_regno_nregs_1 ( int regno , machine_mode mode ) { if ( regno == FLG_REGNO && mode == CCmode ) return 1 ; if ( regno >= FIRST_PSEUDO_REGISTER ) return ( ( GET_MODE_SIZE ( mode ) + UNITS_PER_WORD - 1 ) / UNITS_PER_WORD ) ; if ( regno >= MEM0_REGNO && regno <= MEM7_REGNO ) return ( GET_MODE_SIZE ( mode ) + 1 ) / 2 ; if ( GET_MODE_SIZE ( mode ) <= 1 ) return nregs_table [ regno ] . qi_regs ; if ( GET_MODE_SIZE ( mode ) <= 2 ) return nregs_table [ regno ] . hi_regs ; if ( regno == A0_REGNO && mode == SImode && TARGET_A16 ) return 2 ; if ( ( GET_MODE_SIZE ( mode ) <= 3 || mode == PSImode ) && TARGET_A24 ) return nregs_table [ regno ] . pi_regs ; if ( GET_MODE_SIZE ( mode ) <= 4 ) return nregs_table [ regno ] . si_regs ; if ( GET_MODE_SIZE ( mode ) <= 8 ) return nregs_table [ regno ] . di_regs ; return 0 ; } </s>


<s> int m32c_hard_regno_ok ( int regno , machine_mode mode ) { return m32c_hard_regno_nregs_1 ( regno , mode ) != 0 ; } </s>

Implements HARD_REGNO_MODE_OK . The above function does the work already ; just test its return value .


<s> static int m32c_leaf_function_p ( void ) { rtx_insn * saved_first , * saved_last ; struct sequence_stack * seq ; int rv ; saved_first = crtl -> emit . x_first_insn ; saved_last = crtl -> emit . x_last_insn ; for ( seq = crtl -> emit . sequence_stack ; seq && seq -> next ; seq = seq -> next ) ; if ( seq ) { crtl -> emit . x_first_insn = seq -> first ; crtl -> emit . x_last_insn = seq -> last ; } rv = leaf_function_p ( ) ; crtl -> emit . x_first_insn = saved_first ; crtl -> emit . x_last_insn = saved_last ; return rv ; } </s>

Returns TRUE if the current function is a leaf , and thus we can determine which registers an interrupt function really needs to save . The logic below is mostly about finding the insn sequence that 's the function , versus any sequence that might be open for the current insn .


<s>
static
rtx
m32c_legitimize_address
(
rtx
x
,
rtx
oldx
ATTRIBUTE_UNUSED
,
machine_mode
mode
)
{
fprintf
(
stderr
,
"m32c_legitimize_address for mode %s\n"
,
mode_name
[
mode
]
)
;
debug_rtx
(
x
)
;
fprintf
(
stderr
,
"\n"
)
;
if
(
GET_CODE
(
x
)
==
PLUS
&&
GET_CODE
(
XEXP
(
x
,
0
)
)
==
REG
&&
REGNO
(
XEXP
(
x
,
0
)
)
==
FB_REGNO
&&
GET_CODE
(
XEXP
(
x
,
1
)
)
==
CONST_INT
&&
(
INTVAL
(
XEXP
(
x
,
1
)
)
<
128
||
INTVAL
(
XEXP
(
x
,
1
)
)
>
(
128
GET_MODE_SIZE
(
mode
)
)
)
)
{
rtx
temp
=
gen_reg_rtx
(
Pmode
)
;
x
=
copy_rtx
(
x
)
;
emit_insn
(
gen_rtx_SET
(
VOIDmode
,
temp
,
XEXP
(
x
,
0
)
)
)
;
XEXP
(
x
,
0
)
=
temp
;
}
return
x
;
}
</s>

Implements LEGITIMIZE_ADDRESS . The only address we really have to worry about is frame base offsets , as $ fb has a limited displacement range . We deal with this by attempting to reload $ fb itself into an address register ; that seems to result in the best code .


<s> int m32c_modes_tieable_p ( machine_mode m1 , machine_mode m2 ) { if ( GET_MODE_SIZE ( m1 ) == GET_MODE_SIZE ( m2 ) ) return 1 ; if ( m1 == QImode || m2 == QImode ) return 0 ; return 1 ; } </s>

Implements MODES_TIEABLE_P . In general , modes are n't tieable since registers are all different sizes . However , since most modes are bigger than our registers anyway , it 's easier to implement this function that way , leaving QImode as the only unique case .


<s>
static
reg_class_t
m32c_preferred_reload_class
(
rtx
x
,
reg_class_t
rclass
)
{
reg_class_t
newclass
=
rclass
;
fprintf
(
stderr
,
"\npreferred_reload_class for %s is "
,
class_names
[
rclass
]
)
;
if
(
rclass
==
NO_REGS
)
rclass
=
GET_MODE
(
x
)
==
QImode
HL_REGS
:
R03_REGS
;
if
(
reg_classes_intersect_p
(
rclass
,
CR_REGS
)
)
{
switch
(
GET_MODE
(
x
)
)
{
case
QImode
:
newclass
=
HL_REGS
;
break
;
default
:
break
;
}
}
else
if
(
newclass
==
QI_REGS
&&
GET_MODE_SIZE
(
GET_MODE
(
x
)
)
>
2
)
newclass
=
SI_REGS
;
else
if
(
GET_MODE_SIZE
(
GET_MODE
(
x
)
)
>
4
&&
!
reg_class_subset_p
(
R03_REGS
,
rclass
)
)
newclass
=
DI_REGS
;
rclass
=
reduce_class
(
rclass
,
newclass
,
rclass
)
;
if
(
GET_MODE
(
x
)
==
QImode
)
rclass
=
reduce_class
(
rclass
,
HL_REGS
,
rclass
)
;
fprintf
(
stderr
,
"%s\n"
,
class_names
[
rclass
]
)
;
debug_rtx
(
x
)
;
if
(
GET_CODE
(
x
)
==
MEM
&&
GET_CODE
(
XEXP
(
x
,
0
)
)
==
PLUS
&&
GET_CODE
(
XEXP
(
XEXP
(
x
,
0
)
,
0
)
)
==
PLUS
)
fprintf
(
stderr
,
"Glorm!\n"
)
;
return
rclass
;
}
</s>

Implements PREFERRED_RELOAD_CLASS . In general , prefer general registers of the appropriate size .


<s> int m32c_prepare_move ( rtx * operands , machine_mode mode ) { if ( far_addr_space_p ( operands [ 0 ] ) && CONSTANT_P ( operands [ 1 ] ) ) { operands [ 1 ] = force_reg ( GET_MODE ( operands [ 0 ] ) , operands [ 1 ] ) ; } if ( TARGET_A16 && mode == PSImode ) return m32c_split_move ( operands , mode , 1 ) ; if ( ( GET_CODE ( operands [ 0 ] ) == MEM ) && ( GET_CODE ( XEXP ( operands [ 0 ] , 0 ) ) == PRE_MODIFY ) ) { rtx pmv = XEXP ( operands [ 0 ] , 0 ) ; rtx dest_reg = XEXP ( pmv , 0 ) ; rtx dest_mod = XEXP ( pmv , 1 ) ; emit_insn ( gen_rtx_SET ( Pmode , dest_reg , dest_mod ) ) ; operands [ 0 ] = gen_rtx_MEM ( mode , dest_reg ) ; } if ( can_create_pseudo_p ( ) && MEM_P ( operands [ 0 ] ) && MEM_P ( operands [ 1 ] ) ) operands [ 1 ] = copy_to_mode_reg ( mode , operands [ 1 ] ) ; return 0 ; } </s>

Used to emit move instructions . We split some moves , and avoid mem-mem moves .


<s> static void m32c_print_operand_address ( FILE * stream , rtx address ) { if ( GET_CODE ( address ) == MEM ) address = XEXP ( address , 0 ) ; else gcc_assert ( GET_CODE ( address ) == REG ) ; m32c_print_operand ( stream , address , 0 ) ; } </s>

Implements PRINT_OPERAND_ADDRESS . Nothing unusual here .


<s> unsigned int m32c_push_rounding ( int n ) { if ( TARGET_R8C || TARGET_M16C ) return n ; return ( n + 1 ) & ~ 1 ; } </s>

Implements PUSH_ROUNDING . The R8C and M16C have byte stacks , the M32C has word stacks .


"register_move_cost %s from %s to %s = %d\n"

Implements REGISTER_MOVE_COST . We make impossible moves prohibitively expensive , like trying to put QIs in r2/r3 ( there are no opcodes to do that ) . We also discourage use of mem * registers since they 're really memory .


<s> static int need_to_save ( int regno ) { if ( fixed_regs [ regno ] ) return 0 ; if ( crtl -> calls_eh_return ) return 1 ; if ( regno == FP_REGNO ) return 0 ; if ( cfun -> machine -> is_interrupt && ( ! cfun -> machine -> is_leaf || ( regno == A0_REGNO && m32c_function_needs_enter ( ) ) ) ) return 1 ; if ( df_regs_ever_live_p ( regno ) && ( ! call_used_regs [ regno ] || cfun -> machine -> is_interrupt ) ) return 1 ; return 0 ; } </s>

Returns true if the given register needs to be saved by the current function .


<s> static reg_class_t reduce_class ( reg_class_t original_class , reg_class_t limiting_class , reg_class_t returned_if_empty ) { HARD_REG_SET cc ; int i ; reg_class_t best = NO_REGS ; unsigned int best_size = 0 ; if ( original_class == limiting_class ) return original_class ; cc = reg_class_contents [ original_class ] ; AND_HARD_REG_SET ( cc , reg_class_contents [ limiting_class ] ) ; for ( i = 0 ; i < LIM_REG_CLASSES ; i ++ ) { if ( hard_reg_set_subset_p ( reg_class_contents [ i ] , cc ) ) if ( best_size < reg_class_size [ i ] ) { best = ( reg_class_t ) i ; best_size = reg_class_size [ i ] ; } } if ( best == NO_REGS ) return returned_if_empty ; return best ; } </s>

Given two register classes , find the largest intersection between them . If there is no intersection , return RETURNED_IF_EMPTY instead .


<s> static int bank_switch_p ( tree node ATTRIBUTE_UNUSED ) { tree list = M32C_ATTRIBUTES ( node ) ; while ( list ) { if ( is_attribute_p ( "bank_switch" , TREE_PURPOSE ( list ) ) ) return 1 ; list = TREE_CHAIN ( list ) ; } return 0 ; } </s>

Returns TRUE if the given tree has the `` bank_switch '' attribute .


<s>
static
bool
class_can_hold_mode
(
reg_class_t
rclass
,
machine_mode
mode
)
{
static
char
results
[
LIM_REG_CLASSES
]
[
MAX_MACHINE_MODE
]
;
if
(
results
[
(
int
)
rclass
]
[
mode
]
==
0
)
{
int
r
;
results
[
rclass
]
[
mode
]
=
1
;
for
(
r
=
0
;
r
<
FIRST_PSEUDO_REGISTER
;
r
++
)
if
(
in_hard_reg_set_p
(
reg_class_contents
[
(
int
)
rclass
]
,
mode
,
r
)
&&
m32c_hard_regno_mode_ok
(
r
,
mode
)
)
{
results
[
rclass
]
[
mode
]
=
2
;
break
;
}
}
fprintf
(
stderr
,
"class %s can hold %s? %s\n"
,
class_names
[
(
int
)
rclass
]
,
mode_name
[
mode
]
,
(
results
[
rclass
]
[
mode
]
==
2
)
"yes"
:
"no"
)
;
return
results
[
(
int
)
rclass
]
[
mode
]
==
2
;
}
</s>

Used by m32c_register_move_cost to determine if a move is impossibly expensive .


<s> int current_function_special_page_vector ( rtx x ) { int num ; if ( ( GET_CODE ( x ) == SYMBOL_REF ) && ( SYMBOL_REF_FLAGS ( x ) & SYMBOL_FLAG_FUNCVEC_FUNCTION ) ) { tree list ; tree t = SYMBOL_REF_DECL ( x ) ; if ( TREE_CODE ( t ) != FUNCTION_DECL ) return 0 ; list = M32C_ATTRIBUTES ( t ) ; while ( list ) { if ( is_attribute_p ( "function_vector" , TREE_PURPOSE ( list ) ) ) { num = TREE_INT_CST_LOW ( TREE_VALUE ( TREE_VALUE ( list ) ) ) ; return num ; } list = TREE_CHAIN ( list ) ; } return 0 ; } else return 0 ; } </s>

If the function is assigned the attribute 'function_vector ' , it returns the function vector number , otherwise returns zero .


<s> static rtx F ( rtx x ) { RTX_FRAME_RELATED_P ( x ) = 1 ; return x ; } </s>

We use this to wrap all emitted insns in the prologue , so they get the `` frame-related '' ( /f ) flag set .


<s> static int fast_interrupt_p ( tree node ATTRIBUTE_UNUSED ) { tree list = M32C_ATTRIBUTES ( node ) ; while ( list ) { if ( is_attribute_p ( "fast_interrupt" , TREE_PURPOSE ( list ) ) ) return 1 ; list = TREE_CHAIN ( list ) ; } return 0 ; } </s>

Returns TRUE if the given tree has the `` fast_interrupt '' attribute .


<s> static int flags_needed_for_conditional ( rtx cond ) { switch ( GET_CODE ( cond ) ) { case LE : case GT : return FLAGS_OSZ ; case LEU : case GTU : return FLAGS_ZC ; case LT : case GE : return FLAGS_OS ; case LTU : case GEU : return FLAGS_C ; case EQ : case NE : return FLAGS_Z ; default : return FLAGS_N ; } } </s>

Indicate which flags must be properly set for a given conditional .


<s> static rtx m32c_addr_space_convert ( rtx op , tree from_type , tree to_type ) { addr_space_t from_as = TYPE_ADDR_SPACE ( TREE_TYPE ( from_type ) ) ; addr_space_t to_as = TYPE_ADDR_SPACE ( TREE_TYPE ( to_type ) ) ; rtx result ; gcc_assert ( from_as == ADDR_SPACE_GENERIC || from_as == ADDR_SPACE_FAR ) ; gcc_assert ( to_as == ADDR_SPACE_GENERIC || to_as == ADDR_SPACE_FAR ) ; if ( to_as == ADDR_SPACE_GENERIC && from_as == ADDR_SPACE_FAR ) { result = gen_reg_rtx ( HImode ) ; emit_move_insn ( result , simplify_subreg ( HImode , op , SImode , 0 ) ) ; return result ; } else if ( to_as == ADDR_SPACE_FAR && from_as == ADDR_SPACE_GENERIC ) { result = gen_reg_rtx ( SImode ) ; emit_insn ( gen_zero_extendhisi2 ( result , op ) ) ; return result ; } else gcc_unreachable ( ) ; } </s>

Convert from one address space to another .


<s> static rtx m32c_all_frame_related ( rtx par ) { int len = XVECLEN ( par , 0 ) ; int i ; for ( i = 0 ; i < len ; i ++ ) F ( XVECEXP ( par , 0 , i ) ) ; return par ; } </s>

Mark all the subexpressions of the PARALLEL rtx PAR as frame-related . Return PAR . dwarf2out.cc : dwarf2out_frame_debug_expr ignores sub-expressions of a PARALLEL rtx other than the first if they do not have the FRAME_RELATED flag set on them . So this function is handy for marking up 'enter ' instructions .


<s>
static
bool
m32c_can_change_mode_class
(
machine_mode
from
,
machine_mode
to
,
reg_class_t
rclass
)
{
int
rn
;
fprintf
(
stderr
,
"can change from %s to %s in %s\n"
,
mode_name
[
from
]
,
mode_name
[
to
]
,
class_names
[
rclass
]
)
;
for
(
rn
=
0
;
rn
<
FIRST_PSEUDO_REGISTER
;
rn
++
)
if
(
class_contents
[
rclass
]
[
0
]
&
(
1
<<
rn
)
)
if
(
!
m32c_hard_regno_mode_ok
(
rn
,
to
)
)
return
false
;
if
(
to
==
QImode
)
return
(
class_contents
[
rclass
]
[
0
]
&
0x1ffa
)
==
0
;
if
(
class_contents
[
rclass
]
[
0
]
&
0x0005
&&
GET_MODE_SIZE
(
from
)
>
1
)
return
true
;
if
(
GET_MODE_SIZE
(
from
)
>
2
)
return
true
;
return
false
;
}
</s>

Implements TARGET_CAN_CHANGE_MODE_CLASS . Only r0 and r1 can change to QI ( r0l , r1l ) because the chip does n't support QI ops on other registers ( well , it does on a0/a1 but if we let gcc do that , reload suffers ) . Otherwise , we allow changes to larger modes .


<s> static bool m32c_class_likely_spilled_p ( reg_class_t regclass ) { if ( regclass == A_REGS ) return true ; return ( reg_class_size [ ( int ) regclass ] == 1 ) ; } </s>

Implements CLASS_LIKELY_SPILLED_P . A_REGS is needed for address reloads .


<s> static unsigned char m32c_class_max_nregs ( reg_class_t regclass , machine_mode mode ) { int rn ; unsigned char max = 0 ; for ( rn = 0 ; rn < FIRST_PSEUDO_REGISTER ; rn ++ ) if ( TEST_HARD_REG_BIT ( reg_class_contents [ ( int ) regclass ] , rn ) ) { unsigned char n = m32c_hard_regno_nregs ( rn , mode ) ; if ( max < n ) max = n ; } return max ; } </s>

Implements CLASS_MAX_NREGS . We calculate this according to its documented meaning , to avoid potential inconsistencies with actual class definitions .


<s> void m32c_conditional_register_usage ( void ) { int i ; if ( target_memregs >= 0 && target_memregs <= 16 ) { for ( i = ( target_memregs + 1 ) / 2 ; i < 8 ; i ++ ) { fixed_regs [ MEM0_REGNO + i ] = 1 ; CLEAR_HARD_REG_BIT ( reg_class_contents [ MEM_REGS ] , MEM0_REGNO + i ) ; } } if ( TARGET_A24 ) { call_used_regs [ R1_REGNO ] = 0 ; call_used_regs [ R2_REGNO ] = 0 ; call_used_regs [ R3_REGNO ] = 0 ; call_used_regs [ A0_REGNO ] = 0 ; call_used_regs [ A1_REGNO ] = 0 ; } } </s>

Implements CONDITIONAL_REGISTER_USAGE . We adjust the number of available memregs , and select which registers need to be preserved across calls based on the chip family .


<s> unsigned int m32c_dwarf_frame_regnum ( int n ) { switch ( n ) { case R0_REGNO : return 5 ; case R1_REGNO : return 6 ; case R2_REGNO : return 7 ; case R3_REGNO : return 8 ; case A0_REGNO : return 9 ; case A1_REGNO : return 10 ; case FB_REGNO : return 11 ; case SB_REGNO : return 19 ; case SP_REGNO : return 12 ; case PC_REGNO : return 13 ; default : return DWARF_FRAME_REGISTERS + 1 ; } } </s>

Implements DWARF_FRAME_REGNUM and DEBUGGER_REGNO . Note that the original spec called for dwarf numbers to vary with register width as well , for example , r0l , r0 , and r2r0 would each have different dwarf numbers . GCC does n't support this , and we do n't do it , and gdb seems to like it this way anyway .


<s> int m32c_eh_return_data_regno ( int n ) { switch ( n ) { case 0 : return MEM0_REGNO ; case 1 : return MEM0_REGNO + 4 ; default : return INVALID_REGNUM ; } } </s>

Implements EH_RETURN_DATA_REGNO . Choose registers able to hold pointers .


<s> rtx m32c_eh_return_stackadj_rtx ( void ) { if ( ! cfun -> machine -> eh_stack_adjust ) { rtx sa ; sa = gen_rtx_REG ( Pmode , R0_REGNO ) ; cfun -> machine -> eh_stack_adjust = sa ; } return cfun -> machine -> eh_stack_adjust ; } </s>

Implements EH_RETURN_STACKADJ_RTX . Saved and used later in m32c_emit_eh_epilogue .


"%<fast_interrupt%> attribute directive ignored"

Likewise , for the epilogue . The only exception is that , for interrupts , we must manually unwind the frame as the REIT opcode does n't do that .


"%<bank_switch%> has no effect on non-interrupt functions"

Emits the prologue . See the frame layout comment earlier in this file . We can reserve up to 256 bytes with the ENTER opcode , beyond that we manually update sp .


<s> static void m32c_encode_section_info ( tree decl , rtx rtl , int first ) { int extra_flags = 0 ; default_encode_section_info ( decl , rtl , first ) ; if ( TREE_CODE ( decl ) == FUNCTION_DECL && m32c_special_page_vector_p ( decl ) ) extra_flags = SYMBOL_FLAG_FUNCVEC_FUNCTION ; if ( extra_flags ) SYMBOL_REF_FLAGS ( XEXP ( rtl , 0 ) ) |= extra_flags ; } </s>

Encode symbol attributes of a SYMBOL_REF into its SYMBOL_REF_FLAGS .


<s> int m32c_epilogue_uses ( int regno ATTRIBUTE_UNUSED ) { if ( cfun -> machine -> is_interrupt ) return 1 ; return 0 ; } </s>

Implements EPILOGUE_USES . Interrupts restore all registers .


<s> int m32c_expand_cmpstr ( rtx * operands ) { rtx src1a , src2a ; src1a = XEXP ( operands [ 1 ] , 0 ) ; src2a = XEXP ( operands [ 2 ] , 0 ) ; if ( GET_CODE ( src1a ) != REG || REGNO ( src1a ) < FIRST_PSEUDO_REGISTER ) src1a = copy_to_mode_reg ( Pmode , src1a ) ; if ( GET_CODE ( src2a ) != REG || REGNO ( src2a ) < FIRST_PSEUDO_REGISTER ) src2a = copy_to_mode_reg ( Pmode , src2a ) ; emit_insn ( gen_cmpstrhi_op ( src1a , src2a , src1a , src2a ) ) ; emit_insn ( gen_cond_to_int ( operands [ 0 ] ) ) ; return 1 ; } </s>

This is a strcmp ( ) opcode . $ 0 is the destination ( HI ) which holds < = > 0 depending on the comparison , $ 1 is one string ( MEM : BLK ) , and $ 2 is the other ( MEM : BLK ) . We must do the comparison , and then convert the flags to a signed integer result .


<s> int m32c_expand_cpymemhi ( rtx * operands ) { rtx desta , srca , count ; rtx desto , srco , counto ; desta = XEXP ( operands [ 0 ] , 0 ) ; srca = XEXP ( operands [ 1 ] , 0 ) ; count = operands [ 2 ] ; desto = gen_reg_rtx ( Pmode ) ; srco = gen_reg_rtx ( Pmode ) ; counto = gen_reg_rtx ( HImode ) ; if ( GET_CODE ( desta ) != REG || REGNO ( desta ) < FIRST_PSEUDO_REGISTER ) desta = copy_to_mode_reg ( Pmode , desta ) ; if ( GET_CODE ( srca ) != REG || REGNO ( srca ) < FIRST_PSEUDO_REGISTER ) srca = copy_to_mode_reg ( Pmode , srca ) ; if ( GET_CODE ( count ) == CONST_INT && ! ( INTVAL ( count ) & 1 ) && ( INTVAL ( count ) > 1 ) ) { count = copy_to_mode_reg ( HImode , GEN_INT ( INTVAL ( count ) / 2 ) ) ; if ( TARGET_A16 ) emit_insn ( gen_cpymemhi_whi_op ( desto , srco , counto , desta , srca , count ) ) ; else emit_insn ( gen_cpymemhi_wpsi_op ( desto , srco , counto , desta , srca , count ) ) ; return 1 ; } if ( GET_CODE ( count ) != REG || REGNO ( count ) < FIRST_PSEUDO_REGISTER ) count = copy_to_mode_reg ( HImode , count ) ; if ( TARGET_A16 ) emit_insn ( gen_cpymemhi_bhi_op ( desto , srco , counto , desta , srca , count ) ) ; else emit_insn ( gen_cpymemhi_bpsi_op ( desto , srco , counto , desta , srca , count ) ) ; return 1 ; } </s>

This is a memcpy ( ) opcode . All operands are implied , so we need to arrange for them to be in the right registers . The opcode wants addresses , not [ mem ] syntax . $ 0 is the destination ( MEM : BLK ) , $ 1 is the source ( MEM : BLK ) , and $ 2 the count ( HI ) .


<s> int m32c_expand_movcc ( rtx * operands ) { rtx rel = operands [ 1 ] ; if ( GET_CODE ( rel ) != EQ && GET_CODE ( rel ) != NE ) return 1 ; if ( GET_CODE ( operands [ 2 ] ) != CONST_INT || GET_CODE ( operands [ 3 ] ) != CONST_INT ) return 1 ; if ( GET_CODE ( rel ) == NE ) { rtx tmp = operands [ 2 ] ; operands [ 2 ] = operands [ 3 ] ; operands [ 3 ] = tmp ; rel = gen_rtx_EQ ( GET_MODE ( rel ) , XEXP ( rel , 0 ) , XEXP ( rel , 1 ) ) ; } emit_move_insn ( operands [ 0 ] , gen_rtx_IF_THEN_ELSE ( GET_MODE ( operands [ 0 ] ) , rel , operands [ 2 ] , operands [ 3 ] ) ) ; return 0 ; } </s>


<s> int m32c_expand_movstr ( rtx * operands ) { rtx desta , srca ; rtx desto , srco ; desta = XEXP ( operands [ 1 ] , 0 ) ; srca = XEXP ( operands [ 2 ] , 0 ) ; desto = gen_reg_rtx ( Pmode ) ; srco = gen_reg_rtx ( Pmode ) ; if ( GET_CODE ( desta ) != REG || REGNO ( desta ) < FIRST_PSEUDO_REGISTER ) desta = copy_to_mode_reg ( Pmode , desta ) ; if ( GET_CODE ( srca ) != REG || REGNO ( srca ) < FIRST_PSEUDO_REGISTER ) srca = copy_to_mode_reg ( Pmode , srca ) ; emit_insn ( gen_movstr_op ( desto , srco , desta , srca ) ) ; emit_insn ( gen_addpsi3 ( operands [ 0 ] , desto , GEN_INT ( - 1 ) ) ) ; return 1 ; } </s>

This is a stpcpy ( ) opcode . $ 0 is the destination ( MEM : BLK ) after the copy , which should point to the NUL at the end of the string , $ 1 is the destination ( MEM : BLK ) , and $ 2 is the source ( MEM : BLK ) . Since our opcode leaves the destination pointing * after * the NUL , we must emit an adjustment .


<s> void m32c_expand_neg_mulpsi3 ( rtx * operands ) { rtx temp1 ; rtx scale ; rtx temp2 ; temp1 = gen_reg_rtx ( SImode ) ; temp2 = gen_reg_rtx ( SImode ) ; if ( GET_CODE ( operands [ 2 ] ) != CONST_INT ) { scale = gen_reg_rtx ( SImode ) ; emit_insn ( gen_zero_extendpsisi2 ( scale , operands [ 2 ] ) ) ; } else scale = copy_to_mode_reg ( SImode , operands [ 2 ] ) ; emit_insn ( gen_zero_extendpsisi2 ( temp1 , operands [ 1 ] ) ) ; temp2 = expand_simple_binop ( SImode , MULT , temp1 , scale , temp2 , 1 , OPTAB_LIB ) ; emit_insn ( gen_truncsipsi2 ( operands [ 0 ] , temp2 ) ) ; } </s>

The m32c has a limited range of operations that work on PSImode values ; we have to expand to SI , do the math , and truncate back to PSI . Yes , this is expensive , but hopefully gcc will learn to avoid those cases .


<s> int m32c_expand_setmemhi ( rtx * operands ) { rtx desta , count , val ; rtx desto , counto ; desta = XEXP ( operands [ 0 ] , 0 ) ; count = operands [ 1 ] ; val = operands [ 2 ] ; desto = gen_reg_rtx ( Pmode ) ; counto = gen_reg_rtx ( HImode ) ; if ( GET_CODE ( desta ) != REG || REGNO ( desta ) < FIRST_PSEUDO_REGISTER ) desta = copy_to_mode_reg ( Pmode , desta ) ; if ( GET_CODE ( count ) == CONST_INT && GET_CODE ( val ) == CONST_INT && ! ( INTVAL ( count ) & 1 ) && ( INTVAL ( count ) > 1 ) && ( INTVAL ( val ) <= 7 && INTVAL ( val ) >= - 8 ) ) { unsigned v = INTVAL ( val ) & 0xff ; v = v | ( v << 8 ) ; count = copy_to_mode_reg ( HImode , GEN_INT ( INTVAL ( count ) / 2 ) ) ; val = copy_to_mode_reg ( HImode , GEN_INT ( v ) ) ; if ( TARGET_A16 ) emit_insn ( gen_setmemhi_whi_op ( desto , counto , val , desta , count ) ) ; else emit_insn ( gen_setmemhi_wpsi_op ( desto , counto , val , desta , count ) ) ; return 1 ; } if ( GET_CODE ( val ) != REG || REGNO ( val ) < FIRST_PSEUDO_REGISTER ) val = copy_to_mode_reg ( QImode , val ) ; if ( GET_CODE ( count ) != REG || REGNO ( count ) < FIRST_PSEUDO_REGISTER ) count = copy_to_mode_reg ( HImode , count ) ; if ( TARGET_A16 ) emit_insn ( gen_setmemhi_bhi_op ( desto , counto , val , desta , count ) ) ; else emit_insn ( gen_setmemhi_bpsi_op ( desto , counto , val , desta , count ) ) ; return 1 ; } </s>

This is a memset ( ) opcode . All operands are implied , so we need to arrange for them to be in the right registers . The opcode wants addresses , not [ mem ] syntax . $ 0 is the destination ( MEM : BLK ) , $ 1 the count ( HI ) , and $ 2 the value ( QI ) .


<s>
static
rtx
m32c_function_arg
(
cumulative_args_t
ca_v
,
const
function_arg_info
&
arg
)
{
CUMULATIVE_ARGS
*
ca
=
get_cumulative_args
(
ca_v
)
;
rtx
rv
=
NULL_RTX
;
fprintf
(
stderr
,
"func_arg %d (%s, %d)\n"
,
ca
->
parm_num
,
mode_name
[
arg
.
mode
]
,
arg
.
named
)
;
debug_tree
(
arg
.
type
)
;
if
(
arg
.
end_marker_p
(
)
)
return
GEN_INT
(
0
)
;
if
(
ca
->
force_mem
||
!
arg
.
named
)
{
fprintf
(
stderr
,
"func arg: force %d named %d, mem\n"
,
ca
->
force_mem
,
arg
.
named
)
;
return
NULL_RTX
;
}
if
(
arg
.
type
&&
INTEGRAL_TYPE_P
(
arg
.
type
)
&&
POINTER_TYPE_P
(
arg
.
type
)
)
return
NULL_RTX
;
if
(
arg
.
aggregate_type_p
(
)
)
return
NULL_RTX
;
switch
(
ca
->
parm_num
)
{
case
1
:
if
(
GET_MODE_SIZE
(
arg
.
mode
)
==
1
||
GET_MODE_SIZE
(
arg
.
mode
)
==
2
)
rv
=
gen_rtx_REG
(
arg
.
mode
,
TARGET_A16
R1_REGNO
:
R0_REGNO
)
;
break
;
case
2
:
if
(
TARGET_A16
&&
GET_MODE_SIZE
(
arg
.
mode
)
==
2
)
rv
=
gen_rtx_REG
(
arg
.
mode
,
R2_REGNO
)
;
break
;
}
debug_rtx
(
rv
)
;
return
rv
;
}
</s>

Implements FUNCTION_ARG . Arguments are passed partly in registers , partly on stack . If our function returns a struct , a pointer to a buffer for it is at the top of the stack ( last thing pushed ) . The first few real arguments may be in registers as follows : R8C/M16C : arg1 in r1 if it 's QI or HI ( else it 's pushed on stack ) arg2 in r2 if it 's HI ( else pushed on stack ) rest on stack M32C : arg1 in r0 if it 's QI or HI ( else it 's pushed on stack ) rest on stack Structs are not passed in registers , even if they fit . Only integer and pointer types are passed in registers . Note that when arg1 does n't fit in r1 , arg2 may still be passed in r2 if it fits .


<s> static void m32c_function_arg_advance ( cumulative_args_t ca_v , const function_arg_info & ) { CUMULATIVE_ARGS * ca = get_cumulative_args ( ca_v ) ; if ( ca -> force_mem ) ca -> force_mem = 0 ; else ca -> parm_num ++ ; } </s>

Implements FUNCTION_ARG_ADVANCE . force_mem is set for functions returning structures , so we always reset that . Otherwise , we only need to know the sequence number of the argument to know what to do with it .


<s> int m32c_function_arg_regno_p ( int r ) { if ( TARGET_A24 ) return ( r == R0_REGNO ) ; return ( r == R1_REGNO || r == R2_REGNO ) ; } </s>


<s> static bool m32c_function_needs_enter ( void ) { rtx_insn * insn ; rtx sp = gen_rtx_REG ( Pmode , SP_REGNO ) ; rtx fb = gen_rtx_REG ( Pmode , FB_REGNO ) ; for ( insn = get_topmost_sequence ( ) -> first ; insn ; insn = NEXT_INSN ( insn ) ) if ( NONDEBUG_INSN_P ( insn ) ) { if ( reg_mentioned_p ( sp , insn ) ) return true ; if ( reg_mentioned_p ( fb , insn ) ) return true ; } return false ; } </s>

Returns TRUE if the current function needs to use the ENTER/EXIT opcodes . If the function does n't need the frame base or stack pointer , it can use the simpler RTS opcode .


<s> static rtx m32c_function_value ( const_tree valtype , const_tree fn_decl_or_type ATTRIBUTE_UNUSED , bool outgoing ATTRIBUTE_UNUSED ) { const machine_mode mode = TYPE_MODE ( valtype ) ; return m32c_libcall_value ( mode , NULL_RTX ) ; } </s>

Implements FUNCTION_VALUE . Functions and libcalls have the same conventions .


<s> static bool m32c_hard_regno_mode_ok ( unsigned int regno , machine_mode mode ) { return m32c_hard_regno_nregs_1 ( regno , mode ) != 0 ; } </s>

Implement TARGET_HARD_REGNO_MODE_OK . The above function does the work already ; just test its return value .


<s> static unsigned int m32c_hard_regno_nregs ( unsigned int regno , machine_mode mode ) { unsigned int rv = m32c_hard_regno_nregs_1 ( regno , mode ) ; return rv ? rv : 1 ; } </s>

Implements HARD_REGNO_NREGS . This is complicated by the fact that different registers are different sizes from each other , * and * may be different sizes in different chip families .


<s> static unsigned int m32c_hard_regno_nregs_1 ( unsigned int regno , machine_mode mode ) { if ( regno == FLG_REGNO && mode == CCmode ) return 1 ; if ( regno >= FIRST_PSEUDO_REGISTER ) return ( ( GET_MODE_SIZE ( mode ) + UNITS_PER_WORD - 1 ) / UNITS_PER_WORD ) ; if ( regno >= MEM0_REGNO && regno <= MEM7_REGNO ) return ( GET_MODE_SIZE ( mode ) + 1 ) / 2 ; if ( GET_MODE_SIZE ( mode ) <= 1 ) return nregs_table [ regno ] . qi_regs ; if ( GET_MODE_SIZE ( mode ) <= 2 ) return nregs_table [ regno ] . hi_regs ; if ( regno == A0_REGNO && mode == SImode && TARGET_A16 ) return 2 ; if ( ( GET_MODE_SIZE ( mode ) <= 3 || mode == PSImode ) && TARGET_A24 ) return nregs_table [ regno ] . pi_regs ; if ( GET_MODE_SIZE ( mode ) <= 4 ) return nregs_table [ regno ] . si_regs ; if ( GET_MODE_SIZE ( mode ) <= 8 ) return nregs_table [ regno ] . di_regs ; return 0 ; } </s>


<s> bool m32c_illegal_subreg_p ( rtx op ) { int offset ; unsigned int i ; machine_mode src_mode , dest_mode ; if ( GET_CODE ( op ) == MEM && ! m32c_legitimate_address_p ( Pmode , XEXP ( op , 0 ) , false ) ) { return true ; } if ( GET_CODE ( op ) != SUBREG ) return false ; dest_mode = GET_MODE ( op ) ; offset = SUBREG_BYTE ( op ) ; op = SUBREG_REG ( op ) ; src_mode = GET_MODE ( op ) ; if ( GET_MODE_SIZE ( dest_mode ) == GET_MODE_SIZE ( src_mode ) ) return false ; if ( GET_CODE ( op ) != REG ) return false ; if ( REGNO ( op ) >= MEM0_REGNO ) return false ; offset = ( 1 << offset ) ; for ( i = 0 ; i < ARRAY_SIZE ( legal_subregs ) ; i ++ ) if ( legal_subregs [ i ] . outer_mode_size == GET_MODE_SIZE ( dest_mode ) && legal_subregs [ i ] . regno == REGNO ( op ) && legal_subregs [ i ] . inner_mode_size == GET_MODE_SIZE ( src_mode ) && legal_subregs [ i ] . byte_mask & offset ) { switch ( legal_subregs [ i ] . legal_when ) { case 1 : return false ; case 16 : if ( TARGET_A16 ) return false ; break ; case 24 : if ( TARGET_A24 ) return false ; break ; } } return true ; } </s>

Returns TRUE if OP is a subreg of a hard reg which we do n't support . We also bail on MEMs with illegal addresses .


<s> rtx m32c_incoming_return_addr_rtx ( void ) { return gen_rtx_MEM ( PSImode , gen_rtx_REG ( PSImode , SP_REGNO ) ) ; } </s>

Implements INCOMING_RETURN_ADDR_RTX . See comment above .


"initial_elimination_offset from=%d to=%d, ofs=%d\n"

Implements INITIAL_ELIMINATION_OFFSET . See the comment above that diagrams our call frame .


<s> void m32c_init_cumulative_args ( CUMULATIVE_ARGS * ca , tree fntype , rtx libname ATTRIBUTE_UNUSED , tree fndecl , int n_named_args ATTRIBUTE_UNUSED ) { if ( fntype && aggregate_value_p ( TREE_TYPE ( fntype ) , fndecl ) ) ca -> force_mem = 1 ; else ca -> force_mem = 0 ; ca -> parm_num = 1 ; } </s>


<s> void m32c_init_expanders ( void ) { init_machine_status = m32c_init_machine_status ; } </s>

Implements INIT_EXPANDERS . We just set up to call the above function .


<s> static struct machine_function * m32c_init_machine_status ( void ) { return ggc_cleared_alloc < machine_function > ( ) ; } </s>

The usual ; we set up our machine_function data .


<s> static int m32c_leaf_function_p ( void ) { int rv ; push_topmost_sequence ( ) ; rv = leaf_function_p ( ) ; pop_topmost_sequence ( ) ; return rv ; } </s>

Returns TRUE if the current function is a leaf , and thus we can determine which registers an interrupt function really needs to save . The logic below is mostly about finding the insn sequence that 's the function , versus any sequence that might be open for the current insn .


<s>
bool
m32c_legitimate_address_p
(
machine_mode
mode
,
rtx
x
,
bool
strict
)
{
int
mode_adjust
;
if
(
CONSTANT_P
(
x
)
)
return
1
;
if
(
TARGET_A16
&&
GET_MODE
(
x
)
!=
HImode
&&
GET_MODE
(
x
)
!=
SImode
)
return
0
;
if
(
TARGET_A24
&&
GET_MODE
(
x
)
!=
PSImode
)
return
0
;
mode_adjust
=
GET_MODE_SIZE
(
mode
)
1
;
if
(
GET_CODE
(
x
)
==
PRE_DEC
||
GET_CODE
(
x
)
==
POST_INC
||
GET_CODE
(
x
)
==
PRE_MODIFY
)
{
return
(
GET_CODE
(
XEXP
(
x
,
0
)
)
==
REG
&&
REGNO
(
XEXP
(
x
,
0
)
)
==
SP_REGNO
)
;
}
if
(
TARGET_A24
&&
GET_CODE
(
x
)
==
MEM
&&
GET_CODE
(
XEXP
(
x
,
0
)
)
!=
PLUS
)
{
fprintf
(
stderr
,
"double indirect\n"
)
;
x
=
XEXP
(
x
,
0
)
;
}
encode_pattern
(
x
)
;
if
(
RTX_IS
(
"r"
)
)
{
switch
(
REGNO
(
patternr
[
0
]
)
)
{
case
A1_REGNO
:
case
SB_REGNO
:
case
FB_REGNO
:
case
SP_REGNO
:
if
(
TARGET_A16
&&
GET_MODE
(
x
)
==
SImode
)
return
0
;
case
A0_REGNO
:
return
1
;
default
:
if
(
IS_PSEUDO
(
patternr
[
0
]
,
strict
)
)
return
1
;
return
0
;
}
}
if
(
TARGET_A16
&&
GET_MODE
(
x
)
==
SImode
)
return
0
;
if
(
RTX_IS
(
"+ri"
)
)
{
int
rn
=
REGNO
(
patternr
[
1
]
)
;
HOST_WIDE_INT
offs
=
INTVAL
(
patternr
[
2
]
)
;
switch
(
rn
)
{
case
A0_REGNO
:
case
A1_REGNO
:
case
SB_REGNO
:
if
(
TARGET_A16
)
return
(
offs
>=
65536
&&
offs
<=
65535
mode_adjust
)
;
if
(
rn
==
SB_REGNO
)
return
(
offs
>=
0
&&
offs
<=
65535
mode_adjust
)
;
return
(
offs
>=
16777216
&&
offs
<=
16777215
)
;
case
FB_REGNO
:
if
(
TARGET_A16
)
return
(
offs
>=
128
&&
offs
<=
127
mode_adjust
)
;
return
(
offs
>=
65536
&&
offs
<=
65535
mode_adjust
)
;
case
SP_REGNO
:
return
(
offs
>=
128
&&
offs
<=
127
mode_adjust
)
;
default
:
if
(
IS_PSEUDO
(
patternr
[
1
]
,
strict
)
)
return
1
;
return
0
;
}
}
if
(
RTX_IS
(
"+rs"
)
||
RTX_IS
(
"+r+si"
)
)
{
rtx
reg
=
patternr
[
1
]
;
switch
(
REGNO
(
reg
)
)
{
case
A0_REGNO
:
case
A1_REGNO
:
return
1
;
default
:
if
(
GET_CODE
(
reg
)
==
SUBREG
)
return
0
;
if
(
IS_PSEUDO
(
reg
,
strict
)
)
return
1
;
return
0
;
}
}
return
0
;
}
</s>

Used by GO_IF_LEGITIMATE_ADDRESS . The r8c/m32c family supports a wide range of non-orthogonal addressing modes , including the ability to double-indirect on * some * of them . Not all insns support all modes , either , but we rely on predicates and constraints to deal with that .


<s>
static
rtx
m32c_legitimize_address
(
rtx
x
,
rtx
oldx
ATTRIBUTE_UNUSED
,
machine_mode
mode
)
{
fprintf
(
stderr
,
"m32c_legitimize_address for mode %s\n"
,
mode_name
[
mode
]
)
;
debug_rtx
(
x
)
;
fprintf
(
stderr
,
"\n"
)
;
if
(
GET_CODE
(
x
)
==
PLUS
&&
GET_CODE
(
XEXP
(
x
,
0
)
)
==
REG
&&
REGNO
(
XEXP
(
x
,
0
)
)
==
FB_REGNO
&&
GET_CODE
(
XEXP
(
x
,
1
)
)
==
CONST_INT
&&
(
INTVAL
(
XEXP
(
x
,
1
)
)
<
128
||
INTVAL
(
XEXP
(
x
,
1
)
)
>
(
128
GET_MODE_SIZE
(
mode
)
)
)
)
{
rtx
temp
=
gen_reg_rtx
(
Pmode
)
;
x
=
copy_rtx
(
x
)
;
emit_insn
(
gen_rtx_SET
(
temp
,
XEXP
(
x
,
0
)
)
)
;
XEXP
(
x
,
0
)
=
temp
;
}
return
x
;
}
</s>

Implements LEGITIMIZE_ADDRESS . The only address we really have to worry about is frame base offsets , as $ fb has a limited displacement range . We deal with this by attempting to reload $ fb itself into an address register ; that seems to result in the best code .


"\nm32c_legitimize_reload_address for mode %s\n"

Implements LEGITIMIZE_RELOAD_ADDRESS . See comment above .


<s> static rtx m32c_libcall_value ( machine_mode mode , const_rtx fun ATTRIBUTE_UNUSED ) { if ( mode == DFmode ) { rtx rv ; rv = gen_rtx_PARALLEL ( mode , rtvec_alloc ( 4 ) ) ; XVECEXP ( rv , 0 , 0 ) = gen_rtx_EXPR_LIST ( VOIDmode , gen_rtx_REG ( HImode , R0_REGNO ) , GEN_INT ( 0 ) ) ; XVECEXP ( rv , 0 , 1 ) = gen_rtx_EXPR_LIST ( VOIDmode , gen_rtx_REG ( HImode , R1_REGNO ) , GEN_INT ( 2 ) ) ; XVECEXP ( rv , 0 , 2 ) = gen_rtx_EXPR_LIST ( VOIDmode , gen_rtx_REG ( HImode , R2_REGNO ) , GEN_INT ( 4 ) ) ; XVECEXP ( rv , 0 , 3 ) = gen_rtx_EXPR_LIST ( VOIDmode , gen_rtx_REG ( HImode , R3_REGNO ) , GEN_INT ( 6 ) ) ; return rv ; } if ( TARGET_A24 && GET_MODE_SIZE ( mode ) > 2 ) { rtx rv ; rv = gen_rtx_PARALLEL ( mode , rtvec_alloc ( 1 ) ) ; XVECEXP ( rv , 0 , 0 ) = gen_rtx_EXPR_LIST ( VOIDmode , gen_rtx_REG ( mode , R0_REGNO ) , GEN_INT ( 0 ) ) ; return rv ; } if ( GET_MODE_SIZE ( mode ) > 2 ) return gen_rtx_REG ( mode , MEM0_REGNO ) ; return gen_rtx_REG ( mode , R0_REGNO ) ; } </s>

Implements LIBCALL_VALUE . Most values are returned in $ r0 , or some combination of registers starting there ( r2r0 for longs , r3r1r2r0 for long long , r3r2r1r0 for doubles ) , except that that ABI currently does n't work because it ends up using all available general registers and gcc often ca n't compile it . So , instead , we return anything bigger than 16 bits in `` mem0 '' ( effectively , a memory location ) .


<s>
int
m32c_limit_reload_class
(
machine_mode
mode
,
int
rclass
)
{
fprintf
(
stderr
,
"limit_reload_class for %s: %s ->"
,
mode_name
[
mode
]
,
class_names
[
rclass
]
)
;
if
(
mode
==
QImode
)
rclass
=
reduce_class
(
rclass
,
HL_REGS
,
rclass
)
;
else
if
(
mode
==
HImode
)
rclass
=
reduce_class
(
rclass
,
HI_REGS
,
rclass
)
;
else
if
(
mode
==
SImode
)
rclass
=
reduce_class
(
rclass
,
SI_REGS
,
rclass
)
;
if
(
rclass
!=
A_REGS
)
rclass
=
reduce_class
(
rclass
,
DI_REGS
,
rclass
)
;
fprintf
(
stderr
,
" %s\n"
,
class_names
[
rclass
]
)
;
return
rclass
;
}
</s>

Implements LIMIT_RELOAD_CLASS . We basically want to avoid using address registers for reloads since they 're needed for address reloads .


<s> static int m32c_memory_move_cost ( machine_mode mode ATTRIBUTE_UNUSED , reg_class_t rclass ATTRIBUTE_UNUSED , bool in ATTRIBUTE_UNUSED ) { return COSTS_N_INSNS ( 10 ) ; } </s>


<s> static bool m32c_modes_tieable_p ( machine_mode m1 , machine_mode m2 ) { if ( GET_MODE_SIZE ( m1 ) == GET_MODE_SIZE ( m2 ) ) return 1 ; if ( m1 == QImode || m2 == QImode ) return 0 ; return 1 ; } </s>

Implements MODES_TIEABLE_P . In general , modes are n't tieable since registers are all different sizes . However , since most modes are bigger than our registers anyway , it 's easier to implement this function that way , leaving QImode as the only unique case .


<s>
bool
m32c_mov_ok
(
rtx
*
operands
,
machine_mode
mode
ATTRIBUTE_UNUSED
)
{
rtx
op0
=
operands
[
0
]
;
rtx
op1
=
operands
[
1
]
;
if
(
TARGET_A24
)
return
true
;
fprintf
(
stderr
,
"m32c_mov_ok %s\n"
,
mode_name
[
mode
]
)
;
debug_rtx
(
op0
)
;
debug_rtx
(
op1
)
;
if
(
GET_CODE
(
op0
)
==
SUBREG
)
op0
=
XEXP
(
op0
,
0
)
;
if
(
GET_CODE
(
op1
)
==
SUBREG
)
op1
=
XEXP
(
op1
,
0
)
;
if
(
GET_CODE
(
op0
)
==
MEM
&&
GET_CODE
(
op1
)
==
MEM
&&
!
reload_completed
)
{
fprintf
(
stderr
,
" - no, mem to mem\n"
)
;
return
false
;
}
fprintf
(
stderr
,
" - ok\n"
)
;
return
true
;
}
</s>

Returns TRUE if we support a move between the first two operands . At the moment , we just want to discourage mem to mem moves until after reload , because reload has a hard time with our limited number of address registers , and we can get into a situation where we need three of them when we only have two .


<s>
char
*
m32c_output_compare
(
rtx_insn
*
insn
,
rtx
*
operands
)
{
static
char
templ
[
]
=
";cmp.b\t%1,%0"
;
templ
[
5
]
=
" bwll"
[
GET_MODE_SIZE
(
GET_MODE
(
operands
[
0
]
)
)
]
;
if
(
m32c_compare_redundant
(
insn
,
operands
)
)
{
fprintf
(
stderr
,
"cbranch: cmp not needed\n"
)
;
return
templ
;
}
fprintf
(
stderr
,
"cbranch: cmp needed: `%s'\n"
,
templ
+
1
)
;
return
templ
+
1
;
}
</s>

Return the pattern for a compare . This will be commented out if the compare is redundant , else a normal pattern is returned . Thus , the assembler output says where the compare would have been .


<s>
void
m32c_output_reg_pop
(
FILE
*
s
,
int
regno
)
{
if
(
regno
==
FLG_REGNO
)
fprintf
(
s
,
"\tpopc\tflg\n"
)
;
else
fprintf
(
s
,
"\tpop.%c\t%s\n"
,
" bwll"
[
reg_push_size
(
regno
)
]
,
reg_names
[
regno
]
)
;
}
</s>


<s>
void
m32c_output_reg_push
(
FILE
*
s
,
int
regno
)
{
if
(
regno
==
FLG_REGNO
)
fprintf
(
s
,
"\tpushc\tflg\n"
)
;
else
fprintf
(
s
,
"\tpush.%c\t%s\n"
,
" bwll"
[
reg_push_size
(
regno
)
]
,
reg_names
[
regno
]
)
;
}
</s>

Implements ASM_OUTPUT_REG_PUSH . Control registers are pushed differently than general registers .


<s> static reg_class_t m32c_preferred_output_reload_class ( rtx x , reg_class_t rclass ) { return m32c_preferred_reload_class ( x , rclass ) ; } </s>


<s>
static
reg_class_t
m32c_preferred_reload_class
(
rtx
x
,
reg_class_t
rclass
)
{
reg_class_t
newclass
=
rclass
;
fprintf
(
stderr
,
"\npreferred_reload_class for %s is "
,
class_names
[
rclass
]
)
;
if
(
rclass
==
NO_REGS
)
rclass
=
GET_MODE
(
x
)
==
QImode
HL_REGS
:
R03_REGS
;
if
(
reg_classes_intersect_p
(
rclass
,
CR_REGS
)
)
{
switch
(
GET_MODE
(
x
)
)
{
case
E_QImode
:
newclass
=
HL_REGS
;
break
;
default
:
break
;
}
}
else
if
(
newclass
==
QI_REGS
&&
GET_MODE_SIZE
(
GET_MODE
(
x
)
)
>
2
)
newclass
=
SI_REGS
;
else
if
(
GET_MODE_SIZE
(
GET_MODE
(
x
)
)
>
4
&&
!
reg_class_subset_p
(
R03_REGS
,
rclass
)
)
newclass
=
DI_REGS
;
rclass
=
reduce_class
(
rclass
,
newclass
,
rclass
)
;
if
(
GET_MODE
(
x
)
==
QImode
)
rclass
=
reduce_class
(
rclass
,
HL_REGS
,
rclass
)
;
fprintf
(
stderr
,
"%s\n"
,
class_names
[
rclass
]
)
;
debug_rtx
(
x
)
;
if
(
GET_CODE
(
x
)
==
MEM
&&
GET_CODE
(
XEXP
(
x
,
0
)
)
==
PLUS
&&
GET_CODE
(
XEXP
(
XEXP
(
x
,
0
)
,
0
)
)
==
PLUS
)
fprintf
(
stderr
,
"Glorm!\n"
)
;
return
rclass
;
}
</s>

Implements PREFERRED_RELOAD_CLASS . In general , prefer general registers of the appropriate size .


<s> int m32c_prepare_move ( rtx * operands , machine_mode mode ) { if ( far_addr_space_p ( operands [ 0 ] ) && CONSTANT_P ( operands [ 1 ] ) ) { operands [ 1 ] = force_reg ( GET_MODE ( operands [ 0 ] ) , operands [ 1 ] ) ; } if ( TARGET_A16 && mode == PSImode ) return m32c_split_move ( operands , mode , 1 ) ; if ( ( GET_CODE ( operands [ 0 ] ) == MEM ) && ( GET_CODE ( XEXP ( operands [ 0 ] , 0 ) ) == PRE_MODIFY ) ) { rtx pmv = XEXP ( operands [ 0 ] , 0 ) ; rtx dest_reg = XEXP ( pmv , 0 ) ; rtx dest_mod = XEXP ( pmv , 1 ) ; emit_insn ( gen_rtx_SET ( dest_reg , dest_mod ) ) ; operands [ 0 ] = gen_rtx_MEM ( mode , dest_reg ) ; } if ( can_create_pseudo_p ( ) && MEM_P ( operands [ 0 ] ) && MEM_P ( operands [ 1 ] ) ) operands [ 1 ] = copy_to_mode_reg ( mode , operands [ 1 ] ) ; return 0 ; } </s>

Used to emit move instructions . We split some moves , and avoid mem-mem moves .


<s> static void m32c_print_operand_address ( FILE * stream , machine_mode , rtx address ) { if ( GET_CODE ( address ) == MEM ) address = XEXP ( address , 0 ) ; else gcc_assert ( GET_CODE ( address ) == REG ) ; m32c_print_operand ( stream , address , 0 ) ; } </s>

Implements PRINT_OPERAND_ADDRESS . Nothing unusual here .


<s> static bool m32c_print_operand_punct_valid_p ( unsigned char c ) { if ( c == '&' || c == '!' ) return true ; return false ; } </s>

Implements PRINT_OPERAND_PUNCT_VALID_P . See m32c_print_operand above for descriptions of what these do .


<s> poly_int64 m32c_push_rounding ( poly_int64 n ) { if ( TARGET_R8C || TARGET_M16C ) return n ; return ( n + 1 ) & ~ 1 ; } </s>

Implements PUSH_ROUNDING . The R8C and M16C have byte stacks , the M32C has word stacks .


"register_move_cost %s from %s to %s = %d\n"

Implements REGISTER_MOVE_COST . We make impossible moves prohibitively expensive , like trying to put QIs in r2/r3 ( there are no opcodes to do that ) . We also discourage use of mem * registers since they 're really memory .


<s> int m32c_regno_ok_for_base_p ( int regno ) { if ( regno == A0_REGNO || regno == A1_REGNO || regno >= FIRST_PSEUDO_REGISTER ) return 1 ; return 0 ; } </s>


<s> enum reg_class m32c_regno_reg_class ( int regno ) { switch ( regno ) { case R0_REGNO : return R0_REGS ; case R1_REGNO : return R1_REGS ; case R2_REGNO : return R2_REGS ; case R3_REGNO : return R3_REGS ; case A0_REGNO : return A0_REGS ; case A1_REGNO : return A1_REGS ; case SB_REGNO : return SB_REGS ; case FB_REGNO : return FB_REGS ; case SP_REGNO : return SP_REGS ; case FLG_REGNO : return FLG_REGS ; default : if ( IS_MEM_REGNO ( regno ) ) return MEM_REGS ; return ALL_REGS ; } } </s>


<s> int m32c_reg_ok_for_base_p ( rtx x , int strict ) { if ( GET_CODE ( x ) != REG ) return 0 ; switch ( REGNO ( x ) ) { case A0_REGNO : case A1_REGNO : case SB_REGNO : case FB_REGNO : case SP_REGNO : return 1 ; default : if ( IS_PSEUDO ( x , strict ) ) return 1 ; return 0 ; } } </s>


<s> rtx m32c_return_addr_rtx ( int count ) { machine_mode mode ; int offset ; rtx ra_mem ; if ( count ) return NULL_RTX ; if ( TARGET_A24 ) { mode = PSImode ; offset = 4 ; } else { mode = HImode ; offset = 2 ; } ra_mem = gen_rtx_MEM ( mode , plus_constant ( Pmode , gen_rtx_REG ( Pmode , FP_REGNO ) , offset ) ) ; return copy_to_mode_reg ( mode , ra_mem ) ; } </s>

Implements RETURN_ADDR_RTX . Note that R8C and M16C push 24 bits ( yes , THREE bytes ) onto the stack for the return address , but we do n't support pointers bigger than 16 bits on those chips . This will likely wreak havoc with exception unwinding . FIXME .


<s>
int
m32c_secondary_reload_class
(
int
rclass
,
machine_mode
mode
,
rtx
x
)
{
int
cc
=
class_contents
[
rclass
]
[
0
]
;
fprintf
(
stderr
,
"\nsecondary reload class %s %s\n"
,
class_names
[
rclass
]
,
mode_name
[
mode
]
)
;
debug_rtx
(
x
)
;
if
(
mode
==
QImode
&&
GET_CODE
(
x
)
==
MEM
&&
(
cc
&
~
class_contents
[
R23_REGS
]
[
0
]
)
==
0
)
return
QI_REGS
;
if
(
reg_classes_intersect_p
(
rclass
,
CR_REGS
)
&&
GET_CODE
(
x
)
==
REG
&&
REGNO
(
x
)
>=
SB_REGNO
&&
REGNO
(
x
)
<=
SP_REGNO
)
return
(
TARGET_A16
||
mode
==
HImode
)
HI_REGS
:
A_REGS
;
return
NO_REGS
;
}
</s>

Implements SECONDARY_RELOAD_CLASS . QImode have to be reloaded in r0 or r1 , as those are the only real QImode registers . CR regs get reloaded through appropriately sized general or address registers .


<s> int m32c_special_page_vector_p ( tree func ) { tree list ; if ( TREE_CODE ( func ) != FUNCTION_DECL ) return 0 ; list = M32C_ATTRIBUTES ( func ) ; while ( list ) { if ( is_attribute_p ( "function_vector" , TREE_PURPOSE ( list ) ) ) return 1 ; list = TREE_CHAIN ( list ) ; } return 0 ; } </s>

Returns TRUE if given tree has the `` function_vector '' attribute .


<s>
int
m32c_split_psi_p
(
rtx
*
operands
)
{
fprintf
(
stderr
,
"\nm32c_split_psi_p\n"
)
;
debug_rtx
(
operands
[
0
]
)
;
debug_rtx
(
operands
[
1
]
)
;
if
(
TARGET_A16
)
{
fprintf
(
stderr
,
"yes, A16\n"
)
;
return
1
;
}
if
(
GET_CODE
(
operands
[
1
]
)
==
MEM
&&
GET_CODE
(
XEXP
(
operands
[
1
]
,
0
)
)
==
POST_INC
)
{
fprintf
(
stderr
,
"yes, pop.l\n"
)
;
return
1
;
}
fprintf
(
stderr
,
"no, default\n"
)
;
return
0
;
}
</s>

Returns TRUE if the given PSImode move should be split . We split for all r8c/m16c moves , since it does n't support them , and for POP.L as we can only * push * SImode .


<s> int m32c_trampoline_alignment ( void ) { return 2 ; } </s>


<s> int m32c_trampoline_size ( void ) { return TARGET_A16 ? 8 : 10 ; } </s>


<s> static int need_to_save ( int regno ) { if ( fixed_regs [ regno ] ) return 0 ; if ( crtl -> calls_eh_return ) return 1 ; if ( regno == FP_REGNO ) return 0 ; if ( cfun -> machine -> is_interrupt && ( ! cfun -> machine -> is_leaf || ( regno == A0_REGNO && m32c_function_needs_enter ( ) ) ) ) return 1 ; if ( df_regs_ever_live_p ( regno ) && ( ! call_used_or_fixed_reg_p ( regno ) || cfun -> machine -> is_interrupt ) ) return 1 ; return 0 ; } </s>

Returns true if the given register needs to be saved by the current function .


<s> static reg_class_t reduce_class ( reg_class_t original_class , reg_class_t limiting_class , reg_class_t returned_if_empty ) { HARD_REG_SET cc ; int i ; reg_class_t best = NO_REGS ; unsigned int best_size = 0 ; if ( original_class == limiting_class ) return original_class ; cc = reg_class_contents [ original_class ] & reg_class_contents [ limiting_class ] ; for ( i = 0 ; i < LIM_REG_CLASSES ; i ++ ) { if ( hard_reg_set_subset_p ( reg_class_contents [ i ] , cc ) ) if ( best_size < reg_class_size [ i ] ) { best = ( reg_class_t ) i ; best_size = reg_class_size [ i ] ; } } if ( best == NO_REGS ) return returned_if_empty ; return best ; } </s>

Given two register classes , find the largest intersection between them . If there is no intersection , return RETURNED_IF_EMPTY instead .


<s> static const char * reg_name_with_mode ( int regno , machine_mode mode ) { int mlen = GET_MODE_SIZE ( mode ) ; if ( regno == R0_REGNO && mlen == 1 ) return "r0l" ; if ( regno == R0_REGNO && ( mlen == 3 || mlen == 4 ) ) return "r2r0" ; if ( regno == R0_REGNO && mlen == 6 ) return "r2r1r0" ; if ( regno == R0_REGNO && mlen == 8 ) return "r3r1r2r0" ; if ( regno == R1_REGNO && mlen == 1 ) return "r1l" ; if ( regno == R1_REGNO && ( mlen == 3 || mlen == 4 ) ) return "r3r1" ; if ( regno == A0_REGNO && TARGET_A16 && ( mlen == 3 || mlen == 4 ) ) return "a1a0" ; return reg_names [ regno ] ; } </s>

Since register names indicate the mode they 're used in , we need a way to determine which name to refer to the register with . Called by print_operand ( ) .


<s> static int reg_push_size ( int regno ) { switch ( regno ) { case R0_REGNO : case R1_REGNO : return 2 ; case R2_REGNO : case R3_REGNO : case FLG_REGNO : return 2 ; case A0_REGNO : case A1_REGNO : case SB_REGNO : case FB_REGNO : case SP_REGNO : if ( TARGET_A16 ) return 2 ; else return 3 ; default : gcc_unreachable ( ) ; } } </s>

How many bytes a register uses on stack when it 's pushed . We need to know this because the push opcode needs to explicitly indicate the size of the register , even though the name of the register already tells it that . Used by m32c_output_reg_ { push , pop } , which is only used through calls to ASM_OUTPUT_REG_ { PUSH , POP } .