#include "config.h"
#include "system.h"
#include "rtl.h"
#include "tree.h"
#include "obstack.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
#include "reload.h"
#include "function.h"
#include "expr.h"
#include "optabs.h"
#include "toplev.h"
#include "recog.h"
#include "ggc.h"
#include "except.h"
#include "c-pragma.h"
#include "integrate.h"
#include "tm_p.h"
#include "target.h"
#include "target-def.h"
typedef struct minipool_node Mnode;
typedef struct minipool_fixup Mfix;
#define Hint HOST_WIDE_INT
#define Mmode enum machine_mode
#define Ulong unsigned long
#define Ccstar const char *
const struct attribute_spec arm_attribute_table[];
static void arm_add_gc_roots PARAMS ((void));
static int arm_gen_constant PARAMS ((enum rtx_code, Mmode, Hint, rtx, rtx, int, int));
static unsigned bit_count PARAMS ((Ulong));
static int const_ok_for_op PARAMS ((Hint, enum rtx_code));
static int eliminate_lr2ip PARAMS ((rtx *));
static rtx emit_multi_reg_push PARAMS ((int));
static rtx emit_sfm PARAMS ((int, int));
#ifndef AOF_ASSEMBLER
static bool arm_assemble_integer PARAMS ((rtx, unsigned int, int));
#endif
static Ccstar fp_const_from_val PARAMS ((REAL_VALUE_TYPE *));
static arm_cc get_arm_condition_code PARAMS ((rtx));
static void init_fpa_table PARAMS ((void));
static Hint int_log2 PARAMS ((Hint));
static rtx is_jump_table PARAMS ((rtx));
static Ccstar output_multi_immediate PARAMS ((rtx *, Ccstar, Ccstar, int, Hint));
static void print_multi_reg PARAMS ((FILE *, Ccstar, int, int));
static Mmode select_dominance_cc_mode PARAMS ((rtx, rtx, Hint));
static Ccstar shift_op PARAMS ((rtx, Hint *));
static struct machine_function * arm_init_machine_status PARAMS ((void));
static int number_of_first_bit_set PARAMS ((int));
static void replace_symbols_in_block PARAMS ((tree, rtx, rtx));
static void thumb_exit PARAMS ((FILE *, int, rtx));
static void thumb_pushpop PARAMS ((FILE *, int, int));
static Ccstar thumb_condition_code PARAMS ((rtx, int));
static rtx is_jump_table PARAMS ((rtx));
static Hint get_jump_table_size PARAMS ((rtx));
static Mnode * move_minipool_fix_forward_ref PARAMS ((Mnode *, Mnode *, Hint));
static Mnode * add_minipool_forward_ref PARAMS ((Mfix *));
static Mnode * move_minipool_fix_backward_ref PARAMS ((Mnode *, Mnode *, Hint));
static Mnode * add_minipool_backward_ref PARAMS ((Mfix *));
static void assign_minipool_offsets PARAMS ((Mfix *));
static void arm_print_value PARAMS ((FILE *, rtx));
static void dump_minipool PARAMS ((rtx));
static int arm_barrier_cost PARAMS ((rtx));
static Mfix * create_fix_barrier PARAMS ((Mfix *, Hint));
static void push_minipool_barrier PARAMS ((rtx, Hint));
static void push_minipool_fix PARAMS ((rtx, Hint, rtx *, Mmode, rtx));
static void note_invalid_constants PARAMS ((rtx, Hint));
static int current_file_function_operand PARAMS ((rtx));
static Ulong arm_compute_save_reg0_reg12_mask PARAMS ((void));
static Ulong arm_compute_save_reg_mask PARAMS ((void));
static Ulong arm_isr_value PARAMS ((tree));
static Ulong arm_compute_func_type PARAMS ((void));
static tree arm_handle_fndecl_attribute PARAMS ((tree *, tree, tree, int, bool *));
static tree arm_handle_isr_attribute PARAMS ((tree *, tree, tree, int, bool *));
static void arm_output_function_epilogue PARAMS ((FILE *, Hint));
static void arm_output_function_prologue PARAMS ((FILE *, Hint));
static void thumb_output_function_prologue PARAMS ((FILE *, Hint));
static int arm_comp_type_attributes PARAMS ((tree, tree));
static void arm_set_default_type_attributes PARAMS ((tree));
static int arm_adjust_cost PARAMS ((rtx, rtx, rtx, int));
static int count_insns_for_constant PARAMS ((HOST_WIDE_INT, int));
static int arm_get_strip_length PARAMS ((int));
#ifdef OBJECT_FORMAT_ELF
static void arm_elf_asm_named_section PARAMS ((const char *, unsigned int));
#endif
#ifndef ARM_PE
static void arm_encode_section_info PARAMS ((tree, int));
#endif
#ifdef AOF_ASSEMBLER
static void aof_globalize_label PARAMS ((FILE *, const char *));
#endif
static void arm_output_mi_thunk PARAMS ((FILE *, tree,
HOST_WIDE_INT,
HOST_WIDE_INT, tree));
#undef Hint
#undef Mmode
#undef Ulong
#undef Ccstar
#ifdef TARGET_DLLIMPORT_DECL_ATTRIBUTES
#undef TARGET_MERGE_DECL_ATTRIBUTES
#define TARGET_MERGE_DECL_ATTRIBUTES merge_dllimport_decl_attributes
#endif
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE arm_attribute_table
#ifdef AOF_ASSEMBLER
#undef TARGET_ASM_BYTE_OP
#define TARGET_ASM_BYTE_OP "\tDCB\t"
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\tDCW\t"
#undef TARGET_ASM_ALIGNED_SI_OP
#define TARGET_ASM_ALIGNED_SI_OP "\tDCD\t"
#undef TARGET_ASM_GLOBALIZE_LABEL
#define TARGET_ASM_GLOBALIZE_LABEL aof_globalize_label
#else
#undef TARGET_ASM_ALIGNED_SI_OP
#define TARGET_ASM_ALIGNED_SI_OP NULL
#undef TARGET_ASM_INTEGER
#define TARGET_ASM_INTEGER arm_assemble_integer
#endif
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE arm_output_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE arm_output_function_epilogue
#undef TARGET_COMP_TYPE_ATTRIBUTES
#define TARGET_COMP_TYPE_ATTRIBUTES arm_comp_type_attributes
#undef TARGET_SET_DEFAULT_TYPE_ATTRIBUTES
#define TARGET_SET_DEFAULT_TYPE_ATTRIBUTES arm_set_default_type_attributes
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS arm_init_builtins
#undef TARGET_EXPAND_BUILTIN
#define TARGET_EXPAND_BUILTIN arm_expand_builtin
#undef TARGET_SCHED_ADJUST_COST
#define TARGET_SCHED_ADJUST_COST arm_adjust_cost
#undef TARGET_ENCODE_SECTION_INFO
#ifdef ARM_PE
#define TARGET_ENCODE_SECTION_INFO arm_pe_encode_section_info
#else
#define TARGET_ENCODE_SECTION_INFO arm_encode_section_info
#endif
#undef TARGET_STRIP_NAME_ENCODING
#define TARGET_STRIP_NAME_ENCODING arm_strip_name_encoding
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK arm_output_mi_thunk
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
struct gcc_target targetm = TARGET_INITIALIZER;
static struct obstack minipool_obstack;
static char * minipool_startobj;
static int max_insns_skipped = 5;
extern FILE * asm_out_file;
int making_const_table;
rtx arm_compare_op0, arm_compare_op1;
enum floating_point_type arm_fpu;
enum floating_point_type arm_fpu_arch;
enum prog_mode_type arm_prgmode;
const char * target_fp_name = NULL;
const char * structure_size_string = NULL;
int arm_structure_size_boundary = DEFAULT_STRUCTURE_SIZE_BOUNDARY;
#define FL_CO_PROC (1 << 0)
#define FL_FAST_MULT (1 << 1)
#define FL_MODE26 (1 << 2)
#define FL_MODE32 (1 << 3)
#define FL_ARCH4 (1 << 4)
#define FL_ARCH5 (1 << 5)
#define FL_THUMB (1 << 6)
#define FL_LDSCHED (1 << 7)
#define FL_STRONG (1 << 8)
#define FL_ARCH5E (1 << 9)
#define FL_XSCALE (1 << 10)
static unsigned long insn_flags = 0;
static unsigned long tune_flags = 0;
int arm_fast_multiply = 0;
int arm_arch4 = 0;
int arm_arch5 = 0;
int arm_arch5e = 0;
int arm_ld_sched = 0;
int arm_is_strong = 0;
int arm_is_xscale = 0;
int arm_is_6_or_7 = 0;
int thumb_code = 0;
enum machine_mode output_memory_reference_mode;
const char * arm_pic_register_string = NULL;
int arm_pic_register = INVALID_REGNUM;
int return_used_this_function;
static int after_arm_reorg = 0;
static int arm_constant_limit = 3;
int arm_ccfsm_state;
enum arm_cond_code arm_current_cc;
rtx arm_target_insn;
int arm_target_label;
static const char * const arm_condition_codes[] =
{
"eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
"hi", "ls", "ge", "lt", "gt", "le", "al", "nv"
};
#define streq(string1, string2) (strcmp (string1, string2) == 0)
struct processors
{
const char *const name;
const unsigned long flags;
};
static const struct processors all_cores[] =
{
{"arm2", FL_CO_PROC | FL_MODE26 },
{"arm250", FL_CO_PROC | FL_MODE26 },
{"arm3", FL_CO_PROC | FL_MODE26 },
{"arm6", FL_CO_PROC | FL_MODE26 | FL_MODE32 },
{"arm60", FL_CO_PROC | FL_MODE26 | FL_MODE32 },
{"arm600", FL_CO_PROC | FL_MODE26 | FL_MODE32 },
{"arm610", FL_MODE26 | FL_MODE32 },
{"arm620", FL_CO_PROC | FL_MODE26 | FL_MODE32 },
{"arm7", FL_CO_PROC | FL_MODE26 | FL_MODE32 },
{"arm7m", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT },
{"arm7d", FL_CO_PROC | FL_MODE26 | FL_MODE32 },
{"arm7dm", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT },
{"arm7di", FL_CO_PROC | FL_MODE26 | FL_MODE32 },
{"arm7dmi", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT },
{"arm70", FL_CO_PROC | FL_MODE26 | FL_MODE32 },
{"arm700", FL_CO_PROC | FL_MODE26 | FL_MODE32 },
{"arm700i", FL_CO_PROC | FL_MODE26 | FL_MODE32 },
{"arm710", FL_MODE26 | FL_MODE32 },
{"arm710t", FL_MODE26 | FL_MODE32 | FL_THUMB },
{"arm720", FL_MODE26 | FL_MODE32 },
{"arm720t", FL_MODE26 | FL_MODE32 | FL_THUMB },
{"arm740t", FL_MODE26 | FL_MODE32 | FL_THUMB },
{"arm710c", FL_MODE26 | FL_MODE32 },
{"arm7100", FL_MODE26 | FL_MODE32 },
{"arm7500", FL_MODE26 | FL_MODE32 },
{"arm7500fe", FL_CO_PROC | FL_MODE26 | FL_MODE32 },
{"arm7tdmi", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB },
{"arm8", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED },
{"arm810", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED },
{"arm9", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED },
{"arm920", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED },
{"arm920t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED },
{"arm940t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED },
{"arm9tdmi", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED },
{"arm9e", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED },
{"strongarm", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG },
{"strongarm110", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG },
{"strongarm1100", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG },
{"strongarm1110", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG },
{"arm10tdmi", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_ARCH5 },
{"arm1020t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_ARCH5 },
{"xscale", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_STRONG | FL_ARCH5 | FL_ARCH5E | FL_XSCALE },
{NULL, 0}
};
static const struct processors all_architectures[] =
{
{ "armv2", FL_CO_PROC | FL_MODE26 },
{ "armv2a", FL_CO_PROC | FL_MODE26 },
{ "armv3", FL_CO_PROC | FL_MODE26 | FL_MODE32 },
{ "armv3m", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT },
{ "armv4", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 },
{ "armv4t", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB },
{ "armv5", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 },
{ "armv5t", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 },
{ "armv5te", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 | FL_ARCH5E },
{ NULL, 0 }
};
struct arm_cpu_select arm_select[] =
{
{ NULL, "-mcpu=", all_cores },
{ NULL, "-march=", all_architectures },
{ NULL, "-mtune=", all_cores }
};
static unsigned
bit_count (value)
unsigned long value;
{
unsigned long count = 0;
while (value)
{
count++;
value &= value - 1;
}
return count;
}
void
arm_override_options ()
{
unsigned i;
for (i = ARRAY_SIZE (arm_select); i--;)
{
struct arm_cpu_select * ptr = arm_select + i;
if (ptr->string != NULL && ptr->string[0] != '\0')
{
const struct processors * sel;
for (sel = ptr->processors; sel->name != NULL; sel++)
if (streq (ptr->string, sel->name))
{
if (i == 2)
tune_flags = sel->flags;
else
{
if (insn_flags != 0 && (insn_flags ^ sel->flags))
warning ("switch -mcpu=%s conflicts with -march= switch",
ptr->string);
insn_flags = sel->flags;
}
break;
}
if (sel->name == NULL)
error ("bad value (%s) for %s switch", ptr->string, ptr->name);
}
}
if (insn_flags == 0)
{
const struct processors * sel;
unsigned int sought;
static const struct cpu_default
{
const int cpu;
const char *const name;
}
cpu_defaults[] =
{
{ TARGET_CPU_arm2, "arm2" },
{ TARGET_CPU_arm6, "arm6" },
{ TARGET_CPU_arm610, "arm610" },
{ TARGET_CPU_arm710, "arm710" },
{ TARGET_CPU_arm7m, "arm7m" },
{ TARGET_CPU_arm7500fe, "arm7500fe" },
{ TARGET_CPU_arm7tdmi, "arm7tdmi" },
{ TARGET_CPU_arm8, "arm8" },
{ TARGET_CPU_arm810, "arm810" },
{ TARGET_CPU_arm9, "arm9" },
{ TARGET_CPU_strongarm, "strongarm" },
{ TARGET_CPU_xscale, "xscale" },
{ TARGET_CPU_generic, "arm" },
{ 0, 0 }
};
const struct cpu_default * def;
for (def = cpu_defaults; def->name; def++)
if (def->cpu == TARGET_CPU_DEFAULT)
break;
if (def->name == NULL)
abort ();
for (sel = all_cores; sel->name != NULL; sel++)
if (streq (def->name, sel->name))
break;
if (sel->name == NULL)
abort ();
insn_flags = sel->flags;
sought = 0;
if (TARGET_INTERWORK || TARGET_THUMB)
{
sought |= (FL_THUMB | FL_MODE32);
target_flags |= ARM_FLAG_APCS_32;
insn_flags &= ~FL_MODE26;
}
else if (!TARGET_APCS_32)
sought |= FL_MODE26;
if (sought != 0 && ((sought & insn_flags) != sought))
{
for (sel = all_cores; sel->name != NULL; sel++)
if ((sel->flags & sought) == (sought | insn_flags))
break;
if (sel->name == NULL)
{
unsigned current_bit_count = 0;
const struct processors * best_fit = NULL;
for (sel = all_cores; sel->name != NULL; sel++)
if ((sel->flags & sought) == sought)
{
unsigned count;
count = bit_count (sel->flags & insn_flags);
if (count >= current_bit_count)
{
best_fit = sel;
current_bit_count = count;
}
}
if (best_fit == NULL)
abort ();
else
sel = best_fit;
}
insn_flags = sel->flags;
}
}
if (tune_flags == 0)
tune_flags = insn_flags;
if (TARGET_APCS_32 && !(insn_flags & FL_MODE32))
{
if ((TARGET_DEFAULT & ARM_FLAG_APCS_32) == 0)
warning ("target CPU does not support APCS-32" );
target_flags &= ~ARM_FLAG_APCS_32;
}
else if (!TARGET_APCS_32 && !(insn_flags & FL_MODE26))
{
warning ("target CPU does not support APCS-26" );
target_flags |= ARM_FLAG_APCS_32;
}
if (TARGET_INTERWORK && !(insn_flags & FL_THUMB))
{
warning ("target CPU does not support interworking" );
target_flags &= ~ARM_FLAG_INTERWORK;
}
if (TARGET_THUMB && !(insn_flags & FL_THUMB))
{
warning ("target CPU does not support THUMB instructions");
target_flags &= ~ARM_FLAG_THUMB;
}
if (TARGET_APCS_FRAME && TARGET_THUMB)
{
target_flags &= ~ARM_FLAG_APCS_FRAME;
}
if ((target_flags & (THUMB_FLAG_LEAF_BACKTRACE | THUMB_FLAG_BACKTRACE))
&& TARGET_ARM)
warning ("enabling backtrace support is only meaningful when compiling for the Thumb");
if (TARGET_ARM && TARGET_CALLEE_INTERWORKING)
warning ("enabling callee interworking support is only meaningful when compiling for the Thumb");
if (TARGET_ARM && TARGET_CALLER_INTERWORKING)
warning ("enabling caller interworking support is only meaningful when compiling for the Thumb");
if (TARGET_INTERWORK)
{
if (!TARGET_APCS_32)
warning ("interworking forces APCS-32 to be used" );
target_flags |= ARM_FLAG_APCS_32;
}
if (TARGET_APCS_STACK && !TARGET_APCS_FRAME)
{
warning ("-mapcs-stack-check incompatible with -mno-apcs-frame");
target_flags |= ARM_FLAG_APCS_FRAME;
}
if (TARGET_POKE_FUNCTION_NAME)
target_flags |= ARM_FLAG_APCS_FRAME;
if (TARGET_APCS_REENT && flag_pic)
error ("-fpic and -mapcs-reent are incompatible");
if (TARGET_APCS_REENT)
warning ("APCS reentrant code not supported. Ignored");
if (TARGET_ARM
&& write_symbols != NO_DEBUG
&& !TARGET_APCS_FRAME
&& (TARGET_DEFAULT & ARM_FLAG_APCS_FRAME))
warning ("-g with -mno-apcs-frame may not give sensible debugging");
if (flag_pic)
arm_pic_register = TARGET_APCS_STACK ? 9 : 10;
if (TARGET_APCS_FLOAT)
warning ("passing floating point arguments in fp regs not yet supported");
arm_fast_multiply = (insn_flags & FL_FAST_MULT) != 0;
arm_arch4 = (insn_flags & FL_ARCH4) != 0;
arm_arch5 = (insn_flags & FL_ARCH5) != 0;
arm_arch5e = (insn_flags & FL_ARCH5E) != 0;
arm_is_xscale = (insn_flags & FL_XSCALE) != 0;
arm_ld_sched = (tune_flags & FL_LDSCHED) != 0;
arm_is_strong = (tune_flags & FL_STRONG) != 0;
thumb_code = (TARGET_ARM == 0);
arm_is_6_or_7 = (((tune_flags & (FL_MODE26 | FL_MODE32))
&& !(tune_flags & FL_ARCH4))) != 0;
arm_fpu = (tune_flags & FL_CO_PROC) ? FP_HARD : FP_SOFT3;
if (target_fp_name)
{
if (streq (target_fp_name, "2"))
arm_fpu_arch = FP_SOFT2;
else if (streq (target_fp_name, "3"))
arm_fpu_arch = FP_SOFT3;
else
error ("invalid floating point emulation option: -mfpe-%s",
target_fp_name);
}
else
arm_fpu_arch = FP_DEFAULT;
if (TARGET_FPE && arm_fpu != FP_HARD)
arm_fpu = FP_SOFT2;
if ((TARGET_SOFT_FLOAT || arm_fpu != FP_HARD)
&& (tune_flags & FL_MODE32) == 0)
flag_schedule_insns = flag_schedule_insns_after_reload = 0;
arm_prgmode = TARGET_APCS_32 ? PROG_MODE_PROG32 : PROG_MODE_PROG26;
if (structure_size_string != NULL)
{
int size = strtol (structure_size_string, NULL, 0);
if (size == 8 || size == 32)
arm_structure_size_boundary = size;
else
warning ("structure size boundary can only be set to 8 or 32");
}
if (arm_pic_register_string != NULL)
{
int pic_register = decode_reg_name (arm_pic_register_string);
if (!flag_pic)
warning ("-mpic-register= is useless without -fpic");
else if (pic_register < 0 || call_used_regs[pic_register]
|| pic_register == HARD_FRAME_POINTER_REGNUM
|| pic_register == STACK_POINTER_REGNUM
|| pic_register >= PC_REGNUM)
error ("unable to use '%s' for PIC register", arm_pic_register_string);
else
arm_pic_register = pic_register;
}
if (TARGET_THUMB && flag_schedule_insns)
{
flag_schedule_insns = 0;
}
if (optimize_size || (tune_flags & FL_LDSCHED))
arm_constant_limit = 1;
if (arm_is_xscale)
arm_constant_limit = 2;
if (optimize_size)
max_insns_skipped = 6;
else if (arm_is_strong)
max_insns_skipped = 3;
arm_add_gc_roots ();
}
static void
arm_add_gc_roots ()
{
gcc_obstack_init(&minipool_obstack);
minipool_startobj = (char *) obstack_alloc (&minipool_obstack, 0);
}
typedef struct
{
const char *const arg;
const unsigned long return_value;
}
isr_attribute_arg;
static const isr_attribute_arg isr_attribute_args [] =
{
{ "IRQ", ARM_FT_ISR },
{ "irq", ARM_FT_ISR },
{ "FIQ", ARM_FT_FIQ },
{ "fiq", ARM_FT_FIQ },
{ "ABORT", ARM_FT_ISR },
{ "abort", ARM_FT_ISR },
{ "ABORT", ARM_FT_ISR },
{ "abort", ARM_FT_ISR },
{ "UNDEF", ARM_FT_EXCEPTION },
{ "undef", ARM_FT_EXCEPTION },
{ "SWI", ARM_FT_EXCEPTION },
{ "swi", ARM_FT_EXCEPTION },
{ NULL, ARM_FT_NORMAL }
};
static unsigned long
arm_isr_value (argument)
tree argument;
{
const isr_attribute_arg * ptr;
const char * arg;
if (argument == NULL_TREE)
return ARM_FT_ISR;
if (TREE_VALUE (argument) == NULL_TREE
|| TREE_CODE (TREE_VALUE (argument)) != STRING_CST)
return ARM_FT_UNKNOWN;
arg = TREE_STRING_POINTER (TREE_VALUE (argument));
for (ptr = isr_attribute_args; ptr->arg != NULL; ptr ++)
if (streq (arg, ptr->arg))
return ptr->return_value;
return ARM_FT_UNKNOWN;
}
static unsigned long
arm_compute_func_type ()
{
unsigned long type = ARM_FT_UNKNOWN;
tree a;
tree attr;
if (TREE_CODE (current_function_decl) != FUNCTION_DECL)
abort ();
if (optimize > 0
&& current_function_nothrow
&& TREE_THIS_VOLATILE (current_function_decl))
type |= ARM_FT_VOLATILE;
if (current_function_needs_context)
type |= ARM_FT_NESTED;
attr = DECL_ATTRIBUTES (current_function_decl);
a = lookup_attribute ("naked", attr);
if (a != NULL_TREE)
type |= ARM_FT_NAKED;
if (cfun->machine->eh_epilogue_sp_ofs != NULL_RTX)
type |= ARM_FT_EXCEPTION_HANDLER;
else
{
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;
}
unsigned long
arm_current_func_type ()
{
if (ARM_FUNC_TYPE (cfun->machine->func_type) == ARM_FT_UNKNOWN)
cfun->machine->func_type = arm_compute_func_type ();
return cfun->machine->func_type;
}
int
use_return_insn (iscond)
int iscond;
{
int regno;
unsigned int func_type;
unsigned long saved_int_regs;
if (!reload_completed)
return 0;
func_type = arm_current_func_type ();
if (func_type & (ARM_FT_VOLATILE | ARM_FT_NAKED))
return 0;
if (current_function_pretend_args_size
|| cfun->machine->uses_anonymous_args
|| ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER
|| ((arm_get_frame_size () + current_function_outgoing_args_size != 0)
&& !frame_pointer_needed))
return 0;
saved_int_regs = arm_compute_save_reg_mask ();
if (TARGET_INTERWORK && saved_int_regs != 0)
return 0;
if (iscond && arm_is_strong)
{
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)
for (regno = FIRST_ARM_FP_REGNUM; regno <= LAST_ARM_FP_REGNUM; regno++)
if (regs_ever_live[regno] && !call_used_regs[regno])
return 0;
return 1;
}
int
const_ok_for_arm (i)
HOST_WIDE_INT i;
{
unsigned HOST_WIDE_INT mask = ~(unsigned HOST_WIDE_INT)0xFF;
if ((i & ~(unsigned HOST_WIDE_INT) 0xffffffff) != 0
&& ((i & ~(unsigned HOST_WIDE_INT) 0xffffffff)
!= ((~(unsigned HOST_WIDE_INT) 0)
& ~(unsigned HOST_WIDE_INT) 0xffffffff)))
return FALSE;
if ((i & (i - 1)) == 0)
return TRUE;
do
{
if ((i & mask & (unsigned HOST_WIDE_INT) 0xffffffff) == 0)
return TRUE;
mask =
(mask << 2) | ((mask & (unsigned HOST_WIDE_INT) 0xffffffff)
>> (32 - 2)) | ~(unsigned HOST_WIDE_INT) 0xffffffff;
}
while (mask != ~(unsigned HOST_WIDE_INT) 0xFF);
return FALSE;
}
static int
const_ok_for_op (i, code)
HOST_WIDE_INT i;
enum rtx_code code;
{
if (const_ok_for_arm (i))
return 1;
switch (code)
{
case PLUS:
return const_ok_for_arm (ARM_SIGN_EXTEND (-i));
case MINUS:
case XOR:
case IOR:
return 0;
case AND:
return const_ok_for_arm (ARM_SIGN_EXTEND (~i));
default:
abort ();
}
}
int
arm_split_constant (code, mode, val, target, source, subtargets)
enum rtx_code code;
enum machine_mode mode;
HOST_WIDE_INT val;
rtx target;
rtx source;
int subtargets;
{
if (subtargets || code == SET
|| (GET_CODE (target) == REG && GET_CODE (source) == REG
&& REGNO (target) != REGNO (source)))
{
if (!after_arm_reorg
&& (arm_gen_constant (code, mode, val, target, source, 1, 0)
> arm_constant_limit + (code != SET)))
{
if (code == SET)
{
emit_insn (gen_rtx_SET (VOIDmode, target, GEN_INT (val)));
return 1;
}
else
{
rtx temp = subtargets ? gen_reg_rtx (mode) : target;
emit_insn (gen_rtx_SET (VOIDmode, temp, GEN_INT (val)));
if (code == MINUS)
emit_insn (gen_rtx_SET (VOIDmode, target,
gen_rtx_MINUS (mode, temp, source)));
else
emit_insn (gen_rtx_SET (VOIDmode, target,
gen_rtx (code, mode, source, temp)));
return 2;
}
}
}
return arm_gen_constant (code, mode, val, target, source, subtargets, 1);
}
static int
count_insns_for_constant (remainder, i)
HOST_WIDE_INT remainder;
int i;
{
HOST_WIDE_INT temp1;
int num_insns = 0;
do
{
int end;
if (i <= 0)
i += 32;
if (remainder & (3 << (i - 2)))
{
end = i - 8;
if (end < 0)
end += 32;
temp1 = remainder & ((0x0ff << end)
| ((i < end) ? (0xff >> (32 - end)) : 0));
remainder &= ~temp1;
num_insns++;
i -= 6;
}
i -= 2;
} while (remainder);
return num_insns;
}
static int
arm_gen_constant (code, mode, val, target, source, subtargets, generate)
enum rtx_code code;
enum machine_mode mode;
HOST_WIDE_INT val;
rtx target;
rtx source;
int subtargets;
int generate;
{
int can_invert = 0;
int can_negate = 0;
int can_negate_initial = 0;
int can_shift = 0;
int i;
int num_bits_set = 0;
int set_sign_bit_copies = 0;
int clear_sign_bit_copies = 0;
int clear_zero_bit_copies = 0;
int set_zero_bit_copies = 0;
int insns = 0;
unsigned HOST_WIDE_INT temp1, temp2;
unsigned HOST_WIDE_INT remainder = val & 0xffffffff;
switch (code)
{
case SET:
can_invert = 1;
can_shift = 1;
can_negate = 1;
break;
case PLUS:
can_negate = 1;
can_negate_initial = 1;
break;
case IOR:
if (remainder == 0xffffffff)
{
if (generate)
emit_insn (gen_rtx_SET (VOIDmode, target,
GEN_INT (ARM_SIGN_EXTEND (val))));
return 1;
}
if (remainder == 0)
{
if (reload_completed && rtx_equal_p (target, source))
return 0;
if (generate)
emit_insn (gen_rtx_SET (VOIDmode, target, source));
return 1;
}
break;
case AND:
if (remainder == 0)
{
if (generate)
emit_insn (gen_rtx_SET (VOIDmode, target, const0_rtx));
return 1;
}
if (remainder == 0xffffffff)
{
if (reload_completed && rtx_equal_p (target, source))
return 0;
if (generate)
emit_insn (gen_rtx_SET (VOIDmode, target, source));
return 1;
}
can_invert = 1;
break;
case XOR:
if (remainder == 0)
{
if (reload_completed && rtx_equal_p (target, source))
return 0;
if (generate)
emit_insn (gen_rtx_SET (VOIDmode, target, source));
return 1;
}
if (remainder == 0xffffffff)
{
if (generate)
emit_insn (gen_rtx_SET (VOIDmode, target,
gen_rtx_NOT (mode, source)));
return 1;
}
abort ();
case MINUS:
if (remainder == 0)
{
if (generate)
emit_insn (gen_rtx_SET (VOIDmode, target,
gen_rtx_NEG (mode, source)));
return 1;
}
if (const_ok_for_arm (val))
{
if (generate)
emit_insn (gen_rtx_SET (VOIDmode, target,
gen_rtx_MINUS (mode, GEN_INT (val),
source)));
return 1;
}
can_negate = 1;
break;
default:
abort ();
}
if (const_ok_for_arm (val)
|| (can_negate_initial && const_ok_for_arm (-val))
|| (can_invert && const_ok_for_arm (~val)))
{
if (generate)
emit_insn (gen_rtx_SET (VOIDmode, target,
(source ? gen_rtx (code, mode, source,
GEN_INT (val))
: GEN_INT (val))));
return 1;
}
for (i = 31; i >= 0; i--)
{
if ((remainder & (1 << i)) == 0)
clear_sign_bit_copies++;
else
break;
}
for (i = 31; i >= 0; i--)
{
if ((remainder & (1 << i)) != 0)
set_sign_bit_copies++;
else
break;
}
for (i = 0; i <= 31; i++)
{
if ((remainder & (1 << i)) == 0)
clear_zero_bit_copies++;
else
break;
}
for (i = 0; i <= 31; i++)
{
if ((remainder & (1 << i)) != 0)
set_zero_bit_copies++;
else
break;
}
switch (code)
{
case SET:
if (set_sign_bit_copies > 1)
{
if (const_ok_for_arm
(temp1 = ARM_SIGN_EXTEND (remainder
<< (set_sign_bit_copies - 1))))
{
if (generate)
{
rtx new_src = subtargets ? gen_reg_rtx (mode) : target;
emit_insn (gen_rtx_SET (VOIDmode, new_src,
GEN_INT (temp1)));
emit_insn (gen_ashrsi3 (target, new_src,
GEN_INT (set_sign_bit_copies - 1)));
}
return 2;
}
temp1 |= (1 << (set_sign_bit_copies - 1)) - 1;
if (const_ok_for_arm (~temp1))
{
if (generate)
{
rtx new_src = subtargets ? gen_reg_rtx (mode) : target;
emit_insn (gen_rtx_SET (VOIDmode, new_src,
GEN_INT (temp1)));
emit_insn (gen_ashrsi3 (target, new_src,
GEN_INT (set_sign_bit_copies - 1)));
}
return 2;
}
}
if (val & 0xffff0000)
{
temp1 = remainder & 0xffff0000;
temp2 = remainder & 0x0000ffff;
for (i = 9; i < 24; i++)
{
if ((((temp2 | (temp2 << i)) & 0xffffffff) == remainder)
&& !const_ok_for_arm (temp2))
{
rtx new_src = (subtargets
? (generate ? gen_reg_rtx (mode) : NULL_RTX)
: target);
insns = arm_gen_constant (code, mode, temp2, new_src,
source, subtargets, generate);
source = new_src;
if (generate)
emit_insn (gen_rtx_SET
(VOIDmode, target,
gen_rtx_IOR (mode,
gen_rtx_ASHIFT (mode, source,
GEN_INT (i)),
source)));
return insns + 1;
}
}
for (i = 17; i < 24; i++)
{
if (((temp1 | (temp1 >> i)) == remainder)
&& !const_ok_for_arm (temp1))
{
rtx new_src = (subtargets
? (generate ? gen_reg_rtx (mode) : NULL_RTX)
: target);
insns = arm_gen_constant (code, mode, temp1, new_src,
source, subtargets, generate);
source = new_src;
if (generate)
emit_insn
(gen_rtx_SET (VOIDmode, target,
gen_rtx_IOR
(mode,
gen_rtx_LSHIFTRT (mode, source,
GEN_INT (i)),
source)));
return insns + 1;
}
}
}
break;
case IOR:
case XOR:
if (subtargets
|| (reload_completed && !reg_mentioned_p (target, source)))
{
if (const_ok_for_arm (ARM_SIGN_EXTEND (~val)))
{
if (generate)
{
rtx sub = subtargets ? gen_reg_rtx (mode) : target;
emit_insn (gen_rtx_SET (VOIDmode, sub, GEN_INT (val)));
emit_insn (gen_rtx_SET (VOIDmode, target,
gen_rtx (code, mode, source, sub)));
}
return 2;
}
}
if (code == XOR)
break;
if (set_sign_bit_copies > 8
&& (val & (-1 << (32 - set_sign_bit_copies))) == val)
{
if (generate)
{
rtx sub = subtargets ? gen_reg_rtx (mode) : target;
rtx shift = GEN_INT (set_sign_bit_copies);
emit_insn (gen_rtx_SET (VOIDmode, sub,
gen_rtx_NOT (mode,
gen_rtx_ASHIFT (mode,
source,
shift))));
emit_insn (gen_rtx_SET (VOIDmode, target,
gen_rtx_NOT (mode,
gen_rtx_LSHIFTRT (mode, sub,
shift))));
}
return 2;
}
if (set_zero_bit_copies > 8
&& (remainder & ((1 << set_zero_bit_copies) - 1)) == remainder)
{
if (generate)
{
rtx sub = subtargets ? gen_reg_rtx (mode) : target;
rtx shift = GEN_INT (set_zero_bit_copies);
emit_insn (gen_rtx_SET (VOIDmode, sub,
gen_rtx_NOT (mode,
gen_rtx_LSHIFTRT (mode,
source,
shift))));
emit_insn (gen_rtx_SET (VOIDmode, target,
gen_rtx_NOT (mode,
gen_rtx_ASHIFT (mode, sub,
shift))));
}
return 2;
}
if (const_ok_for_arm (temp1 = ARM_SIGN_EXTEND (~val)))
{
if (generate)
{
rtx sub = subtargets ? gen_reg_rtx (mode) : target;
emit_insn (gen_rtx_SET (VOIDmode, sub,
gen_rtx_NOT (mode, source)));
source = sub;
if (subtargets)
sub = gen_reg_rtx (mode);
emit_insn (gen_rtx_SET (VOIDmode, sub,
gen_rtx_AND (mode, source,
GEN_INT (temp1))));
emit_insn (gen_rtx_SET (VOIDmode, target,
gen_rtx_NOT (mode, sub)));
}
return 3;
}
break;
case AND:
if (clear_sign_bit_copies >= 16 && clear_sign_bit_copies < 24)
{
HOST_WIDE_INT shift_mask = ((0xffffffff
<< (32 - clear_sign_bit_copies))
& 0xffffffff);
if ((remainder | shift_mask) != 0xffffffff)
{
if (generate)
{
rtx new_src = subtargets ? gen_reg_rtx (mode) : target;
insns = arm_gen_constant (AND, mode, remainder | shift_mask,
new_src, source, subtargets, 1);
source = new_src;
}
else
{
rtx targ = subtargets ? NULL_RTX : target;
insns = arm_gen_constant (AND, mode, remainder | shift_mask,
targ, source, subtargets, 0);
}
}
if (generate)
{
rtx new_src = subtargets ? gen_reg_rtx (mode) : target;
rtx shift = GEN_INT (clear_sign_bit_copies);
emit_insn (gen_ashlsi3 (new_src, source, shift));
emit_insn (gen_lshrsi3 (target, new_src, shift));
}
return insns + 2;
}
if (clear_zero_bit_copies >= 16 && clear_zero_bit_copies < 24)
{
HOST_WIDE_INT shift_mask = (1 << clear_zero_bit_copies) - 1;
if ((remainder | shift_mask) != 0xffffffff)
{
if (generate)
{
rtx new_src = subtargets ? gen_reg_rtx (mode) : target;
insns = arm_gen_constant (AND, mode, remainder | shift_mask,
new_src, source, subtargets, 1);
source = new_src;
}
else
{
rtx targ = subtargets ? NULL_RTX : target;
insns = arm_gen_constant (AND, mode, remainder | shift_mask,
targ, source, subtargets, 0);
}
}
if (generate)
{
rtx new_src = subtargets ? gen_reg_rtx (mode) : target;
rtx shift = GEN_INT (clear_zero_bit_copies);
emit_insn (gen_lshrsi3 (new_src, source, shift));
emit_insn (gen_ashlsi3 (target, new_src, shift));
}
return insns + 2;
}
break;
default:
break;
}
for (i = 0; i < 32; i++)
if (remainder & (1 << i))
num_bits_set++;
if (code == AND || (can_invert && num_bits_set > 16))
remainder = (~remainder) & 0xffffffff;
else if (code == PLUS && num_bits_set > 16)
remainder = (-remainder) & 0xffffffff;
else
{
can_invert = 0;
can_negate = 0;
}
{
int best_start = 0;
int best_consecutive_zeros = 0;
for (i = 0; i < 32; i += 2)
{
int consecutive_zeros = 0;
if (!(remainder & (3 << i)))
{
while ((i < 32) && !(remainder & (3 << i)))
{
consecutive_zeros += 2;
i += 2;
}
if (consecutive_zeros > best_consecutive_zeros)
{
best_consecutive_zeros = consecutive_zeros;
best_start = i - consecutive_zeros;
}
i -= 2;
}
}
if (best_start != 0
&& ((((unsigned HOST_WIDE_INT) 1) << best_start) < remainder)
&& (count_insns_for_constant (remainder, 0) <=
count_insns_for_constant (remainder, best_start)))
best_start = 0;
i = best_start;
do
{
int end;
if (i <= 0)
i += 32;
if (remainder & (3 << (i - 2)))
{
end = i - 8;
if (end < 0)
end += 32;
temp1 = remainder & ((0x0ff << end)
| ((i < end) ? (0xff >> (32 - end)) : 0));
remainder &= ~temp1;
if (generate)
{
rtx new_src, temp1_rtx;
if (code == SET || code == MINUS)
{
new_src = (subtargets ? gen_reg_rtx (mode) : target);
if (can_invert && code != MINUS)
temp1 = ~temp1;
}
else
{
if (remainder && subtargets)
new_src = gen_reg_rtx (mode);
else
new_src = target;
if (can_invert)
temp1 = ~temp1;
else if (can_negate)
temp1 = -temp1;
}
temp1 = trunc_int_for_mode (temp1, mode);
temp1_rtx = GEN_INT (temp1);
if (code == SET)
;
else if (code == MINUS)
temp1_rtx = gen_rtx_MINUS (mode, temp1_rtx, source);
else
temp1_rtx = gen_rtx_fmt_ee (code, mode, source, temp1_rtx);
emit_insn (gen_rtx_SET (VOIDmode, new_src, temp1_rtx));
source = new_src;
}
if (code == SET)
{
can_invert = 0;
code = PLUS;
}
else if (code == MINUS)
code = PLUS;
insns++;
i -= 6;
}
i -= 2;
}
while (remainder);
}
return insns;
}
enum rtx_code
arm_canonicalize_comparison (code, op1)
enum rtx_code code;
rtx * op1;
{
unsigned HOST_WIDE_INT i = INTVAL (*op1);
switch (code)
{
case EQ:
case NE:
return code;
case GT:
case LE:
if (i != ((((unsigned HOST_WIDE_INT) 1) << (HOST_BITS_PER_WIDE_INT - 1)) - 1)
&& (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1))))
{
*op1 = GEN_INT (i + 1);
return code == GT ? GE : LT;
}
break;
case GE:
case LT:
if (i != (((unsigned HOST_WIDE_INT) 1) << (HOST_BITS_PER_WIDE_INT - 1))
&& (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1))))
{
*op1 = GEN_INT (i - 1);
return code == GE ? GT : LE;
}
break;
case GTU:
case LEU:
if (i != ~((unsigned HOST_WIDE_INT) 0)
&& (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1))))
{
*op1 = GEN_INT (i + 1);
return code == GTU ? GEU : LTU;
}
break;
case GEU:
case LTU:
if (i != 0
&& (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1))))
{
*op1 = GEN_INT (i - 1);
return code == GEU ? GTU : LEU;
}
break;
default:
abort ();
}
return code;
}
int
arm_return_in_memory (type)
tree type;
{
HOST_WIDE_INT size;
if (!AGGREGATE_TYPE_P (type))
return 0;
size = int_size_in_bytes (type);
if (TARGET_ATPCS)
{
return (size < 0 || size > UNITS_PER_WORD);
}
#ifndef ARM_WINCE
if (size < 0 || size > UNITS_PER_WORD)
return 1;
if (TREE_CODE (type) == RECORD_TYPE)
{
tree field;
for (field = TYPE_FIELDS (type);
field && TREE_CODE (field) != FIELD_DECL;
field = TREE_CHAIN (field))
continue;
if (field == NULL)
return 0;
if (FLOAT_TYPE_P (TREE_TYPE (field)))
return 1;
if (RETURN_IN_MEMORY (TREE_TYPE (field)))
return 1;
for (field = TREE_CHAIN (field);
field;
field = TREE_CHAIN (field))
{
if (TREE_CODE (field) != FIELD_DECL)
continue;
if (!DECL_BIT_FIELD_TYPE (field))
return 1;
}
return 0;
}
if (TREE_CODE (type) == UNION_TYPE)
{
tree field;
for (field = TYPE_FIELDS (type);
field;
field = TREE_CHAIN (field))
{
if (TREE_CODE (field) != FIELD_DECL)
continue;
if (FLOAT_TYPE_P (TREE_TYPE (field)))
return 1;
if (RETURN_IN_MEMORY (TREE_TYPE (field)))
return 1;
}
return 0;
}
#endif
return 1;
}
int
arm_float_words_big_endian ()
{
if (TARGET_HARD_FLOAT)
{
return 1;
}
if (TARGET_VFP)
return (TARGET_BIG_END ? 1 : 0);
return 1;
}
void
arm_init_cumulative_args (pcum, fntype, libname, indirect)
CUMULATIVE_ARGS * pcum;
tree fntype;
rtx libname ATTRIBUTE_UNUSED;
int indirect ATTRIBUTE_UNUSED;
{
pcum->nregs = ((fntype && aggregate_value_p (TREE_TYPE (fntype))) ? 1 : 0);
pcum->call_cookie = CALL_NORMAL;
if (TARGET_LONG_CALLS)
pcum->call_cookie = CALL_LONG;
if (fntype)
{
if (lookup_attribute ("short_call", TYPE_ATTRIBUTES (fntype)))
pcum->call_cookie = CALL_SHORT;
else if (lookup_attribute ("long_call", TYPE_ATTRIBUTES (fntype)))
pcum->call_cookie = CALL_LONG;
}
}
rtx
arm_function_arg (pcum, mode, type, named)
CUMULATIVE_ARGS * pcum;
enum machine_mode mode;
tree type ATTRIBUTE_UNUSED;
int named;
{
if (mode == VOIDmode)
return GEN_INT (pcum->call_cookie);
if (!named || pcum->nregs >= NUM_ARG_REGS)
return NULL_RTX;
return gen_rtx_REG (mode, pcum->nregs);
}
int
arm_function_arg_pass_by_reference (cum, mode, type, named)
CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED;
enum machine_mode mode ATTRIBUTE_UNUSED;
tree type;
int named ATTRIBUTE_UNUSED;
{
return type && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST;
}
rtx
arm_va_arg (valist, type)
tree valist, type;
{
if (TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
{
rtx addr = std_expand_builtin_va_arg (valist, build_pointer_type (type));
return gen_rtx_MEM (ptr_mode, force_reg (Pmode, addr));
}
return std_expand_builtin_va_arg (valist, type);
}
typedef enum
{
OFF,
LONG,
SHORT
} arm_pragma_enum;
static arm_pragma_enum arm_pragma_long_calls = OFF;
void
arm_pr_long_calls (pfile)
cpp_reader * pfile ATTRIBUTE_UNUSED;
{
arm_pragma_long_calls = LONG;
}
void
arm_pr_no_long_calls (pfile)
cpp_reader * pfile ATTRIBUTE_UNUSED;
{
arm_pragma_long_calls = SHORT;
}
void
arm_pr_long_calls_off (pfile)
cpp_reader * pfile ATTRIBUTE_UNUSED;
{
arm_pragma_long_calls = OFF;
}
const struct attribute_spec arm_attribute_table[] =
{
{ "long_call", 0, 0, false, true, true, NULL },
{ "short_call", 0, 0, false, true, true, NULL },
{ "isr", 0, 1, false, false, false, arm_handle_isr_attribute },
{ "interrupt", 0, 1, false, false, false, arm_handle_isr_attribute },
{ "naked", 0, 0, true, false, false, arm_handle_fndecl_attribute },
#ifdef ARM_PE
{ "dllimport", 0, 0, true, false, false, NULL },
{ "dllexport", 0, 0, true, false, false, NULL },
{ "interfacearm", 0, 0, true, false, false, arm_handle_fndecl_attribute },
#endif
{ NULL, 0, 0, false, false, false, NULL }
};
static tree
arm_handle_fndecl_attribute (node, name, args, flags, no_add_attrs)
tree * node;
tree name;
tree args ATTRIBUTE_UNUSED;
int flags ATTRIBUTE_UNUSED;
bool * no_add_attrs;
{
if (TREE_CODE (*node) != FUNCTION_DECL)
{
warning ("`%s' attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
return NULL_TREE;
}
static tree
arm_handle_isr_attribute (node, name, args, flags, no_add_attrs)
tree * node;
tree name;
tree args;
int flags;
bool * no_add_attrs;
{
if (DECL_P (*node))
{
if (TREE_CODE (*node) != FUNCTION_DECL)
{
warning ("`%s' attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
}
else
{
if (TREE_CODE (*node) == FUNCTION_TYPE
|| TREE_CODE (*node) == METHOD_TYPE)
{
if (arm_isr_value (args) == ARM_FT_UNKNOWN)
{
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
}
else if (TREE_CODE (*node) == POINTER_TYPE
&& (TREE_CODE (TREE_TYPE (*node)) == FUNCTION_TYPE
|| TREE_CODE (TREE_TYPE (*node)) == METHOD_TYPE)
&& arm_isr_value (args) != ARM_FT_UNKNOWN)
{
*node = build_type_copy (*node);
TREE_TYPE (*node) = build_type_attribute_variant
(TREE_TYPE (*node),
tree_cons (name, args, TYPE_ATTRIBUTES (TREE_TYPE (*node))));
*no_add_attrs = true;
}
else
{
if (flags & ((int) ATTR_FLAG_DECL_NEXT
| (int) ATTR_FLAG_FUNCTION_NEXT
| (int) ATTR_FLAG_ARRAY_NEXT))
{
*no_add_attrs = true;
return tree_cons (name, args, NULL_TREE);
}
else
{
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
}
}
}
return NULL_TREE;
}
static int
arm_comp_type_attributes (type1, type2)
tree type1;
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;
}
void
arm_encode_call_attribute (decl, flag)
tree decl;
int flag;
{
const char * str = XSTR (XEXP (DECL_RTL (decl), 0), 0);
int len = strlen (str);
char * newstr;
if (DECL_WEAK (decl) && flag == SHORT_CALL_FLAG_CHAR)
return;
newstr = alloca (len + 2);
newstr[0] = flag;
strcpy (newstr + 1, str);
newstr = (char *) ggc_alloc_string (newstr, len + 1);
XSTR (XEXP (DECL_RTL (decl), 0), 0) = newstr;
}
static void
arm_set_default_type_attributes (type)
tree type;
{
if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE)
{
tree type_attr_list, attr_name;
type_attr_list = TYPE_ATTRIBUTES (type);
if (arm_pragma_long_calls == LONG)
attr_name = get_identifier ("long_call");
else if (arm_pragma_long_calls == SHORT)
attr_name = get_identifier ("short_call");
else
return;
type_attr_list = tree_cons (attr_name, NULL_TREE, type_attr_list);
TYPE_ATTRIBUTES (type) = type_attr_list;
}
}
static int
current_file_function_operand (sym_ref)
rtx sym_ref;
{
if (ENCODED_SHORT_CALL_ATTR_P (XSTR (sym_ref, 0)))
return 1;
if (sym_ref == XEXP (DECL_RTL (current_function_decl), 0)
&& !DECL_WEAK (current_function_decl))
return 1;
return 0;
}
int
arm_is_longcall_p (sym_ref, call_cookie, call_symbol)
rtx sym_ref;
int call_cookie;
int call_symbol;
{
if (!call_symbol)
{
if (GET_CODE (sym_ref) != MEM)
return 0;
sym_ref = XEXP (sym_ref, 0);
}
if (GET_CODE (sym_ref) != SYMBOL_REF)
return 0;
if (call_cookie & CALL_SHORT)
return 0;
if (TARGET_LONG_CALLS && flag_function_sections)
return 1;
if (current_file_function_operand (sym_ref))
return 0;
return (call_cookie & CALL_LONG)
|| ENCODED_LONG_CALL_ATTR_P (XSTR (sym_ref, 0))
|| TARGET_LONG_CALLS;
}
int
arm_function_ok_for_sibcall (decl)
tree decl;
{
int call_type = TARGET_LONG_CALLS ? CALL_LONG : CALL_NORMAL;
if (decl == NULL || TARGET_THUMB)
return 0;
if (lookup_attribute ("short_call", TYPE_ATTRIBUTES (TREE_TYPE (decl))))
call_type = CALL_SHORT;
else if (lookup_attribute ("long_call", TYPE_ATTRIBUTES (TREE_TYPE (decl))))
call_type = CALL_LONG;
if (call_type == CALL_LONG && (flag_pic || !TREE_ASM_WRITTEN (decl)))
return 0;
if (TARGET_INTERWORK && TREE_PUBLIC (decl) && !TREE_ASM_WRITTEN (decl))
return 0;
if (IS_INTERRUPT (arm_current_func_type ()))
return 0;
return 1;
}
int
legitimate_pic_operand_p (x)
rtx x;
{
if (CONSTANT_P (x)
&& flag_pic
&& (GET_CODE (x) == SYMBOL_REF
|| (GET_CODE (x) == CONST
&& GET_CODE (XEXP (x, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF)))
return 0;
return 1;
}
rtx
legitimize_pic_address (orig, mode, reg)
rtx orig;
enum machine_mode mode;
rtx reg;
{
if (GET_CODE (orig) == SYMBOL_REF
|| GET_CODE (orig) == LABEL_REF)
{
#ifndef AOF_ASSEMBLER
rtx pic_ref, address;
#endif
rtx insn;
int subregs = 0;
if (reg == 0)
{
if (no_new_pseudos)
abort ();
else
reg = gen_reg_rtx (Pmode);
subregs = 1;
}
#ifdef AOF_ASSEMBLER
insn = emit_insn (gen_pic_load_addr_based (reg, orig));
#else
if (subregs)
address = gen_reg_rtx (Pmode);
else
address = reg;
if (TARGET_ARM)
emit_insn (gen_pic_load_addr_arm (address, orig));
else
emit_insn (gen_pic_load_addr_thumb (address, orig));
if ((GET_CODE (orig) == LABEL_REF
|| (GET_CODE (orig) == SYMBOL_REF &&
ENCODED_SHORT_CALL_ATTR_P (XSTR (orig, 0))))
&& NEED_GOT_RELOC)
pic_ref = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, address);
else
{
pic_ref = gen_rtx_MEM (Pmode,
gen_rtx_PLUS (Pmode, pic_offset_table_rtx,
address));
RTX_UNCHANGING_P (pic_ref) = 1;
}
insn = emit_move_insn (reg, pic_ref);
#endif
current_function_uses_pic_offset_table = 1;
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, orig,
REG_NOTES (insn));
return reg;
}
else if (GET_CODE (orig) == CONST)
{
rtx base, offset;
if (GET_CODE (XEXP (orig, 0)) == PLUS
&& XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx)
return orig;
if (reg == 0)
{
if (no_new_pseudos)
abort ();
else
reg = gen_reg_rtx (Pmode);
}
if (GET_CODE (XEXP (orig, 0)) == PLUS)
{
base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg);
offset = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode,
base == reg ? 0 : reg);
}
else
abort ();
if (GET_CODE (offset) == CONST_INT)
{
ARM_GO_IF_LEGITIMATE_INDEX (mode, 0, offset, win);
if (!no_new_pseudos)
offset = force_reg (Pmode, offset);
else
abort ();
win:
if (GET_CODE (offset) == CONST_INT)
return plus_constant (base, INTVAL (offset));
}
if (GET_MODE_SIZE (mode) > 4
&& (GET_MODE_CLASS (mode) == MODE_INT
|| TARGET_SOFT_FLOAT))
{
emit_insn (gen_addsi3 (reg, base, offset));
return reg;
}
return gen_rtx_PLUS (Pmode, base, offset);
}
return orig;
}
void
arm_finalize_pic (prologue)
int prologue ATTRIBUTE_UNUSED;
{
#ifndef AOF_ASSEMBLER
rtx l1, pic_tmp, pic_tmp2, seq, pic_rtx;
rtx global_offset_table;
if (current_function_uses_pic_offset_table == 0 || TARGET_SINGLE_PIC_BASE)
return;
if (!flag_pic)
abort ();
start_sequence ();
l1 = gen_label_rtx ();
global_offset_table = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
pic_tmp = plus_constant (gen_rtx_LABEL_REF (Pmode, l1), TARGET_ARM ? 8 : 4);
if (GOT_PCREL)
pic_tmp2 = gen_rtx_CONST (VOIDmode,
gen_rtx_PLUS (Pmode, global_offset_table, pc_rtx));
else
pic_tmp2 = gen_rtx_CONST (VOIDmode, global_offset_table);
pic_rtx = gen_rtx_CONST (Pmode, gen_rtx_MINUS (Pmode, pic_tmp2, pic_tmp));
if (TARGET_ARM)
{
emit_insn (gen_pic_load_addr_arm (pic_offset_table_rtx, pic_rtx));
emit_insn (gen_pic_add_dot_plus_eight (pic_offset_table_rtx, l1));
}
else
{
emit_insn (gen_pic_load_addr_thumb (pic_offset_table_rtx, pic_rtx));
emit_insn (gen_pic_add_dot_plus_four (pic_offset_table_rtx, l1));
}
seq = get_insns ();
end_sequence ();
if (prologue)
emit_insn_after (seq, get_insns ());
else
emit_insn (seq);
emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx));
#endif
}
#define REG_OR_SUBREG_REG(X) \
(GET_CODE (X) == REG \
|| (GET_CODE (X) == SUBREG && GET_CODE (SUBREG_REG (X)) == REG))
#define REG_OR_SUBREG_RTX(X) \
(GET_CODE (X) == REG ? (X) : SUBREG_REG (X))
#ifndef COSTS_N_INSNS
#define COSTS_N_INSNS(N) ((N) * 4 - 2)
#endif
int
arm_rtx_costs (x, code, outer)
rtx x;
enum rtx_code code;
enum rtx_code outer;
{
enum machine_mode mode = GET_MODE (x);
enum rtx_code subcode;
int extra_cost;
if (TARGET_THUMB)
{
switch (code)
{
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
case ROTATERT:
case PLUS:
case MINUS:
case COMPARE:
case NEG:
case NOT:
return COSTS_N_INSNS (1);
case MULT:
if (GET_CODE (XEXP (x, 1)) == CONST_INT)
{
int cycles = 0;
unsigned HOST_WIDE_INT i = INTVAL (XEXP (x, 1));
while (i)
{
i >>= 2;
cycles++;
}
return COSTS_N_INSNS (2) + cycles;
}
return COSTS_N_INSNS (1) + 16;
case SET:
return (COSTS_N_INSNS (1)
+ 4 * ((GET_CODE (SET_SRC (x)) == MEM)
+ GET_CODE (SET_DEST (x)) == MEM));
case CONST_INT:
if (outer == SET)
{
if ((unsigned HOST_WIDE_INT) INTVAL (x) < 256)
return 0;
if (thumb_shiftable_const (INTVAL (x)))
return COSTS_N_INSNS (2);
return COSTS_N_INSNS (3);
}
else if (outer == PLUS
&& INTVAL (x) < 256 && INTVAL (x) > -256)
return 0;
else if (outer == COMPARE
&& (unsigned HOST_WIDE_INT) INTVAL (x) < 256)
return 0;
else if (outer == ASHIFT || outer == ASHIFTRT
|| outer == LSHIFTRT)
return 0;
return COSTS_N_INSNS (2);
case CONST:
case CONST_DOUBLE:
case LABEL_REF:
case SYMBOL_REF:
return COSTS_N_INSNS (3);
case UDIV:
case UMOD:
case DIV:
case MOD:
return 100;
case TRUNCATE:
return 99;
case AND:
case XOR:
case IOR:
return 8;
case ADDRESSOF:
case MEM:
return (10 + 4 * ((GET_MODE_SIZE (mode) - 1) / UNITS_PER_WORD)
+ ((GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x))
? 4 : 0));
case IF_THEN_ELSE:
if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC)
return 14;
return 2;
case ZERO_EXTEND:
switch (GET_MODE (XEXP (x, 0)))
{
case QImode:
return (1 + (mode == DImode ? 4 : 0)
+ (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
case HImode:
return (4 + (mode == DImode ? 4 : 0)
+ (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
case SImode:
return (1 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
default:
return 99;
}
default:
return 99;
#if 0
case FFS:
case FLOAT:
case FIX:
case UNSIGNED_FIX:
fprintf (stderr, "unexpected code for thumb in rtx_costs: %s\n",
rtx_name[code]);
abort ();
#endif
}
}
switch (code)
{
case MEM:
return (10 + 4 * ((GET_MODE_SIZE (mode) - 1) / UNITS_PER_WORD)
+ (GET_CODE (x) == SYMBOL_REF
&& CONSTANT_POOL_ADDRESS_P (x) ? 4 : 0));
case DIV:
case MOD:
return 100;
case ROTATE:
if (mode == SImode && GET_CODE (XEXP (x, 1)) == REG)
return 4;
case ROTATERT:
if (mode != SImode)
return 8;
case ASHIFT: case LSHIFTRT: case ASHIFTRT:
if (mode == DImode)
return (8 + (GET_CODE (XEXP (x, 1)) == CONST_INT ? 0 : 8)
+ ((GET_CODE (XEXP (x, 0)) == REG
|| (GET_CODE (XEXP (x, 0)) == SUBREG
&& GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG))
? 0 : 8));
return (1 + ((GET_CODE (XEXP (x, 0)) == REG
|| (GET_CODE (XEXP (x, 0)) == SUBREG
&& GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG))
? 0 : 4)
+ ((GET_CODE (XEXP (x, 1)) == REG
|| (GET_CODE (XEXP (x, 1)) == SUBREG
&& GET_CODE (SUBREG_REG (XEXP (x, 1))) == REG)
|| (GET_CODE (XEXP (x, 1)) == CONST_INT))
? 0 : 4));
case MINUS:
if (mode == DImode)
return (4 + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 8)
+ ((REG_OR_SUBREG_REG (XEXP (x, 0))
|| (GET_CODE (XEXP (x, 0)) == CONST_INT
&& const_ok_for_arm (INTVAL (XEXP (x, 0)))))
? 0 : 8));
if (GET_MODE_CLASS (mode) == MODE_FLOAT)
return (2 + ((REG_OR_SUBREG_REG (XEXP (x, 1))
|| (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE
&& const_double_rtx_ok_for_fpu (XEXP (x, 1))))
? 0 : 8)
+ ((REG_OR_SUBREG_REG (XEXP (x, 0))
|| (GET_CODE (XEXP (x, 0)) == CONST_DOUBLE
&& const_double_rtx_ok_for_fpu (XEXP (x, 0))))
? 0 : 8));
if (((GET_CODE (XEXP (x, 0)) == CONST_INT
&& const_ok_for_arm (INTVAL (XEXP (x, 0)))
&& REG_OR_SUBREG_REG (XEXP (x, 1))))
|| (((subcode = GET_CODE (XEXP (x, 1))) == ASHIFT
|| subcode == ASHIFTRT || subcode == LSHIFTRT
|| subcode == ROTATE || subcode == ROTATERT
|| (subcode == MULT
&& GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT
&& ((INTVAL (XEXP (XEXP (x, 1), 1)) &
(INTVAL (XEXP (XEXP (x, 1), 1)) - 1)) == 0)))
&& REG_OR_SUBREG_REG (XEXP (XEXP (x, 1), 0))
&& (REG_OR_SUBREG_REG (XEXP (XEXP (x, 1), 1))
|| GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT)
&& REG_OR_SUBREG_REG (XEXP (x, 0))))
return 1;
case PLUS:
if (GET_MODE_CLASS (mode) == MODE_FLOAT)
return (2 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 8)
+ ((REG_OR_SUBREG_REG (XEXP (x, 1))
|| (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE
&& const_double_rtx_ok_for_fpu (XEXP (x, 1))))
? 0 : 8));
case AND: case XOR: case IOR:
extra_cost = 0;
if ((REG_OR_SUBREG_REG (XEXP (x, 0))
&& ARM_FRAME_RTX (REG_OR_SUBREG_RTX (XEXP (x, 0)))
&& GET_CODE (XEXP (x, 1)) != CONST_INT)
|| (REG_OR_SUBREG_REG (XEXP (x, 0))
&& ARM_FRAME_RTX (REG_OR_SUBREG_RTX (XEXP (x, 0)))))
extra_cost = 4;
if (mode == DImode)
return (4 + extra_cost + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 8)
+ ((REG_OR_SUBREG_REG (XEXP (x, 1))
|| (GET_CODE (XEXP (x, 1)) == CONST_INT
&& const_ok_for_op (INTVAL (XEXP (x, 1)), code)))
? 0 : 8));
if (REG_OR_SUBREG_REG (XEXP (x, 0)))
return (1 + (GET_CODE (XEXP (x, 1)) == CONST_INT ? 0 : extra_cost)
+ ((REG_OR_SUBREG_REG (XEXP (x, 1))
|| (GET_CODE (XEXP (x, 1)) == CONST_INT
&& const_ok_for_op (INTVAL (XEXP (x, 1)), code)))
? 0 : 4));
else if (REG_OR_SUBREG_REG (XEXP (x, 1)))
return (1 + extra_cost
+ ((((subcode = GET_CODE (XEXP (x, 0))) == ASHIFT
|| subcode == LSHIFTRT || subcode == ASHIFTRT
|| subcode == ROTATE || subcode == ROTATERT
|| (subcode == MULT
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& ((INTVAL (XEXP (XEXP (x, 0), 1)) &
(INTVAL (XEXP (XEXP (x, 0), 1)) - 1)) == 0)))
&& (REG_OR_SUBREG_REG (XEXP (XEXP (x, 0), 0)))
&& ((REG_OR_SUBREG_REG (XEXP (XEXP (x, 0), 1)))
|| GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT))
? 0 : 4));
return 8;
case MULT:
if (arm_fast_multiply && 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))
return 8;
if (GET_MODE_CLASS (mode) == MODE_FLOAT
|| mode == DImode)
return 30;
if (GET_CODE (XEXP (x, 1)) == CONST_INT)
{
unsigned HOST_WIDE_INT i = (INTVAL (XEXP (x, 1))
& (unsigned HOST_WIDE_INT) 0xffffffff);
int add_cost = const_ok_for_arm (i) ? 4 : 8;
int j;
int booth_unit_size = ((tune_flags & FL_FAST_MULT) ? 8 : 2);
for (j = 0; i && j < 32; j += booth_unit_size)
{
i >>= booth_unit_size;
add_cost += 2;
}
return add_cost;
}
return (((tune_flags & FL_FAST_MULT) ? 8 : 30)
+ (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4)
+ (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 4));
case TRUNCATE:
if (arm_fast_multiply && mode == SImode
&& GET_CODE (XEXP (x, 0)) == LSHIFTRT
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT
&& (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0))
== GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)))
&& (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == ZERO_EXTEND
|| GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == SIGN_EXTEND))
return 8;
return 99;
case NEG:
if (GET_MODE_CLASS (mode) == MODE_FLOAT)
return 4 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 6);
case NOT:
if (mode == DImode)
return 4 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4);
return 1 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4);
case IF_THEN_ELSE:
if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC)
return 14;
return 2;
case COMPARE:
return 1;
case ABS:
return 4 + (mode == DImode ? 4 : 0);
case SIGN_EXTEND:
if (GET_MODE (XEXP (x, 0)) == QImode)
return (4 + (mode == DImode ? 4 : 0)
+ (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
case ZERO_EXTEND:
switch (GET_MODE (XEXP (x, 0)))
{
case QImode:
return (1 + (mode == DImode ? 4 : 0)
+ (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
case HImode:
return (4 + (mode == DImode ? 4 : 0)
+ (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
case SImode:
return (1 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
default:
break;
}
abort ();
case CONST_INT:
if (const_ok_for_arm (INTVAL (x)))
return outer == SET ? 2 : -1;
else if (outer == AND
&& const_ok_for_arm (~INTVAL (x)))
return -1;
else if ((outer == COMPARE
|| outer == PLUS || outer == MINUS)
&& const_ok_for_arm (-INTVAL (x)))
return -1;
else
return 5;
case CONST:
case LABEL_REF:
case SYMBOL_REF:
return 6;
case CONST_DOUBLE:
if (const_double_rtx_ok_for_fpu (x))
return outer == SET ? 2 : -1;
else if ((outer == COMPARE || outer == PLUS)
&& neg_const_double_rtx_ok_for_fpu (x))
return -1;
return 7;
default:
return 99;
}
}
static int
arm_adjust_cost (insn, link, dep, cost)
rtx insn;
rtx link;
rtx dep;
int cost;
{
rtx i_pat, d_pat;
if (arm_is_xscale
&& REG_NOTE_KIND (link) == 0
&& recog_memoized (insn) < 0
&& recog_memoized (dep) < 0)
{
int shift_opnum = get_attr_shift (insn);
enum attr_type attr_type = get_attr_type (dep);
if (shift_opnum != 0 && attr_type == TYPE_NORMAL)
{
rtx shifted_operand;
int opno;
extract_insn (insn);
shifted_operand = recog_data.operand[shift_opnum];
extract_insn (dep);
preprocess_constraints ();
for (opno = 0; opno < recog_data.n_operands; opno++)
{
if (recog_data.operand_type[opno] == OP_IN)
continue;
if (reg_overlap_mentioned_p (recog_data.operand[opno],
shifted_operand))
return 2;
}
}
}
if (REG_NOTE_KIND (link) == REG_DEP_ANTI
|| REG_NOTE_KIND (link) == REG_DEP_OUTPUT)
return 0;
if (REG_NOTE_KIND (link) == 0
&& GET_CODE (insn) == CALL_INSN)
return 1;
if ((i_pat = single_set (insn)) != NULL
&& GET_CODE (SET_SRC (i_pat)) == MEM
&& (d_pat = single_set (dep)) != NULL
&& GET_CODE (SET_DEST (d_pat)) == MEM)
{
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;
}
static int fpa_consts_inited = 0;
static const char * const strings_fpa[8] =
{
"0", "1", "2", "3",
"4", "5", "0.5", "10"
};
static REAL_VALUE_TYPE values_fpa[8];
static void
init_fpa_table ()
{
int i;
REAL_VALUE_TYPE r;
for (i = 0; i < 8; i++)
{
r = REAL_VALUE_ATOF (strings_fpa[i], DFmode);
values_fpa[i] = r;
}
fpa_consts_inited = 1;
}
int
const_double_rtx_ok_for_fpu (x)
rtx x;
{
REAL_VALUE_TYPE r;
int i;
if (!fpa_consts_inited)
init_fpa_table ();
REAL_VALUE_FROM_CONST_DOUBLE (r, x);
if (REAL_VALUE_MINUS_ZERO (r))
return 0;
for (i = 0; i < 8; i++)
if (REAL_VALUES_EQUAL (r, values_fpa[i]))
return 1;
return 0;
}
int
neg_const_double_rtx_ok_for_fpu (x)
rtx x;
{
REAL_VALUE_TYPE r;
int i;
if (!fpa_consts_inited)
init_fpa_table ();
REAL_VALUE_FROM_CONST_DOUBLE (r, x);
r = REAL_VALUE_NEGATE (r);
if (REAL_VALUE_MINUS_ZERO (r))
return 0;
for (i = 0; i < 8; i++)
if (REAL_VALUES_EQUAL (r, values_fpa[i]))
return 1;
return 0;
}
int
s_register_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
return (GET_CODE (op) == REG
&& (REGNO (op) >= FIRST_PSEUDO_REGISTER
|| REGNO_REG_CLASS (REGNO (op)) != NO_REGS));
}
int
arm_hard_register_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return 0;
return (GET_CODE (op) == REG
&& REGNO (op) < FIRST_PSEUDO_REGISTER);
}
int
reg_or_int_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == CONST_INT)
return 1;
if (GET_MODE (op) != mode && mode != VOIDmode)
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
return (GET_CODE (op) == REG
&& (REGNO (op) >= FIRST_PSEUDO_REGISTER
|| REGNO_REG_CLASS (REGNO (op)) != NO_REGS));
}
int
arm_reload_memory_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
int regno = true_regnum (op);
return (!CONSTANT_P (op)
&& (regno == -1
|| (GET_CODE (op) == REG
&& REGNO (op) >= FIRST_PSEUDO_REGISTER)));
}
int
bad_signed_byte_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
#if 0
if ((mode == QImode && !memory_operand (op, mode)) || GET_CODE (op) != MEM)
return 0;
#endif
if (GET_CODE (op) != MEM)
return 0;
op = XEXP (op, 0);
if ((GET_CODE (op) == PLUS || GET_CODE (op) == MINUS)
&& (!s_register_operand (XEXP (op, 0), VOIDmode)
|| (!s_register_operand (XEXP (op, 1), VOIDmode)
&& GET_CODE (XEXP (op, 1)) != CONST_INT)))
return 1;
if (GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT
&& (INTVAL (XEXP (op, 1)) > 0xff
|| -INTVAL (XEXP (op, 1)) > 0xff))
return 1;
return 0;
}
int
arm_rhs_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (s_register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op))));
}
int
arm_rhsm_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (s_register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op)))
|| memory_operand (op, mode));
}
int
arm_add_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (TARGET_THUMB)
return thumb_cmp_operand (op, mode);
return (s_register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT
&& (const_ok_for_arm (INTVAL (op))
|| const_ok_for_arm (-INTVAL (op)))));
}
int
arm_not_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (s_register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT
&& (const_ok_for_arm (INTVAL (op))
|| const_ok_for_arm (~INTVAL (op)))));
}
int
offsettable_memory_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (mode == VOIDmode)
mode = GET_MODE (op);
return (mode == GET_MODE (op)
&& GET_CODE (op) == MEM
&& offsettable_address_p (reload_completed | reload_in_progress,
mode, XEXP (op, 0)));
}
int
alignable_memory_operand (op, mode)
rtx op;
enum machine_mode mode;
{
rtx reg;
if (mode == VOIDmode)
mode = GET_MODE (op);
if (mode != GET_MODE (op) || GET_CODE (op) != MEM)
return 0;
op = XEXP (op, 0);
return ((GET_CODE (reg = op) == REG
|| (GET_CODE (op) == SUBREG
&& GET_CODE (reg = SUBREG_REG (op)) == REG)
|| (GET_CODE (op) == PLUS
&& GET_CODE (XEXP (op, 1)) == CONST_INT
&& (GET_CODE (reg = XEXP (op, 0)) == REG
|| (GET_CODE (XEXP (op, 0)) == SUBREG
&& GET_CODE (reg = SUBREG_REG (XEXP (op, 0))) == REG))))
&& REGNO_POINTER_ALIGN (REGNO (reg)) >= 32);
}
int
f_register_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
return (GET_CODE (op) == REG
&& (REGNO (op) >= FIRST_PSEUDO_REGISTER
|| REGNO_REG_CLASS (REGNO (op)) == FPU_REGS));
}
int
fpu_rhs_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (s_register_operand (op, mode))
return TRUE;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) == CONST_DOUBLE)
return const_double_rtx_ok_for_fpu (op);
return FALSE;
}
int
fpu_add_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (s_register_operand (op, mode))
return TRUE;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) == CONST_DOUBLE)
return (const_double_rtx_ok_for_fpu (op)
|| neg_const_double_rtx_ok_for_fpu (op));
return FALSE;
}
int
power_of_two_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) == CONST_INT)
{
HOST_WIDE_INT value = INTVAL (op);
return value != 0 && (value & (value - 1)) == 0;
}
return FALSE;
}
int
di_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (s_register_operand (op, mode))
return TRUE;
if (mode != VOIDmode && GET_MODE (op) != VOIDmode && GET_MODE (op) != DImode)
return FALSE;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
switch (GET_CODE (op))
{
case CONST_DOUBLE:
case CONST_INT:
return TRUE;
case MEM:
return memory_address_p (DImode, XEXP (op, 0));
default:
return FALSE;
}
}
int
nonimmediate_di_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (s_register_operand (op, mode))
return TRUE;
if (mode != VOIDmode && GET_MODE (op) != VOIDmode && GET_MODE (op) != DImode)
return FALSE;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
if (GET_CODE (op) == MEM)
return memory_address_p (DImode, XEXP (op, 0));
return FALSE;
}
int
soft_df_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (s_register_operand (op, mode))
return TRUE;
if (mode != VOIDmode && GET_MODE (op) != mode)
return FALSE;
if (GET_CODE (op) == SUBREG && CONSTANT_P (SUBREG_REG (op)))
return FALSE;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
switch (GET_CODE (op))
{
case CONST_DOUBLE:
return TRUE;
case MEM:
return memory_address_p (DFmode, XEXP (op, 0));
default:
return FALSE;
}
}
int
nonimmediate_soft_df_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (s_register_operand (op, mode))
return TRUE;
if (mode != VOIDmode && GET_MODE (op) != mode)
return FALSE;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
if (GET_CODE (op) == MEM)
return memory_address_p (DFmode, XEXP (op, 0));
return FALSE;
}
int
index_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (s_register_operand (op, mode)
|| (immediate_operand (op, mode)
&& (GET_CODE (op) != CONST_INT
|| (INTVAL (op) < 4096 && INTVAL (op) > -4096))));
}
int
const_shift_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (power_of_two_operand (op, mode)
|| (immediate_operand (op, mode)
&& (GET_CODE (op) != CONST_INT
|| (INTVAL (op) < 32 && INTVAL (op) > 0))));
}
int
shiftable_operator (x, mode)
rtx x;
enum machine_mode mode;
{
enum rtx_code code;
if (GET_MODE (x) != mode)
return FALSE;
code = GET_CODE (x);
return (code == PLUS || code == MINUS
|| code == IOR || code == XOR || code == AND);
}
int
logical_binary_operator (x, mode)
rtx x;
enum machine_mode mode;
{
enum rtx_code code;
if (GET_MODE (x) != mode)
return FALSE;
code = GET_CODE (x);
return (code == IOR || code == XOR || code == AND);
}
int
shift_operator (x, mode)
rtx x;
enum machine_mode mode;
{
enum rtx_code code;
if (GET_MODE (x) != mode)
return FALSE;
code = GET_CODE (x);
if (code == MULT)
return power_of_two_operand (XEXP (x, 1), mode);
return (code == ASHIFT || code == ASHIFTRT || code == LSHIFTRT
|| code == ROTATERT);
}
int
equality_operator (x, mode)
rtx x;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return GET_CODE (x) == EQ || GET_CODE (x) == NE;
}
int
arm_comparison_operator (x, mode)
rtx x;
enum machine_mode mode;
{
return (comparison_operator (x, mode)
&& GET_CODE (x) != LTGT
&& GET_CODE (x) != UNEQ);
}
int
minmax_operator (x, mode)
rtx x;
enum machine_mode mode;
{
enum rtx_code code = GET_CODE (x);
if (GET_MODE (x) != mode)
return FALSE;
return code == SMIN || code == SMAX || code == UMIN || code == UMAX;
}
int
cc_register (x, mode)
rtx x;
enum machine_mode mode;
{
if (mode == VOIDmode)
{
mode = GET_MODE (x);
if (GET_MODE_CLASS (mode) != MODE_CC)
return FALSE;
}
if ( GET_MODE (x) == mode
&& GET_CODE (x) == REG
&& REGNO (x) == CC_REGNUM)
return TRUE;
return FALSE;
}
int
dominant_cc_register (x, mode)
rtx x;
enum machine_mode mode;
{
if (mode == VOIDmode)
{
mode = GET_MODE (x);
if (GET_MODE_CLASS (mode) != MODE_CC)
return FALSE;
}
if ( mode != CC_DNEmode && mode != CC_DEQmode
&& mode != CC_DLEmode && mode != CC_DLTmode
&& mode != CC_DGEmode && mode != CC_DGTmode
&& mode != CC_DLEUmode && mode != CC_DLTUmode
&& mode != CC_DGEUmode && mode != CC_DGTUmode)
return FALSE;
return cc_register (x, mode);
}
int
symbol_mentioned_p (x)
rtx x;
{
const char * fmt;
int i;
if (GET_CODE (x) == SYMBOL_REF)
return 1;
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (symbol_mentioned_p (XVECEXP (x, i, j)))
return 1;
}
else if (fmt[i] == 'e' && symbol_mentioned_p (XEXP (x, i)))
return 1;
}
return 0;
}
int
label_mentioned_p (x)
rtx x;
{
const char * fmt;
int i;
if (GET_CODE (x) == LABEL_REF)
return 1;
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (label_mentioned_p (XVECEXP (x, i, j)))
return 1;
}
else if (fmt[i] == 'e' && label_mentioned_p (XEXP (x, i)))
return 1;
}
return 0;
}
enum rtx_code
minmax_code (x)
rtx x;
{
enum rtx_code code = GET_CODE (x);
if (code == SMAX)
return GE;
else if (code == SMIN)
return LE;
else if (code == UMIN)
return LEU;
else if (code == UMAX)
return GEU;
abort ();
}
int
adjacent_mem_locations (a, b)
rtx a, b;
{
if ((GET_CODE (XEXP (a, 0)) == REG
|| (GET_CODE (XEXP (a, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (a, 0), 1)) == CONST_INT))
&& (GET_CODE (XEXP (b, 0)) == REG
|| (GET_CODE (XEXP (b, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (b, 0), 1)) == CONST_INT)))
{
int val0 = 0, val1 = 0;
int reg0, reg1;
if (GET_CODE (XEXP (a, 0)) == PLUS)
{
reg0 = REGNO (XEXP (XEXP (a, 0), 0));
val0 = INTVAL (XEXP (XEXP (a, 0), 1));
}
else
reg0 = REGNO (XEXP (a, 0));
if (GET_CODE (XEXP (b, 0)) == PLUS)
{
reg1 = REGNO (XEXP (XEXP (b, 0), 0));
val1 = INTVAL (XEXP (XEXP (b, 0), 1));
}
else
reg1 = REGNO (XEXP (b, 0));
return (reg0 == reg1) && ((val1 - val0) == 4 || (val0 - val1) == 4);
}
return 0;
}
int
load_multiple_operation (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
HOST_WIDE_INT count = XVECLEN (op, 0);
int dest_regno;
rtx src_addr;
HOST_WIDE_INT i = 1, base = 0;
rtx elt;
if (count <= 1
|| GET_CODE (XVECEXP (op, 0, 0)) != SET)
return 0;
if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS)
{
i++;
base = 1;
if (GET_CODE (SET_DEST (elt)) != REG
|| GET_CODE (XEXP (SET_SRC (elt), 0)) != REG
|| REGNO (XEXP (SET_SRC (elt), 0)) != REGNO (SET_DEST (elt))
|| GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT
|| INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 1) * 4)
return 0;
}
if (count <= i
|| GET_CODE (XVECEXP (op, 0, i - 1)) != SET
|| GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != REG
|| GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != MEM)
return 0;
dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, i - 1)));
src_addr = XEXP (SET_SRC (XVECEXP (op, 0, i - 1)), 0);
for (; i < count; i++)
{
elt = XVECEXP (op, 0, i);
if (GET_CODE (elt) != SET
|| GET_CODE (SET_DEST (elt)) != REG
|| GET_MODE (SET_DEST (elt)) != SImode
|| REGNO (SET_DEST (elt)) != (unsigned int)(dest_regno + i - base)
|| GET_CODE (SET_SRC (elt)) != MEM
|| GET_MODE (SET_SRC (elt)) != SImode
|| GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
|| !rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
|| GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
|| INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != (i - base) * 4)
return 0;
}
return 1;
}
int
store_multiple_operation (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
HOST_WIDE_INT count = XVECLEN (op, 0);
int src_regno;
rtx dest_addr;
HOST_WIDE_INT i = 1, base = 0;
rtx elt;
if (count <= 1
|| GET_CODE (XVECEXP (op, 0, 0)) != SET)
return 0;
if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS)
{
i++;
base = 1;
if (GET_CODE (SET_DEST (elt)) != REG
|| GET_CODE (XEXP (SET_SRC (elt), 0)) != REG
|| REGNO (XEXP (SET_SRC (elt), 0)) != REGNO (SET_DEST (elt))
|| GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT
|| INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 1) * 4)
return 0;
}
if (count <= i
|| GET_CODE (XVECEXP (op, 0, i - 1)) != SET
|| GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != MEM
|| GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != REG)
return 0;
src_regno = REGNO (SET_SRC (XVECEXP (op, 0, i - 1)));
dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, i - 1)), 0);
for (; i < count; i++)
{
elt = XVECEXP (op, 0, i);
if (GET_CODE (elt) != SET
|| GET_CODE (SET_SRC (elt)) != REG
|| GET_MODE (SET_SRC (elt)) != SImode
|| REGNO (SET_SRC (elt)) != (unsigned int)(src_regno + i - base)
|| GET_CODE (SET_DEST (elt)) != MEM
|| GET_MODE (SET_DEST (elt)) != SImode
|| GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
|| !rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
|| GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
|| INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != (i - base) * 4)
return 0;
}
return 1;
}
int
load_multiple_sequence (operands, nops, regs, base, load_offset)
rtx * operands;
int nops;
int * regs;
int * base;
HOST_WIDE_INT * load_offset;
{
int unsorted_regs[4];
HOST_WIDE_INT unsorted_offsets[4];
int order[4];
int base_reg = -1;
int i;
if (nops < 2 || nops > 4)
abort ();
for (i = 0; i < nops; i++)
{
rtx reg;
rtx offset;
if (GET_CODE (operands[nops + i]) == SUBREG)
operands[nops + i] = alter_subreg (operands + (nops + i));
if (GET_CODE (operands[nops + i]) != MEM)
abort ();
if (MEM_VOLATILE_P (operands[nops + i]))
return 0;
offset = const0_rtx;
if ((GET_CODE (reg = XEXP (operands[nops + i], 0)) == REG
|| (GET_CODE (reg) == SUBREG
&& GET_CODE (reg = SUBREG_REG (reg)) == REG))
|| (GET_CODE (XEXP (operands[nops + i], 0)) == PLUS
&& ((GET_CODE (reg = XEXP (XEXP (operands[nops + i], 0), 0))
== REG)
|| (GET_CODE (reg) == SUBREG
&& GET_CODE (reg = SUBREG_REG (reg)) == REG))
&& (GET_CODE (offset = XEXP (XEXP (operands[nops + i], 0), 1))
== CONST_INT)))
{
if (i == 0)
{
base_reg = REGNO (reg);
unsorted_regs[0] = (GET_CODE (operands[i]) == REG
? REGNO (operands[i])
: REGNO (SUBREG_REG (operands[i])));
order[0] = 0;
}
else
{
if (base_reg != (int) REGNO (reg))
return 0;
unsorted_regs[i] = (GET_CODE (operands[i]) == REG
? REGNO (operands[i])
: REGNO (SUBREG_REG (operands[i])));
if (unsorted_regs[i] < unsorted_regs[order[0]])
order[0] = i;
}
if (unsorted_regs[i] < 0 || unsorted_regs[i] > 14
|| (i != nops - 1 && unsorted_regs[i] == base_reg))
return 0;
unsorted_offsets[i] = INTVAL (offset);
}
else
return 0;
}
for (i = 1; i < nops; i++)
{
int j;
order[i] = order[i - 1];
for (j = 0; j < nops; j++)
if (unsorted_regs[j] > unsorted_regs[order[i - 1]]
&& (order[i] == order[i - 1]
|| unsorted_regs[j] < unsorted_regs[order[i]]))
order[i] = j;
if (order[i] == order[i - 1])
return 0;
if (unsorted_offsets[order[i]] != unsorted_offsets[order[i - 1]] + 4)
return 0;
}
if (base)
{
*base = base_reg;
for (i = 0; i < nops; i++)
regs[i] = unsorted_regs[order[i]];
*load_offset = unsorted_offsets[order[0]];
}
if (unsorted_offsets[order[0]] == 0)
return 1;
if (unsorted_offsets[order[0]] == 4)
return 2;
if (unsorted_offsets[order[nops - 1]] == 0)
return 3;
if (unsorted_offsets[order[nops - 1]] == -4)
return 4;
if (nops == 2 && arm_ld_sched)
return 0;
return (const_ok_for_arm (unsorted_offsets[order[0]])
|| const_ok_for_arm (-unsorted_offsets[order[0]])) ? 5 : 0;
}
const char *
emit_ldm_seq (operands, nops)
rtx * operands;
int nops;
{
int regs[4];
int base_reg;
HOST_WIDE_INT offset;
char buf[100];
int i;
switch (load_multiple_sequence (operands, nops, regs, &base_reg, &offset))
{
case 1:
strcpy (buf, "ldm%?ia\t");
break;
case 2:
strcpy (buf, "ldm%?ib\t");
break;
case 3:
strcpy (buf, "ldm%?da\t");
break;
case 4:
strcpy (buf, "ldm%?db\t");
break;
case 5:
if (offset >= 0)
sprintf (buf, "add%%?\t%s%s, %s%s, #%ld", REGISTER_PREFIX,
reg_names[regs[0]], REGISTER_PREFIX, reg_names[base_reg],
(long) offset);
else
sprintf (buf, "sub%%?\t%s%s, %s%s, #%ld", REGISTER_PREFIX,
reg_names[regs[0]], REGISTER_PREFIX, reg_names[base_reg],
(long) -offset);
output_asm_insn (buf, operands);
base_reg = regs[0];
strcpy (buf, "ldm%?ia\t");
break;
default:
abort ();
}
sprintf (buf + strlen (buf), "%s%s, {%s%s", REGISTER_PREFIX,
reg_names[base_reg], REGISTER_PREFIX, reg_names[regs[0]]);
for (i = 1; i < nops; i++)
sprintf (buf + strlen (buf), ", %s%s", REGISTER_PREFIX,
reg_names[regs[i]]);
strcat (buf, "}\t%@ phole ldm");
output_asm_insn (buf, operands);
return "";
}
int
store_multiple_sequence (operands, nops, regs, base, load_offset)
rtx * operands;
int nops;
int * regs;
int * base;
HOST_WIDE_INT * load_offset;
{
int unsorted_regs[4];
HOST_WIDE_INT unsorted_offsets[4];
int order[4];
int base_reg = -1;
int i;
if (nops < 2 || nops > 4)
abort ();
for (i = 0; i < nops; i++)
{
rtx reg;
rtx offset;
if (GET_CODE (operands[nops + i]) == SUBREG)
operands[nops + i] = alter_subreg (operands + (nops + i));
if (GET_CODE (operands[nops + i]) != MEM)
abort ();
if (MEM_VOLATILE_P (operands[nops + i]))
return 0;
offset = const0_rtx;
if ((GET_CODE (reg = XEXP (operands[nops + i], 0)) == REG
|| (GET_CODE (reg) == SUBREG
&& GET_CODE (reg = SUBREG_REG (reg)) == REG))
|| (GET_CODE (XEXP (operands[nops + i], 0)) == PLUS
&& ((GET_CODE (reg = XEXP (XEXP (operands[nops + i], 0), 0))
== REG)
|| (GET_CODE (reg) == SUBREG
&& GET_CODE (reg = SUBREG_REG (reg)) == REG))
&& (GET_CODE (offset = XEXP (XEXP (operands[nops + i], 0), 1))
== CONST_INT)))
{
if (i == 0)
{
base_reg = REGNO (reg);
unsorted_regs[0] = (GET_CODE (operands[i]) == REG
? REGNO (operands[i])
: REGNO (SUBREG_REG (operands[i])));
order[0] = 0;
}
else
{
if (base_reg != (int) REGNO (reg))
return 0;
unsorted_regs[i] = (GET_CODE (operands[i]) == REG
? REGNO (operands[i])
: REGNO (SUBREG_REG (operands[i])));
if (unsorted_regs[i] < unsorted_regs[order[0]])
order[0] = i;
}
if (unsorted_regs[i] < 0 || unsorted_regs[i] > 14)
return 0;
unsorted_offsets[i] = INTVAL (offset);
}
else
return 0;
}
for (i = 1; i < nops; i++)
{
int j;
order[i] = order[i - 1];
for (j = 0; j < nops; j++)
if (unsorted_regs[j] > unsorted_regs[order[i - 1]]
&& (order[i] == order[i - 1]
|| unsorted_regs[j] < unsorted_regs[order[i]]))
order[i] = j;
if (order[i] == order[i - 1])
return 0;
if (unsorted_offsets[order[i]] != unsorted_offsets[order[i - 1]] + 4)
return 0;
}
if (base)
{
*base = base_reg;
for (i = 0; i < nops; i++)
regs[i] = unsorted_regs[order[i]];
*load_offset = unsorted_offsets[order[0]];
}
if (unsorted_offsets[order[0]] == 0)
return 1;
if (unsorted_offsets[order[0]] == 4)
return 2;
if (unsorted_offsets[order[nops - 1]] == 0)
return 3;
if (unsorted_offsets[order[nops - 1]] == -4)
return 4;
return 0;
}
const char *
emit_stm_seq (operands, nops)
rtx * operands;
int nops;
{
int regs[4];
int base_reg;
HOST_WIDE_INT offset;
char buf[100];
int i;
switch (store_multiple_sequence (operands, nops, regs, &base_reg, &offset))
{
case 1:
strcpy (buf, "stm%?ia\t");
break;
case 2:
strcpy (buf, "stm%?ib\t");
break;
case 3:
strcpy (buf, "stm%?da\t");
break;
case 4:
strcpy (buf, "stm%?db\t");
break;
default:
abort ();
}
sprintf (buf + strlen (buf), "%s%s, {%s%s", REGISTER_PREFIX,
reg_names[base_reg], REGISTER_PREFIX, reg_names[regs[0]]);
for (i = 1; i < nops; i++)
sprintf (buf + strlen (buf), ", %s%s", REGISTER_PREFIX,
reg_names[regs[i]]);
strcat (buf, "}\t%@ phole stm");
output_asm_insn (buf, operands);
return "";
}
int
multi_register_push (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) != PARALLEL
|| (GET_CODE (XVECEXP (op, 0, 0)) != SET)
|| (GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != UNSPEC)
|| (XINT (SET_SRC (XVECEXP (op, 0, 0)), 1) != UNSPEC_PUSH_MULT))
return 0;
return 1;
}
rtx
arm_gen_load_multiple (base_regno, count, from, up, write_back, unchanging_p,
in_struct_p, scalar_p)
int base_regno;
int count;
rtx from;
int up;
int write_back;
int unchanging_p;
int in_struct_p;
int scalar_p;
{
int i = 0, j;
rtx result;
int sign = up ? 1 : -1;
rtx mem;
if (arm_is_xscale && count <= 2 && ! optimize_size)
{
rtx seq;
start_sequence ();
for (i = 0; i < count; i++)
{
mem = gen_rtx_MEM (SImode, plus_constant (from, i * 4 * sign));
RTX_UNCHANGING_P (mem) = unchanging_p;
MEM_IN_STRUCT_P (mem) = in_struct_p;
MEM_SCALAR_P (mem) = scalar_p;
emit_move_insn (gen_rtx_REG (SImode, base_regno + i), mem);
}
if (write_back)
emit_move_insn (from, plus_constant (from, count * 4 * sign));
seq = get_insns ();
end_sequence ();
return seq;
}
result = gen_rtx_PARALLEL (VOIDmode,
rtvec_alloc (count + (write_back ? 1 : 0)));
if (write_back)
{
XVECEXP (result, 0, 0)
= gen_rtx_SET (GET_MODE (from), from,
plus_constant (from, count * 4 * sign));
i = 1;
count++;
}
for (j = 0; i < count; i++, j++)
{
mem = gen_rtx_MEM (SImode, plus_constant (from, j * 4 * sign));
RTX_UNCHANGING_P (mem) = unchanging_p;
MEM_IN_STRUCT_P (mem) = in_struct_p;
MEM_SCALAR_P (mem) = scalar_p;
XVECEXP (result, 0, i)
= gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, base_regno + j), mem);
}
return result;
}
rtx
arm_gen_store_multiple (base_regno, count, to, up, write_back, unchanging_p,
in_struct_p, scalar_p)
int base_regno;
int count;
rtx to;
int up;
int write_back;
int unchanging_p;
int in_struct_p;
int scalar_p;
{
int i = 0, j;
rtx result;
int sign = up ? 1 : -1;
rtx mem;
if (arm_is_xscale && count <= 2 && ! optimize_size)
{
rtx seq;
start_sequence ();
for (i = 0; i < count; i++)
{
mem = gen_rtx_MEM (SImode, plus_constant (to, i * 4 * sign));
RTX_UNCHANGING_P (mem) = unchanging_p;
MEM_IN_STRUCT_P (mem) = in_struct_p;
MEM_SCALAR_P (mem) = scalar_p;
emit_move_insn (mem, gen_rtx_REG (SImode, base_regno + i));
}
if (write_back)
emit_move_insn (to, plus_constant (to, count * 4 * sign));
seq = get_insns ();
end_sequence ();
return seq;
}
result = gen_rtx_PARALLEL (VOIDmode,
rtvec_alloc (count + (write_back ? 1 : 0)));
if (write_back)
{
XVECEXP (result, 0, 0)
= gen_rtx_SET (GET_MODE (to), to,
plus_constant (to, count * 4 * sign));
i = 1;
count++;
}
for (j = 0; i < count; i++, j++)
{
mem = gen_rtx_MEM (SImode, plus_constant (to, j * 4 * sign));
RTX_UNCHANGING_P (mem) = unchanging_p;
MEM_IN_STRUCT_P (mem) = in_struct_p;
MEM_SCALAR_P (mem) = scalar_p;
XVECEXP (result, 0, i)
= gen_rtx_SET (VOIDmode, mem, gen_rtx_REG (SImode, base_regno + j));
}
return result;
}
int
arm_gen_movstrqi (operands)
rtx * operands;
{
HOST_WIDE_INT in_words_to_go, out_words_to_go, last_bytes;
int i;
rtx src, dst;
rtx st_src, st_dst, fin_src, fin_dst;
rtx part_bytes_reg = NULL;
rtx mem;
int dst_unchanging_p, dst_in_struct_p, src_unchanging_p, src_in_struct_p;
int dst_scalar_p, src_scalar_p;
if (GET_CODE (operands[2]) != CONST_INT
|| GET_CODE (operands[3]) != CONST_INT
|| INTVAL (operands[2]) > 64
|| INTVAL (operands[3]) & 3)
return 0;
st_dst = XEXP (operands[0], 0);
st_src = XEXP (operands[1], 0);
dst_unchanging_p = RTX_UNCHANGING_P (operands[0]);
dst_in_struct_p = MEM_IN_STRUCT_P (operands[0]);
dst_scalar_p = MEM_SCALAR_P (operands[0]);
src_unchanging_p = RTX_UNCHANGING_P (operands[1]);
src_in_struct_p = MEM_IN_STRUCT_P (operands[1]);
src_scalar_p = MEM_SCALAR_P (operands[1]);
fin_dst = dst = copy_to_mode_reg (SImode, st_dst);
fin_src = src = copy_to_mode_reg (SImode, st_src);
in_words_to_go = ARM_NUM_INTS (INTVAL (operands[2]));
out_words_to_go = INTVAL (operands[2]) / 4;
last_bytes = INTVAL (operands[2]) & 3;
if (out_words_to_go != in_words_to_go && ((in_words_to_go - 1) & 3) != 0)
part_bytes_reg = gen_rtx_REG (SImode, (in_words_to_go - 1) & 3);
for (i = 0; in_words_to_go >= 2; i+=4)
{
if (in_words_to_go > 4)
emit_insn (arm_gen_load_multiple (0, 4, src, TRUE, TRUE,
src_unchanging_p,
src_in_struct_p,
src_scalar_p));
else
emit_insn (arm_gen_load_multiple (0, in_words_to_go, src, TRUE,
FALSE, src_unchanging_p,
src_in_struct_p, src_scalar_p));
if (out_words_to_go)
{
if (out_words_to_go > 4)
emit_insn (arm_gen_store_multiple (0, 4, dst, TRUE, TRUE,
dst_unchanging_p,
dst_in_struct_p,
dst_scalar_p));
else if (out_words_to_go != 1)
emit_insn (arm_gen_store_multiple (0, out_words_to_go,
dst, TRUE,
(last_bytes == 0
? FALSE : TRUE),
dst_unchanging_p,
dst_in_struct_p,
dst_scalar_p));
else
{
mem = gen_rtx_MEM (SImode, dst);
RTX_UNCHANGING_P (mem) = dst_unchanging_p;
MEM_IN_STRUCT_P (mem) = dst_in_struct_p;
MEM_SCALAR_P (mem) = dst_scalar_p;
emit_move_insn (mem, gen_rtx_REG (SImode, 0));
if (last_bytes != 0)
emit_insn (gen_addsi3 (dst, dst, GEN_INT (4)));
}
}
in_words_to_go -= in_words_to_go < 4 ? in_words_to_go : 4;
out_words_to_go -= out_words_to_go < 4 ? out_words_to_go : 4;
}
if (out_words_to_go)
{
rtx sreg;
mem = gen_rtx_MEM (SImode, src);
RTX_UNCHANGING_P (mem) = src_unchanging_p;
MEM_IN_STRUCT_P (mem) = src_in_struct_p;
MEM_SCALAR_P (mem) = src_scalar_p;
emit_move_insn (sreg = gen_reg_rtx (SImode), mem);
emit_move_insn (fin_src = gen_reg_rtx (SImode), plus_constant (src, 4));
mem = gen_rtx_MEM (SImode, dst);
RTX_UNCHANGING_P (mem) = dst_unchanging_p;
MEM_IN_STRUCT_P (mem) = dst_in_struct_p;
MEM_SCALAR_P (mem) = dst_scalar_p;
emit_move_insn (mem, sreg);
emit_move_insn (fin_dst = gen_reg_rtx (SImode), plus_constant (dst, 4));
in_words_to_go--;
if (in_words_to_go)
abort ();
}
if (in_words_to_go)
{
if (in_words_to_go < 0)
abort ();
mem = gen_rtx_MEM (SImode, src);
RTX_UNCHANGING_P (mem) = src_unchanging_p;
MEM_IN_STRUCT_P (mem) = src_in_struct_p;
MEM_SCALAR_P (mem) = src_scalar_p;
part_bytes_reg = copy_to_mode_reg (SImode, mem);
}
if (last_bytes && part_bytes_reg == NULL)
abort ();
if (BYTES_BIG_ENDIAN && last_bytes)
{
rtx tmp = gen_reg_rtx (SImode);
emit_insn (gen_lshrsi3 (tmp, part_bytes_reg,
GEN_INT (8 * (4 - last_bytes))));
part_bytes_reg = tmp;
while (last_bytes)
{
mem = gen_rtx_MEM (QImode, plus_constant (dst, last_bytes - 1));
RTX_UNCHANGING_P (mem) = dst_unchanging_p;
MEM_IN_STRUCT_P (mem) = dst_in_struct_p;
MEM_SCALAR_P (mem) = dst_scalar_p;
emit_move_insn (mem, gen_lowpart (QImode, part_bytes_reg));
if (--last_bytes)
{
tmp = gen_reg_rtx (SImode);
emit_insn (gen_lshrsi3 (tmp, part_bytes_reg, GEN_INT (8)));
part_bytes_reg = tmp;
}
}
}
else
{
if (last_bytes > 1)
{
mem = gen_rtx_MEM (HImode, dst);
RTX_UNCHANGING_P (mem) = dst_unchanging_p;
MEM_IN_STRUCT_P (mem) = dst_in_struct_p;
MEM_SCALAR_P (mem) = dst_scalar_p;
emit_move_insn (mem, gen_lowpart (HImode, part_bytes_reg));
last_bytes -= 2;
if (last_bytes)
{
rtx tmp = gen_reg_rtx (SImode);
emit_insn (gen_addsi3 (dst, dst, GEN_INT (2)));
emit_insn (gen_lshrsi3 (tmp, part_bytes_reg, GEN_INT (16)));
part_bytes_reg = tmp;
}
}
if (last_bytes)
{
mem = gen_rtx_MEM (QImode, dst);
RTX_UNCHANGING_P (mem) = dst_unchanging_p;
MEM_IN_STRUCT_P (mem) = dst_in_struct_p;
MEM_SCALAR_P (mem) = dst_scalar_p;
emit_move_insn (mem, gen_lowpart (QImode, part_bytes_reg));
}
}
return 1;
}
rtx
arm_gen_rotated_half_load (memref)
rtx memref;
{
HOST_WIDE_INT offset = 0;
rtx base = XEXP (memref, 0);
if (GET_CODE (base) == PLUS)
{
offset = INTVAL (XEXP (base, 1));
base = XEXP (base, 0);
}
if (TARGET_MMU_TRAPS
&& ((BYTES_BIG_ENDIAN ? 1 : 0) ^ ((offset & 2) == 0)))
return NULL;
base = gen_rtx_MEM (SImode, plus_constant (base, offset & ~2));
if ((BYTES_BIG_ENDIAN ? 1 : 0) ^ ((offset & 2) == 2))
return base;
return gen_rtx_ROTATE (SImode, base, GEN_INT (16));
}
static enum machine_mode
select_dominance_cc_mode (x, y, cond_or)
rtx x;
rtx y;
HOST_WIDE_INT cond_or;
{
enum rtx_code cond1, cond2;
int swapped = 0;
if ((arm_select_cc_mode (cond1 = GET_CODE (x), XEXP (x, 0), XEXP (x, 1))
!= CCmode)
|| (arm_select_cc_mode (cond2 = GET_CODE (y), XEXP (y, 0), XEXP (y, 1))
!= CCmode))
return CCmode;
if (cond_or == 1)
cond1 = reverse_condition (cond1);
if (cond1 != cond2
&& !comparison_dominates_p (cond1, cond2)
&& (swapped = 1, !comparison_dominates_p (cond2, cond1)))
return CCmode;
if (swapped)
{
enum rtx_code temp = cond1;
cond1 = cond2;
cond2 = temp;
}
switch (cond1)
{
case EQ:
if (cond2 == EQ || !cond_or)
return CC_DEQmode;
switch (cond2)
{
case LE: return CC_DLEmode;
case LEU: return CC_DLEUmode;
case GE: return CC_DGEmode;
case GEU: return CC_DGEUmode;
default: break;
}
break;
case LT:
if (cond2 == LT || !cond_or)
return CC_DLTmode;
if (cond2 == LE)
return CC_DLEmode;
if (cond2 == NE)
return CC_DNEmode;
break;
case GT:
if (cond2 == GT || !cond_or)
return CC_DGTmode;
if (cond2 == GE)
return CC_DGEmode;
if (cond2 == NE)
return CC_DNEmode;
break;
case LTU:
if (cond2 == LTU || !cond_or)
return CC_DLTUmode;
if (cond2 == LEU)
return CC_DLEUmode;
if (cond2 == NE)
return CC_DNEmode;
break;
case GTU:
if (cond2 == GTU || !cond_or)
return CC_DGTUmode;
if (cond2 == GEU)
return CC_DGEUmode;
if (cond2 == NE)
return CC_DNEmode;
break;
case NE:
return CC_DNEmode;
case LE:
return CC_DLEmode;
case GE:
return CC_DGEmode;
case LEU:
return CC_DLEUmode;
case GEU:
return CC_DGEUmode;
default:
break;
}
abort ();
}
enum machine_mode
arm_select_cc_mode (op, x, y)
enum rtx_code op;
rtx x;
rtx y;
{
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
{
switch (op)
{
case EQ:
case NE:
case UNORDERED:
case ORDERED:
case UNLT:
case UNLE:
case UNGT:
case UNGE:
case UNEQ:
case LTGT:
return CCFPmode;
case LT:
case LE:
case GT:
case GE:
return CCFPEmode;
default:
abort ();
}
}
if (GET_MODE (y) == SImode && GET_CODE (y) == REG
&& (GET_CODE (x) == ASHIFT || GET_CODE (x) == ASHIFTRT
|| GET_CODE (x) == LSHIFTRT || GET_CODE (x) == ROTATE
|| GET_CODE (x) == ROTATERT))
return CC_SWPmode;
if (GET_MODE (x) == SImode
&& GET_CODE (x) == ASHIFT
&& GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) == 24
&& GET_CODE (XEXP (x, 0)) == SUBREG
&& GET_CODE (SUBREG_REG (XEXP (x, 0))) == MEM
&& GET_MODE (SUBREG_REG (XEXP (x, 0))) == QImode
&& (op == EQ || op == NE
|| op == GEU || op == GTU || op == LTU || op == LEU)
&& GET_CODE (y) == CONST_INT)
return CC_Zmode;
if (GET_CODE (x) == IF_THEN_ELSE
&& (XEXP (x, 2) == const0_rtx
|| XEXP (x, 2) == const1_rtx)
&& GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
&& GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<')
return select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1),
INTVAL (XEXP (x, 2)));
if (GET_CODE (x) == AND
&& GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
&& GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<')
return select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1), 0);
if (GET_CODE (x) == IOR
&& GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
&& GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<')
return select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1), 2);
if (GET_MODE (x) == SImode
&& y == const0_rtx
&& (op == EQ || op == NE || op == LT || op == GE)
&& (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS
|| GET_CODE (x) == AND || GET_CODE (x) == IOR
|| GET_CODE (x) == XOR || GET_CODE (x) == MULT
|| GET_CODE (x) == NOT || GET_CODE (x) == NEG
|| GET_CODE (x) == LSHIFTRT
|| GET_CODE (x) == ASHIFT || GET_CODE (x) == ASHIFTRT
|| GET_CODE (x) == ROTATERT || GET_CODE (x) == ZERO_EXTRACT))
return CC_NOOVmode;
if (GET_MODE (x) == QImode && (op == EQ || op == NE))
return CC_Zmode;
if (GET_MODE (x) == SImode && (op == LTU || op == GEU)
&& GET_CODE (x) == PLUS
&& (rtx_equal_p (XEXP (x, 0), y) || rtx_equal_p (XEXP (x, 1), y)))
return CC_Cmode;
return CCmode;
}
rtx
arm_gen_compare_reg (code, x, y)
enum rtx_code code;
rtx x, y;
{
enum machine_mode mode = SELECT_CC_MODE (code, x, y);
rtx cc_reg = gen_rtx_REG (mode, CC_REGNUM);
emit_insn (gen_rtx_SET (VOIDmode, cc_reg,
gen_rtx_COMPARE (mode, x, y)));
return cc_reg;
}
rtx
arm_gen_return_addr_mask ()
{
rtx reg = gen_reg_rtx (Pmode);
emit_insn (gen_return_addr_mask (reg));
return reg;
}
void
arm_reload_in_hi (operands)
rtx * operands;
{
rtx ref = operands[1];
rtx base, scratch;
HOST_WIDE_INT offset = 0;
if (GET_CODE (ref) == SUBREG)
{
offset = SUBREG_BYTE (ref);
ref = SUBREG_REG (ref);
}
if (GET_CODE (ref) == REG)
{
if (reg_equiv_mem[REGNO (ref)])
{
ref = reg_equiv_mem[REGNO (ref)];
base = find_replacement (&XEXP (ref, 0));
}
else
base = reg_equiv_address[REGNO (ref)];
}
else
base = find_replacement (&XEXP (ref, 0));
if (GET_CODE (base) == MINUS
|| (GET_CODE (base) == PLUS && GET_CODE (XEXP (base, 1)) != CONST_INT))
{
rtx base_plus = gen_rtx_REG (SImode, REGNO (operands[2]) + 1);
emit_insn (gen_rtx_SET (VOIDmode, base_plus, base));
base = base_plus;
}
else if (GET_CODE (base) == PLUS)
{
HOST_WIDE_INT hi, lo;
offset += INTVAL (XEXP (base, 1));
base = XEXP (base, 0);
lo = (offset >= 0
? (offset & 0xfff)
: -((-offset) & 0xfff));
if (lo == 4095)
lo &= 0x7ff;
hi = ((((offset - lo) & (HOST_WIDE_INT) 0xffffffff)
^ (HOST_WIDE_INT) 0x80000000)
- (HOST_WIDE_INT) 0x80000000);
if (hi + lo != offset)
abort ();
if (hi != 0)
{
rtx base_plus = gen_rtx_REG (SImode, REGNO (operands[2]) + 1);
emit_insn (gen_addsi3 (base_plus, base, GEN_INT (hi)));
base = base_plus;
offset = lo;
}
}
if (REGNO (operands[2]) == REGNO (operands[0]))
scratch = gen_rtx_REG (SImode, REGNO (operands[2]) + 1);
else
scratch = gen_rtx_REG (SImode, REGNO (operands[2]));
emit_insn (gen_zero_extendqisi2 (scratch,
gen_rtx_MEM (QImode,
plus_constant (base,
offset))));
emit_insn (gen_zero_extendqisi2 (gen_rtx_SUBREG (SImode, operands[0], 0),
gen_rtx_MEM (QImode,
plus_constant (base,
offset + 1))));
if (!BYTES_BIG_ENDIAN)
emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_SUBREG (SImode, operands[0], 0),
gen_rtx_IOR (SImode,
gen_rtx_ASHIFT
(SImode,
gen_rtx_SUBREG (SImode, operands[0], 0),
GEN_INT (8)),
scratch)));
else
emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_SUBREG (SImode, operands[0], 0),
gen_rtx_IOR (SImode,
gen_rtx_ASHIFT (SImode, scratch,
GEN_INT (8)),
gen_rtx_SUBREG (SImode, operands[0],
0))));
}
void
arm_reload_out_hi (operands)
rtx * operands;
{
rtx ref = operands[0];
rtx outval = operands[1];
rtx base, scratch;
HOST_WIDE_INT offset = 0;
if (GET_CODE (ref) == SUBREG)
{
offset = SUBREG_BYTE (ref);
ref = SUBREG_REG (ref);
}
if (GET_CODE (ref) == REG)
{
if (reg_equiv_mem[REGNO (ref)])
{
ref = reg_equiv_mem[REGNO (ref)];
base = find_replacement (&XEXP (ref, 0));
}
else
base = reg_equiv_address[REGNO (ref)];
}
else
base = find_replacement (&XEXP (ref, 0));
scratch = gen_rtx_REG (SImode, REGNO (operands[2]));
if (GET_CODE (base) == MINUS
|| (GET_CODE (base) == PLUS && GET_CODE (XEXP (base, 1)) != CONST_INT))
{
rtx base_plus = gen_rtx_REG (SImode, REGNO (operands[2]) + 1);
if (reg_overlap_mentioned_p (base_plus, outval))
{
if (!reg_overlap_mentioned_p (scratch, outval))
{
rtx tmp = scratch;
scratch = base_plus;
base_plus = tmp;
}
else
{
rtx scratch_hi = gen_rtx_REG (HImode, REGNO (operands[2]));
emit_insn (gen_movhi (scratch_hi, outval));
outval = scratch_hi;
}
}
emit_insn (gen_rtx_SET (VOIDmode, base_plus, base));
base = base_plus;
}
else if (GET_CODE (base) == PLUS)
{
HOST_WIDE_INT hi, lo;
offset += INTVAL (XEXP (base, 1));
base = XEXP (base, 0);
lo = (offset >= 0
? (offset & 0xfff)
: -((-offset) & 0xfff));
if (lo == 4095)
lo &= 0x7ff;
hi = ((((offset - lo) & (HOST_WIDE_INT) 0xffffffff)
^ (HOST_WIDE_INT) 0x80000000)
- (HOST_WIDE_INT) 0x80000000);
if (hi + lo != offset)
abort ();
if (hi != 0)
{
rtx base_plus = gen_rtx_REG (SImode, REGNO (operands[2]) + 1);
if (reg_overlap_mentioned_p (base_plus, outval))
{
if (!reg_overlap_mentioned_p (scratch, outval))
{
rtx tmp = scratch;
scratch = base_plus;
base_plus = tmp;
}
else
{
rtx scratch_hi = gen_rtx_REG (HImode, REGNO (operands[2]));
emit_insn (gen_movhi (scratch_hi, outval));
outval = scratch_hi;
}
}
emit_insn (gen_addsi3 (base_plus, base, GEN_INT (hi)));
base = base_plus;
offset = lo;
}
}
if (BYTES_BIG_ENDIAN)
{
emit_insn (gen_movqi (gen_rtx_MEM (QImode,
plus_constant (base, offset + 1)),
gen_lowpart (QImode, outval)));
emit_insn (gen_lshrsi3 (scratch,
gen_rtx_SUBREG (SImode, outval, 0),
GEN_INT (8)));
emit_insn (gen_movqi (gen_rtx_MEM (QImode, plus_constant (base, offset)),
gen_lowpart (QImode, scratch)));
}
else
{
emit_insn (gen_movqi (gen_rtx_MEM (QImode, plus_constant (base, offset)),
gen_lowpart (QImode, outval)));
emit_insn (gen_lshrsi3 (scratch,
gen_rtx_SUBREG (SImode, outval, 0),
GEN_INT (8)));
emit_insn (gen_movqi (gen_rtx_MEM (QImode,
plus_constant (base, offset + 1)),
gen_lowpart (QImode, scratch)));
}
}
static void
arm_print_value (f, x)
FILE * f;
rtx x;
{
switch (GET_CODE (x))
{
case CONST_INT:
fprintf (f, HOST_WIDE_INT_PRINT_HEX, INTVAL (x));
return;
case CONST_DOUBLE:
fprintf (f, "<0x%lx,0x%lx>", (long)XWINT (x, 2), (long)XWINT (x, 3));
return;
case CONST_STRING:
fprintf (f, "\"%s\"", XSTR (x, 0));
return;
case SYMBOL_REF:
fprintf (f, "`%s'", XSTR (x, 0));
return;
case LABEL_REF:
fprintf (f, "L%d", INSN_UID (XEXP (x, 0)));
return;
case CONST:
arm_print_value (f, XEXP (x, 0));
return;
case PLUS:
arm_print_value (f, XEXP (x, 0));
fprintf (f, "+");
arm_print_value (f, XEXP (x, 1));
return;
case PC:
fprintf (f, "pc");
return;
default:
fprintf (f, "????");
return;
}
}
struct minipool_node
{
Mnode * next;
Mnode * prev;
HOST_WIDE_INT max_address;
HOST_WIDE_INT min_address;
int refcount;
HOST_WIDE_INT offset;
rtx value;
enum machine_mode mode;
int fix_size;
};
struct minipool_fixup
{
Mfix * next;
rtx insn;
HOST_WIDE_INT address;
rtx * loc;
enum machine_mode mode;
int fix_size;
rtx value;
Mnode * minipool;
HOST_WIDE_INT forwards;
HOST_WIDE_INT backwards;
};
#define MINIPOOL_FIX_SIZE(mode) \
(GET_MODE_SIZE ((mode)) >= 4 ? GET_MODE_SIZE ((mode)) : 4)
static Mnode * minipool_vector_head;
static Mnode * minipool_vector_tail;
static rtx minipool_vector_label;
Mfix * minipool_fix_head;
Mfix * minipool_fix_tail;
Mfix * minipool_barrier;
static rtx
is_jump_table (insn)
rtx insn;
{
rtx table;
if (GET_CODE (insn) == JUMP_INSN
&& JUMP_LABEL (insn) != NULL
&& ((table = next_real_insn (JUMP_LABEL (insn)))
== next_real_insn (insn))
&& table != NULL
&& GET_CODE (table) == JUMP_INSN
&& (GET_CODE (PATTERN (table)) == ADDR_VEC
|| GET_CODE (PATTERN (table)) == ADDR_DIFF_VEC))
return table;
return NULL_RTX;
}
#ifndef JUMP_TABLES_IN_TEXT_SECTION
#define JUMP_TABLES_IN_TEXT_SECTION 0
#endif
static HOST_WIDE_INT
get_jump_table_size (insn)
rtx insn;
{
if (JUMP_TABLES_IN_TEXT_SECTION
#if !defined(READONLY_DATA_SECTION) && !defined(READONLY_DATA_SECTION_ASM_OP)
|| 1
#endif
)
{
rtx body = PATTERN (insn);
int elt = GET_CODE (body) == ADDR_DIFF_VEC ? 1 : 0;
return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, elt);
}
return 0;
}
static Mnode *
move_minipool_fix_forward_ref (mp, max_mp, max_address)
Mnode * mp;
Mnode * max_mp;
HOST_WIDE_INT max_address;
{
if (mp == max_mp)
abort ();
if (max_mp == NULL)
{
if (max_address < mp->max_address)
mp->max_address = max_address;
}
else
{
if (max_address > max_mp->max_address - mp->fix_size)
mp->max_address = max_mp->max_address - mp->fix_size;
else
mp->max_address = max_address;
mp->prev->next = mp->next;
if (mp->next != NULL)
mp->next->prev = mp->prev;
else
minipool_vector_tail = mp->prev;
mp->next = max_mp;
mp->prev = max_mp->prev;
max_mp->prev = mp;
if (mp->prev != NULL)
mp->prev->next = mp;
else
minipool_vector_head = mp;
}
max_mp = mp;
while (mp->prev != NULL
&& mp->prev->max_address > mp->max_address - mp->prev->fix_size)
{
mp->prev->max_address = mp->max_address - mp->prev->fix_size;
mp = mp->prev;
}
return max_mp;
}
static Mnode *
add_minipool_forward_ref (fix)
Mfix * fix;
{
Mnode * max_mp = NULL;
HOST_WIDE_INT max_address = fix->address + fix->forwards;
Mnode * mp;
if (minipool_vector_head &&
fix->address >= minipool_vector_head->max_address - fix->fix_size)
return NULL;
for (mp = minipool_vector_head; mp != NULL; mp = mp->next)
{
if (GET_CODE (fix->value) == GET_CODE (mp->value)
&& fix->mode == mp->mode
&& (GET_CODE (fix->value) != CODE_LABEL
|| (CODE_LABEL_NUMBER (fix->value)
== CODE_LABEL_NUMBER (mp->value)))
&& rtx_equal_p (fix->value, mp->value))
{
mp->refcount++;
return move_minipool_fix_forward_ref (mp, max_mp, max_address);
}
if (max_mp == NULL
&& mp->max_address > max_address)
max_mp = mp;
}
mp = xmalloc (sizeof (* mp));
mp->fix_size = fix->fix_size;
mp->mode = fix->mode;
mp->value = fix->value;
mp->refcount = 1;
mp->min_address = -65536;
if (max_mp == NULL)
{
mp->max_address = max_address;
mp->next = NULL;
mp->prev = minipool_vector_tail;
if (mp->prev == NULL)
{
minipool_vector_head = mp;
minipool_vector_label = gen_label_rtx ();
}
else
mp->prev->next = mp;
minipool_vector_tail = mp;
}
else
{
if (max_address > max_mp->max_address - mp->fix_size)
mp->max_address = max_mp->max_address - mp->fix_size;
else
mp->max_address = max_address;
mp->next = max_mp;
mp->prev = max_mp->prev;
max_mp->prev = mp;
if (mp->prev != NULL)
mp->prev->next = mp;
else
minipool_vector_head = mp;
}
max_mp = mp;
while (mp->prev != NULL
&& mp->prev->max_address > mp->max_address - mp->prev->fix_size)
{
mp->prev->max_address = mp->max_address - mp->prev->fix_size;
mp = mp->prev;
}
return max_mp;
}
static Mnode *
move_minipool_fix_backward_ref (mp, min_mp, min_address)
Mnode * mp;
Mnode * min_mp;
HOST_WIDE_INT min_address;
{
HOST_WIDE_INT offset;
if (mp == min_mp)
abort ();
if (min_mp == NULL)
{
if (min_address > mp->min_address)
mp->min_address = min_address;
}
else
{
mp->min_address = min_address;
mp->next->prev = mp->prev;
if (mp->prev != NULL)
mp->prev->next = mp->next;
else
minipool_vector_head = mp->next;
mp->prev = min_mp;
mp->next = min_mp->next;
min_mp->next = mp;
if (mp->next != NULL)
mp->next->prev = mp;
else
minipool_vector_tail = mp;
}
min_mp = mp;
offset = 0;
for (mp = minipool_vector_head; mp != NULL; mp = mp->next)
{
mp->offset = offset;
if (mp->refcount > 0)
offset += mp->fix_size;
if (mp->next && mp->next->min_address < mp->min_address + mp->fix_size)
mp->next->min_address = mp->min_address + mp->fix_size;
}
return min_mp;
}
static Mnode *
add_minipool_backward_ref (fix)
Mfix * fix;
{
Mnode * min_mp = NULL;
HOST_WIDE_INT min_address = fix->address - fix->backwards;
Mnode * mp;
if (min_address >= minipool_barrier->address
|| (minipool_vector_tail->min_address + fix->fix_size
>= minipool_barrier->address))
return NULL;
for (mp = minipool_vector_tail; mp != NULL; mp = mp->prev)
{
if (GET_CODE (fix->value) == GET_CODE (mp->value)
&& fix->mode == mp->mode
&& (GET_CODE (fix->value) != CODE_LABEL
|| (CODE_LABEL_NUMBER (fix->value)
== CODE_LABEL_NUMBER (mp->value)))
&& rtx_equal_p (fix->value, mp->value)
&& (mp->max_address
> (minipool_barrier->address
+ minipool_vector_tail->offset
+ minipool_vector_tail->fix_size)))
{
mp->refcount++;
return move_minipool_fix_backward_ref (mp, min_mp, min_address);
}
if (min_mp != NULL)
mp->min_address += fix->fix_size;
else
{
if (mp->min_address < min_address)
min_mp = mp;
else if (mp->max_address
< minipool_barrier->address + mp->offset + fix->fix_size)
{
min_mp = mp;
min_address = mp->min_address + fix->fix_size;
}
}
}
mp = xmalloc (sizeof (* mp));
mp->fix_size = fix->fix_size;
mp->mode = fix->mode;
mp->value = fix->value;
mp->refcount = 1;
mp->max_address = minipool_barrier->address + 65536;
mp->min_address = min_address;
if (min_mp == NULL)
{
mp->prev = NULL;
mp->next = minipool_vector_head;
if (mp->next == NULL)
{
minipool_vector_tail = mp;
minipool_vector_label = gen_label_rtx ();
}
else
mp->next->prev = mp;
minipool_vector_head = mp;
}
else
{
mp->next = min_mp->next;
mp->prev = min_mp;
min_mp->next = mp;
if (mp->next != NULL)
mp->next->prev = mp;
else
minipool_vector_tail = mp;
}
min_mp = mp;
if (mp->prev)
mp = mp->prev;
else
mp->offset = 0;
while (mp->next != NULL)
{
if (mp->next->min_address < mp->min_address + mp->fix_size)
mp->next->min_address = mp->min_address + mp->fix_size;
if (mp->refcount)
mp->next->offset = mp->offset + mp->fix_size;
else
mp->next->offset = mp->offset;
mp = mp->next;
}
return min_mp;
}
static void
assign_minipool_offsets (barrier)
Mfix * barrier;
{
HOST_WIDE_INT offset = 0;
Mnode * mp;
minipool_barrier = barrier;
for (mp = minipool_vector_head; mp != NULL; mp = mp->next)
{
mp->offset = offset;
if (mp->refcount > 0)
offset += mp->fix_size;
}
}
static void
dump_minipool (scan)
rtx scan;
{
Mnode * mp;
Mnode * nmp;
if (rtl_dump_file)
fprintf (rtl_dump_file,
";; Emitting minipool after insn %u; address %ld\n",
INSN_UID (scan), (unsigned long) minipool_barrier->address);
scan = emit_label_after (gen_label_rtx (), scan);
scan = emit_insn_after (gen_align_4 (), scan);
scan = emit_label_after (minipool_vector_label, scan);
for (mp = minipool_vector_head; mp != NULL; mp = nmp)
{
if (mp->refcount > 0)
{
if (rtl_dump_file)
{
fprintf (rtl_dump_file,
";; Offset %u, min %ld, max %ld ",
(unsigned) mp->offset, (unsigned long) mp->min_address,
(unsigned long) mp->max_address);
arm_print_value (rtl_dump_file, mp->value);
fputc ('\n', rtl_dump_file);
}
switch (mp->fix_size)
{
#ifdef HAVE_consttable_1
case 1:
scan = emit_insn_after (gen_consttable_1 (mp->value), scan);
break;
#endif
#ifdef HAVE_consttable_2
case 2:
scan = emit_insn_after (gen_consttable_2 (mp->value), scan);
break;
#endif
#ifdef HAVE_consttable_4
case 4:
scan = emit_insn_after (gen_consttable_4 (mp->value), scan);
break;
#endif
#ifdef HAVE_consttable_8
case 8:
scan = emit_insn_after (gen_consttable_8 (mp->value), scan);
break;
#endif
default:
abort ();
break;
}
}
nmp = mp->next;
free (mp);
}
minipool_vector_head = minipool_vector_tail = NULL;
scan = emit_insn_after (gen_consttable_end (), scan);
scan = emit_barrier_after (scan);
}
static int
arm_barrier_cost (insn)
rtx insn;
{
int base_cost = 50;
rtx next = next_nonnote_insn (insn);
if (next != NULL && GET_CODE (next) == CODE_LABEL)
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;
}
}
static Mfix *
create_fix_barrier (fix, max_address)
Mfix * fix;
HOST_WIDE_INT max_address;
{
HOST_WIDE_INT count = 0;
rtx barrier;
rtx from = fix->insn;
rtx selected = from;
int selected_cost;
HOST_WIDE_INT selected_address;
Mfix * new_fix;
HOST_WIDE_INT max_count = max_address - fix->address;
rtx label = gen_label_rtx ();
selected_cost = arm_barrier_cost (from);
selected_address = fix->address;
while (from && count < max_count)
{
rtx tmp;
int new_cost;
if (GET_CODE (from) == BARRIER)
abort ();
count += get_attr_length (from);
tmp = is_jump_table (from);
if (tmp != NULL)
{
count += get_jump_table_size (tmp);
new_cost = arm_barrier_cost (from);
if (count < max_count && new_cost <= selected_cost)
{
selected = tmp;
selected_cost = new_cost;
selected_address = fix->address + count;
}
from = NEXT_INSN (tmp);
continue;
}
new_cost = arm_barrier_cost (from);
if (count < max_count && new_cost <= selected_cost)
{
selected = from;
selected_cost = new_cost;
selected_address = fix->address + count;
}
from = NEXT_INSN (from);
}
from = emit_jump_insn_after (gen_jump (label), selected);
JUMP_LABEL (from) = label;
barrier = emit_barrier_after (from);
emit_label_after (label, barrier);
new_fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* new_fix));
new_fix->insn = barrier;
new_fix->address = selected_address;
new_fix->next = fix->next;
fix->next = new_fix;
return new_fix;
}
static void
push_minipool_barrier (insn, address)
rtx insn;
HOST_WIDE_INT address;
{
Mfix * fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* fix));
fix->insn = insn;
fix->address = address;
fix->next = NULL;
if (minipool_fix_head != NULL)
minipool_fix_tail->next = fix;
else
minipool_fix_head = fix;
minipool_fix_tail = fix;
}
static void
push_minipool_fix (insn, address, loc, mode, value)
rtx insn;
HOST_WIDE_INT address;
rtx * loc;
enum machine_mode mode;
rtx value;
{
Mfix * fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* fix));
#ifdef AOF_ASSEMBLER
if (flag_pic && GET_CODE (value) == SYMBOL_REF)
value = aof_pic_entry (value);
#endif
fix->insn = insn;
fix->address = address;
fix->loc = loc;
fix->mode = mode;
fix->fix_size = MINIPOOL_FIX_SIZE (mode);
fix->value = value;
fix->forwards = get_attr_pool_range (insn);
fix->backwards = get_attr_neg_pool_range (insn);
fix->minipool = NULL;
if (fix->forwards == 0 && fix->backwards == 0)
abort ();
if (rtl_dump_file)
{
fprintf (rtl_dump_file,
";; %smode fixup for i%d; addr %lu, range (%ld,%ld): ",
GET_MODE_NAME (mode),
INSN_UID (insn), (unsigned long) address,
-1 * (long)fix->backwards, (long)fix->forwards);
arm_print_value (rtl_dump_file, fix->value);
fprintf (rtl_dump_file, "\n");
}
fix->next = NULL;
if (minipool_fix_head != NULL)
minipool_fix_tail->next = fix;
else
minipool_fix_head = fix;
minipool_fix_tail = fix;
}
static void
note_invalid_constants (insn, address)
rtx insn;
HOST_WIDE_INT address;
{
int opno;
extract_insn (insn);
if (!constrain_operands (1))
fatal_insn_not_found (insn);
preprocess_constraints ();
for (opno = 0; opno < recog_data.n_operands; opno++)
{
if (recog_data.operand_type[opno] != OP_IN)
continue;
if (recog_op_alt[opno][which_alternative].memory_ok)
{
rtx op = recog_data.operand[opno];
if (CONSTANT_P (op))
push_minipool_fix (insn, address, recog_data.operand_loc[opno],
recog_data.operand_mode[opno], op);
#if 0
#ifndef AOF_ASSEMBLER
else if (GET_CODE (op) == UNSPEC && XINT (op, 1) == UNSPEC_PIC_SYM)
push_minipool_fix (insn, address, recog_data.operand_loc[opno],
recog_data.operand_mode[opno],
XVECEXP (op, 0, 0));
#endif
#endif
else if (GET_CODE (op) == MEM
&& GET_CODE (XEXP (op, 0)) == SYMBOL_REF
&& CONSTANT_POOL_ADDRESS_P (XEXP (op, 0)))
push_minipool_fix (insn, address, recog_data.operand_loc[opno],
recog_data.operand_mode[opno],
get_pool_constant (XEXP (op, 0)));
}
}
}
void
arm_reorg (first)
rtx first;
{
rtx insn;
HOST_WIDE_INT address = 0;
Mfix * fix;
minipool_fix_head = minipool_fix_tail = NULL;
if (GET_CODE (first) != NOTE)
abort ();
for (insn = next_nonnote_insn (first); insn; insn = next_nonnote_insn (insn))
{
if (GET_CODE (insn) == BARRIER)
push_minipool_barrier (insn, address);
else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN
|| GET_CODE (insn) == JUMP_INSN)
{
rtx table;
note_invalid_constants (insn, address);
address += get_attr_length (insn);
if ((table = is_jump_table (insn)) != NULL)
{
address += get_jump_table_size (table);
insn = table;
}
}
}
fix = minipool_fix_head;
while (fix)
{
Mfix * ftmp;
Mfix * fdel;
Mfix * last_added_fix;
Mfix * last_barrier = NULL;
Mfix * this_fix;
while (fix && GET_CODE (fix->insn) == BARRIER)
fix = fix->next;
if (fix == NULL)
break;
last_added_fix = NULL;
for (ftmp = fix; ftmp; ftmp = ftmp->next)
{
if (GET_CODE (ftmp->insn) == BARRIER)
{
if (ftmp->address >= minipool_vector_head->max_address)
break;
last_barrier = ftmp;
}
else if ((ftmp->minipool = add_minipool_forward_ref (ftmp)) == NULL)
break;
last_added_fix = ftmp;
}
if (last_barrier != NULL)
{
for (fdel = last_barrier->next;
fdel && fdel != ftmp;
fdel = fdel->next)
{
fdel->minipool->refcount--;
fdel->minipool = NULL;
}
ftmp = last_barrier;
}
else
{
HOST_WIDE_INT max_address;
if (ftmp == NULL)
abort ();
max_address = minipool_vector_head->max_address;
if (ftmp->address < max_address)
max_address = ftmp->address;
last_barrier = create_fix_barrier (last_added_fix, max_address);
}
assign_minipool_offsets (last_barrier);
while (ftmp)
{
if (GET_CODE (ftmp->insn) != BARRIER
&& ((ftmp->minipool = add_minipool_backward_ref (ftmp))
== NULL))
break;
ftmp = ftmp->next;
}
for (this_fix = fix; this_fix && ftmp != this_fix;
this_fix = this_fix->next)
if (GET_CODE (this_fix->insn) != BARRIER)
{
rtx addr
= plus_constant (gen_rtx_LABEL_REF (VOIDmode,
minipool_vector_label),
this_fix->minipool->offset);
*this_fix->loc = gen_rtx_MEM (this_fix->mode, addr);
}
dump_minipool (last_barrier->insn);
fix = ftmp;
}
after_arm_reorg = 1;
obstack_free (&minipool_obstack, minipool_startobj);
}
const char *
fp_immediate_constant (x)
rtx x;
{
REAL_VALUE_TYPE r;
int i;
if (!fpa_consts_inited)
init_fpa_table ();
REAL_VALUE_FROM_CONST_DOUBLE (r, x);
for (i = 0; i < 8; i++)
if (REAL_VALUES_EQUAL (r, values_fpa[i]))
return strings_fpa[i];
abort ();
}
static const char *
fp_const_from_val (r)
REAL_VALUE_TYPE * r;
{
int i;
if (!fpa_consts_inited)
init_fpa_table ();
for (i = 0; i < 8; i++)
if (REAL_VALUES_EQUAL (*r, values_fpa[i]))
return strings_fpa[i];
abort ();
}
static void
print_multi_reg (stream, instr, reg, mask)
FILE * stream;
const char * instr;
int reg;
int mask;
{
int i;
int not_first = FALSE;
fputc ('\t', stream);
asm_fprintf (stream, instr, reg);
fputs (", {", stream);
for (i = 0; i <= LAST_ARM_REGNUM; i++)
if (mask & (1 << i))
{
if (not_first)
fprintf (stream, ", ");
asm_fprintf (stream, "%r", i);
not_first = TRUE;
}
fprintf (stream, "}%s\n", TARGET_APCS_32 ? "" : "^");
}
const char *
output_call (operands)
rtx * operands;
{
if (REGNO (operands[0]) == LR_REGNUM)
{
operands[0] = gen_rtx_REG (SImode, IP_REGNUM);
output_asm_insn ("mov%?\t%0, %|lr", operands);
}
output_asm_insn ("mov%?\t%|lr, %|pc", operands);
if (TARGET_INTERWORK)
output_asm_insn ("bx%?\t%0", operands);
else
output_asm_insn ("mov%?\t%|pc, %0", operands);
return "";
}
static int
eliminate_lr2ip (x)
rtx * x;
{
int something_changed = 0;
rtx x0 = * x;
int code = GET_CODE (x0);
int i, j;
const char * fmt;
switch (code)
{
case REG:
if (REGNO (x0) == LR_REGNUM)
{
*x = gen_rtx_REG (SImode, IP_REGNUM);
return 1;
}
return 0;
default:
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
if (fmt[i] == 'e')
something_changed |= eliminate_lr2ip (&XEXP (x0, i));
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x0, i); j++)
something_changed |= eliminate_lr2ip (&XVECEXP (x0, i, j));
return something_changed;
}
}
const char *
output_call_mem (operands)
rtx * operands;
{
operands[0] = copy_rtx (operands[0]);
if (eliminate_lr2ip (&operands[0]))
output_asm_insn ("mov%?\t%|ip, %|lr", operands);
if (TARGET_INTERWORK)
{
output_asm_insn ("ldr%?\t%|ip, %0", operands);
output_asm_insn ("mov%?\t%|lr, %|pc", operands);
output_asm_insn ("bx%?\t%|ip", operands);
}
else
{
output_asm_insn ("mov%?\t%|lr, %|pc", operands);
output_asm_insn ("ldr%?\t%|pc, %0", operands);
}
return "";
}
const char *
output_mov_long_double_fpu_from_arm (operands)
rtx * operands;
{
int arm_reg0 = REGNO (operands[1]);
rtx ops[3];
if (arm_reg0 == IP_REGNUM)
abort ();
ops[0] = gen_rtx_REG (SImode, arm_reg0);
ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0);
ops[2] = gen_rtx_REG (SImode, 2 + arm_reg0);
output_asm_insn ("stm%?fd\t%|sp!, {%0, %1, %2}", ops);
output_asm_insn ("ldf%?e\t%0, [%|sp], #12", operands);
return "";
}
const char *
output_mov_long_double_arm_from_fpu (operands)
rtx * operands;
{
int arm_reg0 = REGNO (operands[0]);
rtx ops[3];
if (arm_reg0 == IP_REGNUM)
abort ();
ops[0] = gen_rtx_REG (SImode, arm_reg0);
ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0);
ops[2] = gen_rtx_REG (SImode, 2 + arm_reg0);
output_asm_insn ("stf%?e\t%1, [%|sp, #-12]!", operands);
output_asm_insn ("ldm%?fd\t%|sp!, {%0, %1, %2}", ops);
return "";
}
const char *
output_mov_long_double_arm_from_arm (operands)
rtx * operands;
{
int dest_start = REGNO (operands[0]);
int src_start = REGNO (operands[1]);
rtx ops[2];
int i;
if (dest_start < src_start)
{
for (i = 0; i < 3; i++)
{
ops[0] = gen_rtx_REG (SImode, dest_start + i);
ops[1] = gen_rtx_REG (SImode, src_start + i);
output_asm_insn ("mov%?\t%0, %1", ops);
}
}
else
{
for (i = 2; i >= 0; i--)
{
ops[0] = gen_rtx_REG (SImode, dest_start + i);
ops[1] = gen_rtx_REG (SImode, src_start + i);
output_asm_insn ("mov%?\t%0, %1", ops);
}
}
return "";
}
const char *
output_mov_double_fpu_from_arm (operands)
rtx * operands;
{
int arm_reg0 = REGNO (operands[1]);
rtx ops[2];
if (arm_reg0 == IP_REGNUM)
abort ();
ops[0] = gen_rtx_REG (SImode, arm_reg0);
ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0);
output_asm_insn ("stm%?fd\t%|sp!, {%0, %1}", ops);
output_asm_insn ("ldf%?d\t%0, [%|sp], #8", operands);
return "";
}
const char *
output_mov_double_arm_from_fpu (operands)
rtx * operands;
{
int arm_reg0 = REGNO (operands[0]);
rtx ops[2];
if (arm_reg0 == IP_REGNUM)
abort ();
ops[0] = gen_rtx_REG (SImode, arm_reg0);
ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0);
output_asm_insn ("stf%?d\t%1, [%|sp, #-8]!", operands);
output_asm_insn ("ldm%?fd\t%|sp!, {%0, %1}", ops);
return "";
}
const char *
output_move_double (operands)
rtx * operands;
{
enum rtx_code code0 = GET_CODE (operands[0]);
enum rtx_code code1 = GET_CODE (operands[1]);
rtx otherops[3];
if (code0 == REG)
{
int reg0 = REGNO (operands[0]);
otherops[0] = gen_rtx_REG (SImode, 1 + reg0);
if (code1 == REG)
{
int reg1 = REGNO (operands[1]);
if (reg1 == IP_REGNUM)
abort ();
if (reg1 == reg0 + (WORDS_BIG_ENDIAN ? -1 : 1))
output_asm_insn ("mov%?\t%Q0, %Q1\n\tmov%?\t%R0, %R1", operands);
else
output_asm_insn ("mov%?\t%R0, %R1\n\tmov%?\t%Q0, %Q1", operands);
}
else if (code1 == CONST_DOUBLE)
{
if (GET_MODE (operands[1]) == DFmode)
{
REAL_VALUE_TYPE r;
long l[2];
REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
REAL_VALUE_TO_TARGET_DOUBLE (r, l);
otherops[1] = GEN_INT (l[1]);
operands[1] = GEN_INT (l[0]);
}
else if (GET_MODE (operands[1]) != VOIDmode)
abort ();
else if (WORDS_BIG_ENDIAN)
{
otherops[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
operands[1] = GEN_INT (CONST_DOUBLE_HIGH (operands[1]));
}
else
{
otherops[1] = GEN_INT (CONST_DOUBLE_HIGH (operands[1]));
operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
}
output_mov_immediate (operands);
output_mov_immediate (otherops);
}
else if (code1 == CONST_INT)
{
#if HOST_BITS_PER_WIDE_INT > 32
if (WORDS_BIG_ENDIAN)
{
otherops[1] = GEN_INT (ARM_SIGN_EXTEND (INTVAL (operands[1])));
operands[1] = GEN_INT (INTVAL (operands[1]) >> 32);
}
else
{
otherops[1] = GEN_INT (INTVAL (operands[1]) >> 32);
operands[1] = GEN_INT (ARM_SIGN_EXTEND (INTVAL (operands[1])));
}
#else
if (WORDS_BIG_ENDIAN)
{
otherops[1] = operands[1];
operands[1] = (INTVAL (operands[1]) < 0
? constm1_rtx : const0_rtx);
}
else
otherops[1] = INTVAL (operands[1]) < 0 ? constm1_rtx : const0_rtx;
#endif
output_mov_immediate (otherops);
output_mov_immediate (operands);
}
else if (code1 == MEM)
{
switch (GET_CODE (XEXP (operands[1], 0)))
{
case REG:
output_asm_insn ("ldm%?ia\t%m1, %M0", operands);
break;
case PRE_INC:
abort ();
break;
case PRE_DEC:
output_asm_insn ("ldm%?db\t%m1!, %M0", operands);
break;
case POST_INC:
output_asm_insn ("ldm%?ia\t%m1!, %M0", operands);
break;
case POST_DEC:
abort ();
break;
case LABEL_REF:
case CONST:
output_asm_insn ("adr%?\t%0, %1", operands);
output_asm_insn ("ldm%?ia\t%0, %M0", operands);
break;
default:
if (arm_add_operand (XEXP (XEXP (operands[1], 0), 1),
GET_MODE (XEXP (XEXP (operands[1], 0), 1))))
{
otherops[0] = operands[0];
otherops[1] = XEXP (XEXP (operands[1], 0), 0);
otherops[2] = XEXP (XEXP (operands[1], 0), 1);
if (GET_CODE (XEXP (operands[1], 0)) == PLUS)
{
if (GET_CODE (otherops[2]) == CONST_INT)
{
switch (INTVAL (otherops[2]))
{
case -8:
output_asm_insn ("ldm%?db\t%1, %M0", otherops);
return "";
case -4:
output_asm_insn ("ldm%?da\t%1, %M0", otherops);
return "";
case 4:
output_asm_insn ("ldm%?ib\t%1, %M0", otherops);
return "";
}
if (!(const_ok_for_arm (INTVAL (otherops[2]))))
output_asm_insn ("sub%?\t%0, %1, #%n2", otherops);
else
output_asm_insn ("add%?\t%0, %1, %2", otherops);
}
else
output_asm_insn ("add%?\t%0, %1, %2", otherops);
}
else
output_asm_insn ("sub%?\t%0, %1, %2", otherops);
return "ldm%?ia\t%0, %M0";
}
else
{
otherops[1] = adjust_address (operands[1], VOIDmode, 4);
if (reg_mentioned_p (operands[0], operands[1]))
{
output_asm_insn ("ldr%?\t%0, %1", otherops);
output_asm_insn ("ldr%?\t%0, %1", operands);
}
else
{
output_asm_insn ("ldr%?\t%0, %1", operands);
output_asm_insn ("ldr%?\t%0, %1", otherops);
}
}
}
}
else
abort ();
}
else if (code0 == MEM && code1 == REG)
{
if (REGNO (operands[1]) == IP_REGNUM)
abort ();
switch (GET_CODE (XEXP (operands[0], 0)))
{
case REG:
output_asm_insn ("stm%?ia\t%m0, %M1", operands);
break;
case PRE_INC:
abort ();
break;
case PRE_DEC:
output_asm_insn ("stm%?db\t%m0!, %M1", operands);
break;
case POST_INC:
output_asm_insn ("stm%?ia\t%m0!, %M1", operands);
break;
case POST_DEC:
abort ();
break;
case PLUS:
if (GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == CONST_INT)
{
switch (INTVAL (XEXP (XEXP (operands[0], 0), 1)))
{
case -8:
output_asm_insn ("stm%?db\t%m0, %M1", operands);
return "";
case -4:
output_asm_insn ("stm%?da\t%m0, %M1", operands);
return "";
case 4:
output_asm_insn ("stm%?ib\t%m0, %M1", operands);
return "";
}
}
default:
otherops[0] = adjust_address (operands[0], VOIDmode, 4);
otherops[1] = gen_rtx_REG (SImode, 1 + REGNO (operands[1]));
output_asm_insn ("str%?\t%1, %0", operands);
output_asm_insn ("str%?\t%1, %0", otherops);
}
}
else
abort ();
return "";
}
const char *
output_mov_immediate (operands)
rtx * operands;
{
HOST_WIDE_INT n = INTVAL (operands[1]);
if (const_ok_for_arm (n))
output_asm_insn ("mov%?\t%0, %1", operands);
else if (const_ok_for_arm (~n))
{
operands[1] = GEN_INT (~n);
output_asm_insn ("mvn%?\t%0, %1", operands);
}
else
{
int n_ones = 0;
int i;
for (i = 0; i < 32; i ++)
if (n & 1 << i)
n_ones ++;
if (n_ones > 16)
output_multi_immediate (operands, "mvn%?\t%0, %1", "bic%?\t%0, %0, %1", 1, ~ n);
else
output_multi_immediate (operands, "mov%?\t%0, %1", "orr%?\t%0, %0, %1", 1, n);
}
return "";
}
const char *
output_add_immediate (operands)
rtx * operands;
{
HOST_WIDE_INT n = INTVAL (operands[2]);
if (n != 0 || REGNO (operands[0]) != REGNO (operands[1]))
{
if (n < 0)
output_multi_immediate (operands,
"sub%?\t%0, %1, %2", "sub%?\t%0, %0, %2", 2,
-n);
else
output_multi_immediate (operands,
"add%?\t%0, %1, %2", "add%?\t%0, %0, %2", 2,
n);
}
return "";
}
static const char *
output_multi_immediate (operands, instr1, instr2, immed_op, n)
rtx * operands;
const char * instr1;
const char * instr2;
int immed_op;
HOST_WIDE_INT n;
{
#if HOST_BITS_PER_WIDE_INT > 32
n &= 0xffffffff;
#endif
if (n == 0)
{
operands[immed_op] = const0_rtx;
output_asm_insn (instr1, operands);
}
else
{
int i;
const char * instr = instr1;
for (i = 0; i < 32; i += 2)
{
if (n & (3 << i))
{
operands[immed_op] = GEN_INT (n & (255 << i));
output_asm_insn (instr, operands);
instr = instr2;
i += 6;
}
}
}
return "";
}
const char *
arithmetic_instr (op, shift_first_arg)
rtx op;
int shift_first_arg;
{
switch (GET_CODE (op))
{
case PLUS:
return "add";
case MINUS:
return shift_first_arg ? "rsb" : "sub";
case IOR:
return "orr";
case XOR:
return "eor";
case AND:
return "and";
default:
abort ();
}
}
static const char *
shift_op (op, amountp)
rtx op;
HOST_WIDE_INT *amountp;
{
const char * mnem;
enum rtx_code code = GET_CODE (op);
if (GET_CODE (XEXP (op, 1)) == REG || GET_CODE (XEXP (op, 1)) == SUBREG)
*amountp = -1;
else if (GET_CODE (XEXP (op, 1)) == CONST_INT)
*amountp = INTVAL (XEXP (op, 1));
else
abort ();
switch (code)
{
case ASHIFT:
mnem = "asl";
break;
case ASHIFTRT:
mnem = "asr";
break;
case LSHIFTRT:
mnem = "lsr";
break;
case ROTATERT:
mnem = "ror";
break;
case MULT:
if (*amountp != -1)
*amountp = int_log2 (*amountp);
else
abort ();
return "asl";
default:
abort ();
}
if (*amountp != -1)
{
if (code == ROTATERT)
*amountp &= 31;
else if (*amountp != (*amountp & 31))
{
if (code == ASHIFT)
mnem = "lsr";
*amountp = 32;
}
if (*amountp == 0)
return NULL;
}
return mnem;
}
static HOST_WIDE_INT
int_log2 (power)
HOST_WIDE_INT power;
{
HOST_WIDE_INT shift = 0;
while ((((HOST_WIDE_INT) 1 << shift) & power) == 0)
{
if (shift > 31)
abort ();
shift ++;
}
return shift;
}
#define MAX_ASCII_LEN 51
void
output_ascii_pseudo_op (stream, p, len)
FILE * stream;
const unsigned char * p;
int len;
{
int i;
int len_so_far = 0;
fputs ("\t.ascii\t\"", stream);
for (i = 0; i < len; i++)
{
int c = p[i];
if (len_so_far >= MAX_ASCII_LEN)
{
fputs ("\"\n\t.ascii\t\"", stream);
len_so_far = 0;
}
switch (c)
{
case TARGET_TAB:
fputs ("\\t", stream);
len_so_far += 2;
break;
case TARGET_FF:
fputs ("\\f", stream);
len_so_far += 2;
break;
case TARGET_BS:
fputs ("\\b", stream);
len_so_far += 2;
break;
case TARGET_CR:
fputs ("\\r", stream);
len_so_far += 2;
break;
case TARGET_NEWLINE:
fputs ("\\n", stream);
c = p [i + 1];
if ((c >= ' ' && c <= '~')
|| c == TARGET_TAB)
len_so_far = MAX_ASCII_LEN;
else
len_so_far += 2;
break;
case '\"':
case '\\':
putc ('\\', stream);
len_so_far++;
default:
if (c >= ' ' && c <= '~')
{
putc (c, stream);
len_so_far++;
}
else
{
fprintf (stream, "\\%03o", c);
len_so_far += 4;
}
break;
}
}
fputs ("\"\n", stream);
}
static unsigned long
arm_compute_save_reg0_reg12_mask ()
{
unsigned long func_type = arm_current_func_type ();
unsigned int 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 (regs_ever_live[reg]
|| (! current_function_is_leaf && call_used_regs [reg]))
save_reg_mask |= (1 << reg);
}
else
{
for (reg = 0; reg <= 10; reg++)
if (regs_ever_live[reg] && ! call_used_regs [reg])
save_reg_mask |= (1 << reg);
if (! TARGET_APCS_FRAME
&& ! frame_pointer_needed
&& regs_ever_live[HARD_FRAME_POINTER_REGNUM]
&& ! call_used_regs[HARD_FRAME_POINTER_REGNUM])
save_reg_mask |= 1 << HARD_FRAME_POINTER_REGNUM;
if (flag_pic
&& ! TARGET_SINGLE_PIC_BASE
&& regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
save_reg_mask |= 1 << PIC_OFFSET_TABLE_REGNUM;
}
return save_reg_mask;
}
static unsigned long
arm_compute_save_reg_mask ()
{
unsigned int save_reg_mask = 0;
unsigned long func_type = arm_current_func_type ();
if (IS_NAKED (func_type))
return 0;
if (frame_pointer_needed)
save_reg_mask |=
(1 << ARM_HARD_FRAME_POINTER_REGNUM)
| (1 << IP_REGNUM)
| (1 << LR_REGNUM)
| (1 << PC_REGNUM);
if (IS_VOLATILE (func_type))
return save_reg_mask;
save_reg_mask |= arm_compute_save_reg0_reg12_mask ();
if (regs_ever_live [LR_REGNUM]
|| (save_reg_mask
&& optimize_size
&& ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL))
save_reg_mask |= 1 << LR_REGNUM;
if (cfun->machine->lr_save_eliminated)
save_reg_mask &= ~ (1 << LR_REGNUM);
return save_reg_mask;
}
const char *
output_return_instruction (operand, really_return, reverse)
rtx operand;
int really_return;
int reverse;
{
char conditional[10];
char instr[100];
int reg;
unsigned long live_regs_mask;
unsigned long func_type;
func_type = arm_current_func_type ();
if (IS_NAKED (func_type))
return "";
if (IS_VOLATILE (func_type) && TARGET_ABORT_NORETURN)
{
if (really_return)
{
rtx ops[2];
ops[0] = operand;
ops[1] = gen_rtx_SYMBOL_REF (Pmode, NEED_PLT_RELOC ? "abort(PLT)"
: "abort");
assemble_external_libcall (ops[1]);
output_asm_insn (reverse ? "bl%D0\t%a1" : "bl%d0\t%a1", ops);
}
return "";
}
if (current_function_calls_alloca && !really_return)
abort ();
sprintf (conditional, "%%?%%%c0", reverse ? 'D' : 'd');
return_used_this_function = 1;
live_regs_mask = arm_compute_save_reg_mask ();
if (live_regs_mask)
{
const char * return_reg;
if (really_return
&& ! TARGET_INTERWORK)
return_reg = reg_names[PC_REGNUM];
else
return_reg = reg_names[LR_REGNUM];
if ((live_regs_mask & (1 << IP_REGNUM)) == (1 << IP_REGNUM))
if (! IS_INTERRUPT (func_type))
{
live_regs_mask &= ~ (1 << IP_REGNUM);
live_regs_mask |= (1 << SP_REGNUM);
}
for (reg = 0; reg <= LAST_ARM_REGNUM; reg++)
{
if (live_regs_mask == (unsigned int)(1 << reg))
break;
}
if (reg <= LAST_ARM_REGNUM
&& (reg != LR_REGNUM
|| ! really_return
|| (TARGET_APCS_32 && ! IS_INTERRUPT (func_type))))
{
sprintf (instr, "ldr%s\t%%|%s, [%%|sp], #4", conditional,
(reg == LR_REGNUM) ? return_reg : reg_names[reg]);
}
else
{
char *p;
int first = 1;
if (frame_pointer_needed)
sprintf (instr, "ldm%sea\t%%|fp, {", conditional);
else if (live_regs_mask & (1 << SP_REGNUM))
sprintf (instr, "ldm%sfd\t%%|sp, {", conditional);
else
sprintf (instr, "ldm%sfd\t%%|sp!, {", conditional);
p = instr + strlen (instr);
for (reg = 0; reg <= SP_REGNUM; reg++)
if (live_regs_mask & (1 << reg))
{
int l = strlen (reg_names[reg]);
if (first)
first = 0;
else
{
memcpy (p, ", ", 2);
p += 2;
}
memcpy (p, "%|", 2);
memcpy (p + 2, reg_names[reg], l);
p += l + 2;
}
if (live_regs_mask & (1 << LR_REGNUM))
{
int l = strlen (return_reg);
if (! first)
{
memcpy (p, ", ", 2);
p += 2;
}
memcpy (p, "%|", 2);
memcpy (p + 2, return_reg, l);
strcpy (p + 2 + l, ((TARGET_APCS_32
&& !IS_INTERRUPT (func_type))
|| !really_return)
? "}" : "}^");
}
else
strcpy (p, "}");
}
output_asm_insn (instr, & operand);
if (really_return
&& func_type != ARM_FT_INTERWORKED
&& (live_regs_mask & (1 << LR_REGNUM)) != 0)
{
really_return = 0;
}
}
if (really_return)
{
switch ((int) ARM_FUNC_TYPE (func_type))
{
case ARM_FT_ISR:
case ARM_FT_FIQ:
sprintf (instr, "sub%ss\t%%|pc, %%|lr, #4", conditional);
break;
case ARM_FT_INTERWORKED:
sprintf (instr, "bx%s\t%%|lr", conditional);
break;
case ARM_FT_EXCEPTION:
sprintf (instr, "mov%ss\t%%|pc, %%|lr", conditional);
break;
default:
if ((insn_flags & FL_ARCH5) != 0 && TARGET_APCS_32)
sprintf (instr, "bx%s\t%%|lr", conditional);
else
sprintf (instr, "mov%s%s\t%%|pc, %%|lr",
conditional, TARGET_APCS_32 ? "" : "s");
break;
}
output_asm_insn (instr, & operand);
}
return "";
}
void
arm_poke_function_name (stream, name)
FILE * stream;
const char * name;
{
unsigned long alignlength;
unsigned long length;
rtx x;
length = strlen (name) + 1;
alignlength = ROUND_UP (length);
ASM_OUTPUT_ASCII (stream, name, length);
ASM_OUTPUT_ALIGN (stream, 2);
x = GEN_INT ((unsigned HOST_WIDE_INT) 0xff000000 + alignlength);
assemble_aligned_integer (UNITS_PER_WORD, x);
}
static void
arm_output_function_prologue (f, frame_size)
FILE * f;
HOST_WIDE_INT frame_size;
{
unsigned long func_type;
if (!TARGET_ARM)
{
thumb_output_function_prologue (f, frame_size);
return;
}
if (arm_ccfsm_state || arm_target_insn)
abort ();
func_type = arm_current_func_type ();
switch ((int) ARM_FUNC_TYPE (func_type))
{
default:
case ARM_FT_NORMAL:
break;
case ARM_FT_INTERWORKED:
asm_fprintf (f, "\t%@ Function supports interworking.\n");
break;
case ARM_FT_EXCEPTION_HANDLER:
asm_fprintf (f, "\t%@ C++ Exception Handler.\n");
break;
case ARM_FT_ISR:
asm_fprintf (f, "\t%@ Interrupt Service Routine.\n");
break;
case ARM_FT_FIQ:
asm_fprintf (f, "\t%@ Fast Interrupt Service Routine.\n");
break;
case ARM_FT_EXCEPTION:
asm_fprintf (f, "\t%@ ARM Exception Handler.\n");
break;
}
if (IS_NAKED (func_type))
asm_fprintf (f, "\t%@ Naked Function: prologue and epilogue provided by programmer.\n");
if (IS_VOLATILE (func_type))
asm_fprintf (f, "\t%@ Volatile: function does not return.\n");
if (IS_NESTED (func_type))
asm_fprintf (f, "\t%@ Nested: function declared inside another function.\n");
asm_fprintf (f, "\t%@ args = %d, pretend = %d, frame = %d\n",
current_function_args_size,
current_function_pretend_args_size, frame_size);
asm_fprintf (f, "\t%@ frame_needed = %d, uses_anonymous_args = %d\n",
frame_pointer_needed,
cfun->machine->uses_anonymous_args);
if (cfun->machine->lr_save_eliminated)
asm_fprintf (f, "\t%@ link register save eliminated.\n");
#ifdef AOF_ASSEMBLER
if (flag_pic)
asm_fprintf (f, "\tmov\t%r, %r\n", IP_REGNUM, PIC_OFFSET_TABLE_REGNUM);
#endif
return_used_this_function = 0;
}
const char *
arm_output_epilogue (really_return)
int really_return;
{
int reg;
unsigned long saved_regs_mask;
unsigned long func_type;
int floats_offset = 0;
rtx operands[3];
int frame_size = arm_get_frame_size ();
FILE * f = asm_out_file;
rtx eh_ofs = cfun->machine->eh_epilogue_sp_ofs;
if (use_return_insn (FALSE) && return_used_this_function)
return "";
func_type = arm_current_func_type ();
if (IS_NAKED (func_type))
return "";
if (IS_VOLATILE (func_type) && TARGET_ABORT_NORETURN)
{
rtx op;
op = gen_rtx_SYMBOL_REF (Pmode, NEED_PLT_RELOC ? "abort(PLT)" : "abort");
assemble_external_libcall (op);
output_asm_insn ("bl\t%a0", &op);
return "";
}
if (ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER
&& ! really_return)
abort ();
saved_regs_mask = arm_compute_save_reg_mask ();
for (reg = 0; reg <= LAST_ARM_REGNUM; reg ++)
if (saved_regs_mask & (1 << reg))
floats_offset += 4;
if (frame_pointer_needed)
{
int vfp_offset = 4;
if (arm_fpu_arch == FP_SOFT2)
{
for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg--)
if (regs_ever_live[reg] && !call_used_regs[reg])
{
floats_offset += 12;
asm_fprintf (f, "\tldfe\t%r, [%r, #-%d]\n",
reg, FP_REGNUM, floats_offset - vfp_offset);
}
}
else
{
int start_reg = LAST_ARM_FP_REGNUM;
for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg--)
{
if (regs_ever_live[reg] && !call_used_regs[reg])
{
floats_offset += 12;
if (start_reg - reg == 3)
{
asm_fprintf (f, "\tlfm\t%r, 4, [%r, #-%d]\n",
reg, FP_REGNUM, floats_offset - vfp_offset);
start_reg = reg - 1;
}
}
else
{
if (reg != start_reg)
asm_fprintf (f, "\tlfm\t%r, %d, [%r, #-%d]\n",
reg + 1, start_reg - reg,
FP_REGNUM, floats_offset - vfp_offset);
start_reg = reg - 1;
}
}
if (reg != start_reg)
asm_fprintf (f, "\tlfm\t%r, %d, [%r, #-%d]\n",
reg + 1, start_reg - reg,
FP_REGNUM, floats_offset - vfp_offset);
}
if ((saved_regs_mask & (1 << IP_REGNUM)) == 0)
abort ();
saved_regs_mask &= ~ (1 << IP_REGNUM);
saved_regs_mask |= (1 << SP_REGNUM);
if (really_return && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL)
saved_regs_mask &= ~ (1 << LR_REGNUM);
else
saved_regs_mask &= ~ (1 << PC_REGNUM);
print_multi_reg (f, "ldmea\t%r", FP_REGNUM, saved_regs_mask);
if (IS_INTERRUPT (func_type))
print_multi_reg (f, "ldmfd\t%r", SP_REGNUM, 1 << IP_REGNUM);
}
else
{
if (frame_size + current_function_outgoing_args_size != 0)
{
operands[0] = operands[1] = stack_pointer_rtx;
operands[2] = GEN_INT (frame_size
+ current_function_outgoing_args_size);
output_add_immediate (operands);
}
if (arm_fpu_arch == FP_SOFT2)
{
for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg++)
if (regs_ever_live[reg] && !call_used_regs[reg])
asm_fprintf (f, "\tldfe\t%r, [%r], #12\n",
reg, SP_REGNUM);
}
else
{
int start_reg = FIRST_ARM_FP_REGNUM;
for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg++)
{
if (regs_ever_live[reg] && !call_used_regs[reg])
{
if (reg - start_reg == 3)
{
asm_fprintf (f, "\tlfmfd\t%r, 4, [%r]!\n",
start_reg, SP_REGNUM);
start_reg = reg + 1;
}
}
else
{
if (reg != start_reg)
asm_fprintf (f, "\tlfmfd\t%r, %d, [%r]!\n",
start_reg, reg - start_reg,
SP_REGNUM);
start_reg = reg + 1;
}
}
if (reg != start_reg)
asm_fprintf (f, "\tlfmfd\t%r, %d, [%r]!\n",
start_reg, reg - start_reg, SP_REGNUM);
}
if (ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL
&& really_return
&& current_function_pretend_args_size == 0
&& saved_regs_mask & (1 << LR_REGNUM))
{
saved_regs_mask &= ~ (1 << LR_REGNUM);
saved_regs_mask |= (1 << PC_REGNUM);
}
if (saved_regs_mask == (1 << LR_REGNUM))
{
if (eh_ofs)
asm_fprintf (f, "\tadd\t%r, %r, #4\n", SP_REGNUM, SP_REGNUM);
else
asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM, SP_REGNUM);
}
else if (saved_regs_mask)
{
if (saved_regs_mask & (1 << SP_REGNUM))
print_multi_reg (f, "ldmfd\t%r", SP_REGNUM, saved_regs_mask);
else
print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, saved_regs_mask);
}
if (current_function_pretend_args_size)
{
operands[0] = operands[1] = stack_pointer_rtx;
operands[2] = GEN_INT (current_function_pretend_args_size);
output_add_immediate (operands);
}
}
#if 0
if (ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER)
asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
REGNO (eh_ofs));
#endif
if (! really_return
|| (ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL
&& current_function_pretend_args_size == 0
&& saved_regs_mask & (1 << PC_REGNUM)))
return "";
switch ((int) ARM_FUNC_TYPE (func_type))
{
case ARM_FT_EXCEPTION_HANDLER:
asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, EXCEPTION_LR_REGNUM);
break;
case ARM_FT_ISR:
case ARM_FT_FIQ:
asm_fprintf (f, "\tsubs\t%r, %r, #4\n", PC_REGNUM, LR_REGNUM);
break;
case ARM_FT_EXCEPTION:
asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, LR_REGNUM);
break;
case ARM_FT_INTERWORKED:
asm_fprintf (f, "\tbx\t%r\n", LR_REGNUM);
break;
default:
if (frame_pointer_needed)
;
else if (current_function_pretend_args_size == 0
&& (saved_regs_mask & (1 << LR_REGNUM)))
;
else if (TARGET_APCS_32)
asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, LR_REGNUM);
else
asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, LR_REGNUM);
break;
}
return "";
}
static void
arm_output_function_epilogue (file, frame_size)
FILE *file ATTRIBUTE_UNUSED;
HOST_WIDE_INT frame_size;
{
if (TARGET_THUMB)
{
return_used_this_function = 0;
}
else
{
frame_size = arm_get_frame_size ();
if (use_return_insn (FALSE)
&& return_used_this_function
&& (frame_size + current_function_outgoing_args_size) != 0
&& !frame_pointer_needed)
abort ();
after_arm_reorg = 0;
}
}
static rtx
emit_multi_reg_push (mask)
int mask;
{
int num_regs = 0;
int num_dwarf_regs;
int i, j;
rtx par;
rtx dwarf;
int dwarf_par_index;
rtx tmp, reg;
for (i = 0; i <= LAST_ARM_REGNUM; i++)
if (mask & (1 << i))
num_regs++;
if (num_regs == 0 || num_regs > 16)
abort ();
num_dwarf_regs = num_regs;
if (mask & (1 << PC_REGNUM))
num_dwarf_regs--;
par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (num_regs));
dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (num_dwarf_regs + 1));
dwarf_par_index = 1;
for (i = 0; i <= LAST_ARM_REGNUM; i++)
{
if (mask & (1 << i))
{
reg = gen_rtx_REG (SImode, i);
XVECEXP (par, 0, 0)
= gen_rtx_SET (VOIDmode,
gen_rtx_MEM (BLKmode,
gen_rtx_PRE_DEC (BLKmode,
stack_pointer_rtx)),
gen_rtx_UNSPEC (BLKmode,
gen_rtvec (1, reg),
UNSPEC_PUSH_MULT));
if (i != PC_REGNUM)
{
tmp = gen_rtx_SET (VOIDmode,
gen_rtx_MEM (SImode, stack_pointer_rtx),
reg);
RTX_FRAME_RELATED_P (tmp) = 1;
XVECEXP (dwarf, 0, dwarf_par_index) = tmp;
dwarf_par_index++;
}
break;
}
}
for (j = 1, i++; j < num_regs; i++)
{
if (mask & (1 << i))
{
reg = gen_rtx_REG (SImode, i);
XVECEXP (par, 0, j) = gen_rtx_USE (VOIDmode, reg);
if (i != PC_REGNUM)
{
tmp = gen_rtx_SET (VOIDmode,
gen_rtx_MEM (SImode,
plus_constant (stack_pointer_rtx,
4 * j)),
reg);
RTX_FRAME_RELATED_P (tmp) = 1;
XVECEXP (dwarf, 0, dwarf_par_index++) = tmp;
}
j++;
}
}
par = emit_insn (par);
tmp = gen_rtx_SET (SImode,
stack_pointer_rtx,
gen_rtx_PLUS (SImode,
stack_pointer_rtx,
GEN_INT (-4 * num_regs)));
RTX_FRAME_RELATED_P (tmp) = 1;
XVECEXP (dwarf, 0, 0) = tmp;
REG_NOTES (par) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf,
REG_NOTES (par));
return par;
}
static rtx
emit_sfm (base_reg, count)
int base_reg;
int count;
{
rtx par;
rtx dwarf;
rtx tmp, reg;
int i;
par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count));
dwarf = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count));
reg = gen_rtx_REG (XFmode, base_reg++);
XVECEXP (par, 0, 0)
= gen_rtx_SET (VOIDmode,
gen_rtx_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,
gen_rtx_MEM (XFmode,
gen_rtx_PRE_DEC (BLKmode, stack_pointer_rtx)),
reg);
RTX_FRAME_RELATED_P (tmp) = 1;
XVECEXP (dwarf, 0, count - 1) = tmp;
for (i = 1; i < count; i++)
{
reg = gen_rtx_REG (XFmode, base_reg++);
XVECEXP (par, 0, i) = gen_rtx_USE (VOIDmode, reg);
tmp = gen_rtx_SET (VOIDmode,
gen_rtx_MEM (XFmode,
gen_rtx_PRE_DEC (BLKmode,
stack_pointer_rtx)),
reg);
RTX_FRAME_RELATED_P (tmp) = 1;
XVECEXP (dwarf, 0, count - i - 1) = tmp;
}
par = emit_insn (par);
REG_NOTES (par) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf,
REG_NOTES (par));
return par;
}
unsigned int
arm_compute_initial_elimination_offset (from, to)
unsigned int from;
unsigned int to;
{
unsigned int local_vars = arm_get_frame_size ();
unsigned int outgoing_args = current_function_outgoing_args_size;
unsigned int stack_frame;
unsigned int call_saved_registers;
unsigned long func_type;
func_type = arm_current_func_type ();
call_saved_registers = 0;
if (! IS_VOLATILE (func_type))
{
unsigned int reg_mask;
unsigned int reg;
reg_mask = arm_compute_save_reg0_reg12_mask ();
while (reg_mask)
{
call_saved_registers += 4;
reg_mask = reg_mask & ~ (reg_mask & - reg_mask);
}
if ((regs_ever_live[LR_REGNUM]
|| (optimize_size && call_saved_registers > 0))
&& ! frame_pointer_needed)
call_saved_registers += 4;
for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg ++)
if (regs_ever_live[reg] && ! call_used_regs[reg])
call_saved_registers += 12;
}
stack_frame = frame_pointer_needed ? 16 : 0;
switch (from)
{
case ARG_POINTER_REGNUM:
switch (to)
{
case THUMB_HARD_FRAME_POINTER_REGNUM:
return 0;
case FRAME_POINTER_REGNUM:
if (call_saved_registers == 0 && stack_frame == 0)
return 0;
return (call_saved_registers + stack_frame - 4);
case ARM_HARD_FRAME_POINTER_REGNUM:
if (stack_frame == 0 && call_saved_registers != 0)
return 0;
return (frame_pointer_needed
&& current_function_needs_context
&& ! cfun->machine->uses_anonymous_args) ? 4 : 0;
case STACK_POINTER_REGNUM:
return call_saved_registers + stack_frame + local_vars + outgoing_args - 4;
default:
abort ();
}
break;
case FRAME_POINTER_REGNUM:
switch (to)
{
case THUMB_HARD_FRAME_POINTER_REGNUM:
return 0;
case ARM_HARD_FRAME_POINTER_REGNUM:
if (call_saved_registers == 0 && stack_frame == 0)
return 0;
return - (call_saved_registers + stack_frame - 4);
case STACK_POINTER_REGNUM:
return local_vars + outgoing_args;
default:
abort ();
}
break;
default:
abort ();
}
}
HOST_WIDE_INT
arm_get_frame_size ()
{
int regno;
int base_size = ROUND_UP (get_frame_size ());
int entry_size = 0;
unsigned long func_type = arm_current_func_type ();
int leaf;
if (! TARGET_ARM)
abort();
if (! TARGET_ATPCS)
return base_size;
if (reload_completed)
return cfun->machine->frame_size;
leaf = leaf_function_p ();
if (leaf && base_size == 0)
{
cfun->machine->frame_size = 0;
return 0;
}
if (current_function_pretend_args_size)
entry_size += current_function_pretend_args_size;
entry_size += bit_count (arm_compute_save_reg_mask ()) * 4;
if (! IS_VOLATILE (func_type))
{
for (regno = FIRST_ARM_FP_REGNUM; regno <= LAST_ARM_FP_REGNUM; regno++)
if (regs_ever_live[regno] && ! call_used_regs[regno])
entry_size += 12;
}
if ((entry_size + base_size + current_function_outgoing_args_size) & 7)
base_size += 4;
if ((entry_size + base_size + current_function_outgoing_args_size) & 7)
abort ();
cfun->machine->frame_size = base_size;
return base_size;
}
void
arm_expand_prologue ()
{
int reg;
rtx amount;
rtx insn;
rtx ip_rtx;
unsigned long live_regs_mask;
unsigned long func_type;
int fp_offset = 0;
int saved_pretend_args = 0;
unsigned int args_to_push;
func_type = arm_current_func_type ();
if (IS_NAKED (func_type))
return;
args_to_push = current_function_pretend_args_size;
live_regs_mask = arm_compute_save_reg_mask ();
ip_rtx = gen_rtx_REG (SImode, IP_REGNUM);
if (frame_pointer_needed)
{
if (IS_INTERRUPT (func_type))
{
insn = emit_multi_reg_push (1 << IP_REGNUM);
}
else if (IS_NESTED (func_type))
{
if (regs_ever_live[3] == 0)
{
insn = gen_rtx_REG (SImode, 3);
insn = gen_rtx_SET (SImode, insn, ip_rtx);
insn = emit_insn (insn);
}
else if (args_to_push == 0)
{
rtx dwarf;
insn = gen_rtx_PRE_DEC (SImode, stack_pointer_rtx);
insn = gen_rtx_MEM (SImode, insn);
insn = gen_rtx_SET (VOIDmode, insn, ip_rtx);
insn = emit_insn (insn);
fp_offset = 4;
dwarf = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
gen_rtx_PLUS (SImode, stack_pointer_rtx,
GEN_INT (-fp_offset)));
RTX_FRAME_RELATED_P (insn) = 1;
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
dwarf, REG_NOTES (insn));
}
else
{
if (cfun->machine->uses_anonymous_args)
insn = emit_multi_reg_push
((0xf0 >> (args_to_push / 4)) & 0xf);
else
insn = emit_insn
(gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
GEN_INT (- args_to_push)));
RTX_FRAME_RELATED_P (insn) = 1;
saved_pretend_args = 1;
fp_offset = args_to_push;
args_to_push = 0;
insn = gen_rtx_REG (SImode, 3);
insn = gen_rtx_SET (SImode, insn, ip_rtx);
(void) emit_insn (insn);
}
}
if (fp_offset)
{
insn = gen_rtx_PLUS (SImode, stack_pointer_rtx, GEN_INT (fp_offset));
insn = gen_rtx_SET (SImode, ip_rtx, insn);
}
else
insn = gen_movsi (ip_rtx, stack_pointer_rtx);
insn = emit_insn (insn);
RTX_FRAME_RELATED_P (insn) = 1;
}
if (args_to_push)
{
if (cfun->machine->uses_anonymous_args)
insn = emit_multi_reg_push
((0xf0 >> (args_to_push / 4)) & 0xf);
else
insn = emit_insn
(gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
GEN_INT (- args_to_push)));
RTX_FRAME_RELATED_P (insn) = 1;
}
if ((func_type == ARM_FT_ISR || func_type == ARM_FT_FIQ)
&& (live_regs_mask & (1 << LR_REGNUM)) != 0)
{
emit_insn (gen_rtx_SET (SImode,
gen_rtx_REG (SImode, LR_REGNUM),
gen_rtx_PLUS (SImode,
gen_rtx_REG (SImode, LR_REGNUM),
GEN_INT (-4))));
}
if (live_regs_mask)
{
insn = emit_multi_reg_push (live_regs_mask);
RTX_FRAME_RELATED_P (insn) = 1;
}
if (! IS_VOLATILE (func_type))
{
if (arm_fpu_arch == FP_SOFT2)
{
for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg --)
if (regs_ever_live[reg] && !call_used_regs[reg])
{
insn = gen_rtx_PRE_DEC (XFmode, stack_pointer_rtx);
insn = gen_rtx_MEM (XFmode, insn);
insn = emit_insn (gen_rtx_SET (VOIDmode, insn,
gen_rtx_REG (XFmode, reg)));
RTX_FRAME_RELATED_P (insn) = 1;
}
}
else
{
int start_reg = LAST_ARM_FP_REGNUM;
for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg --)
{
if (regs_ever_live[reg] && !call_used_regs[reg])
{
if (start_reg - reg == 3)
{
insn = emit_sfm (reg, 4);
RTX_FRAME_RELATED_P (insn) = 1;
start_reg = reg - 1;
}
}
else
{
if (start_reg != reg)
{
insn = emit_sfm (reg + 1, start_reg - reg);
RTX_FRAME_RELATED_P (insn) = 1;
}
start_reg = reg - 1;
}
}
if (start_reg != reg)
{
insn = emit_sfm (reg + 1, start_reg - reg);
RTX_FRAME_RELATED_P (insn) = 1;
}
}
}
if (frame_pointer_needed)
{
insn = GEN_INT (-(4 + args_to_push + fp_offset));
insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, ip_rtx, insn));
RTX_FRAME_RELATED_P (insn) = 1;
if (IS_NESTED (func_type))
{
if (regs_ever_live [3] == 0
|| saved_pretend_args)
insn = gen_rtx_REG (SImode, 3);
else
{
insn = gen_rtx_PLUS (SImode, hard_frame_pointer_rtx, GEN_INT (4));
insn = gen_rtx_MEM (SImode, insn);
}
emit_insn (gen_rtx_SET (SImode, ip_rtx, insn));
emit_insn (gen_prologue_use (ip_rtx));
}
}
amount = GEN_INT (-(arm_get_frame_size ()
+ current_function_outgoing_args_size));
if (amount != const0_rtx)
{
rtx last = get_last_insn ();
insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
amount));
do
{
last = last ? NEXT_INSN (last) : get_insns ();
RTX_FRAME_RELATED_P (last) = 1;
}
while (last != insn);
if (frame_pointer_needed)
insn = emit_insn (gen_stack_tie (stack_pointer_rtx,
hard_frame_pointer_rtx));
}
if (current_function_profile || TARGET_NO_SCHED_PRO)
emit_insn (gen_blockage ());
if ((live_regs_mask & (1 << LR_REGNUM)) == 0)
{
emit_insn (gen_prologue_use (gen_rtx_REG (SImode, LR_REGNUM)));
cfun->machine->lr_save_eliminated = 1;
}
}
void
arm_print_operand (stream, x, code)
FILE * stream;
rtx x;
int code;
{
switch (code)
{
case '@':
fputs (ASM_COMMENT_START, stream);
return;
case '_':
fputs (user_label_prefix, stream);
return;
case '|':
fputs (REGISTER_PREFIX, stream);
return;
case '?':
if (arm_ccfsm_state == 3 || arm_ccfsm_state == 4)
{
if (TARGET_THUMB || current_insn_predicate != NULL)
abort ();
fputs (arm_condition_codes[arm_current_cc], stream);
}
else if (current_insn_predicate)
{
enum arm_cond_code code;
if (TARGET_THUMB)
abort ();
code = get_arm_condition_code (current_insn_predicate);
fputs (arm_condition_codes[code], stream);
}
return;
case 'N':
{
REAL_VALUE_TYPE r;
REAL_VALUE_FROM_CONST_DOUBLE (r, x);
r = REAL_VALUE_NEGATE (r);
fprintf (stream, "%s", fp_const_from_val (&r));
}
return;
case 'B':
if (GET_CODE (x) == CONST_INT)
{
HOST_WIDE_INT val;
val = ARM_SIGN_EXTEND (~INTVAL (x));
fprintf (stream, HOST_WIDE_INT_PRINT_DEC, val);
}
else
{
putc ('~', stream);
output_addr_const (stream, x);
}
return;
case 'i':
fprintf (stream, "%s", arithmetic_instr (x, 1));
return;
case 'I':
fprintf (stream, "%s", arithmetic_instr (x, 0));
return;
case 'S':
{
HOST_WIDE_INT val;
const char * shift = shift_op (x, &val);
if (shift)
{
fprintf (stream, ", %s ", shift_op (x, &val));
if (val == -1)
arm_print_operand (stream, XEXP (x, 1), 0);
else
{
fputc ('#', stream);
fprintf (stream, HOST_WIDE_INT_PRINT_DEC, val);
}
}
}
return;
case 'Q':
if (REGNO (x) > LAST_ARM_REGNUM)
abort ();
asm_fprintf (stream, "%r", REGNO (x) + (WORDS_BIG_ENDIAN ? 1 : 0));
return;
case 'R':
if (REGNO (x) > LAST_ARM_REGNUM)
abort ();
asm_fprintf (stream, "%r", REGNO (x) + (WORDS_BIG_ENDIAN ? 0 : 1));
return;
case 'H':
if (REGNO (x) > LAST_ARM_REGNUM)
abort ();
asm_fprintf (stream, "%r", REGNO (x) + 1);
return;
case 'm':
asm_fprintf (stream, "%r",
GET_CODE (XEXP (x, 0)) == REG
? REGNO (XEXP (x, 0)) : REGNO (XEXP (XEXP (x, 0), 0)));
return;
case 'M':
asm_fprintf (stream, "{%r-%r}",
REGNO (x),
REGNO (x) + ARM_NUM_REGS (GET_MODE (x)) - 1);
return;
case 'd':
if (x == const_true_rtx)
return;
if (TARGET_ARM)
fputs (arm_condition_codes[get_arm_condition_code (x)],
stream);
else
fputs (thumb_condition_code (x, 0), stream);
return;
case 'D':
if (x == const_true_rtx)
abort ();
if (TARGET_ARM)
fputs (arm_condition_codes[ARM_INVERSE_CONDITION_CODE
(get_arm_condition_code (x))],
stream);
else
fputs (thumb_condition_code (x, 1), stream);
return;
default:
if (x == 0)
abort ();
if (GET_CODE (x) == REG)
asm_fprintf (stream, "%r", REGNO (x));
else if (GET_CODE (x) == MEM)
{
output_memory_reference_mode = GET_MODE (x);
output_address (XEXP (x, 0));
}
else if (GET_CODE (x) == CONST_DOUBLE)
fprintf (stream, "#%s", fp_immediate_constant (x));
else if (GET_CODE (x) == NEG)
abort ();
else
{
fputc ('#', stream);
output_addr_const (stream, x);
}
}
}
#ifndef AOF_ASSEMBLER
static bool
arm_assemble_integer (x, size, aligned_p)
rtx x;
unsigned int size;
int aligned_p;
{
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 (GET_CODE (x) == SYMBOL_REF
&& (CONSTANT_POOL_ADDRESS_P (x)
|| ENCODED_SHORT_CALL_ATTR_P (XSTR (x, 0))))
fputs ("(GOTOFF)", asm_out_file);
else if (GET_CODE (x) == LABEL_REF)
fputs ("(GOTOFF)", asm_out_file);
else
fputs ("(GOT)", asm_out_file);
}
fputc ('\n', asm_out_file);
return true;
}
return default_assemble_integer (x, size, aligned_p);
}
#endif
static enum arm_cond_code
get_arm_condition_code (comparison)
rtx comparison;
{
enum machine_mode mode = GET_MODE (XEXP (comparison, 0));
int code;
enum rtx_code comp_code = GET_CODE (comparison);
if (GET_MODE_CLASS (mode) != MODE_CC)
mode = SELECT_CC_MODE (comp_code, XEXP (comparison, 0),
XEXP (comparison, 1));
switch (mode)
{
case CC_DNEmode: code = ARM_NE; goto dominance;
case CC_DEQmode: code = ARM_EQ; goto dominance;
case CC_DGEmode: code = ARM_GE; goto dominance;
case CC_DGTmode: code = ARM_GT; goto dominance;
case CC_DLEmode: code = ARM_LE; goto dominance;
case CC_DLTmode: code = ARM_LT; goto dominance;
case CC_DGEUmode: code = ARM_CS; goto dominance;
case CC_DGTUmode: code = ARM_HI; goto dominance;
case CC_DLEUmode: code = ARM_LS; goto dominance;
case CC_DLTUmode: code = ARM_CC;
dominance:
if (comp_code != EQ && comp_code != NE)
abort ();
if (comp_code == EQ)
return ARM_INVERSE_CONDITION_CODE (code);
return code;
case CC_NOOVmode:
switch (comp_code)
{
case NE: return ARM_NE;
case EQ: return ARM_EQ;
case GE: return ARM_PL;
case LT: return ARM_MI;
default: abort ();
}
case CC_Zmode:
switch (comp_code)
{
case NE: return ARM_NE;
case EQ: return ARM_EQ;
default: abort ();
}
case CCFPEmode:
case CCFPmode:
switch (comp_code)
{
case GE: return ARM_GE;
case GT: return ARM_GT;
case LE: return ARM_LS;
case LT: return ARM_MI;
case NE: return ARM_NE;
case EQ: return ARM_EQ;
case ORDERED: return ARM_VC;
case UNORDERED: return ARM_VS;
case UNLT: return ARM_LT;
case UNLE: return ARM_LE;
case UNGT: return ARM_HI;
case UNGE: return ARM_PL;
case UNEQ:
case LTGT:
default: abort ();
}
case CC_SWPmode:
switch (comp_code)
{
case NE: return ARM_NE;
case EQ: return ARM_EQ;
case GE: return ARM_LE;
case GT: return ARM_LT;
case LE: return ARM_GE;
case LT: return ARM_GT;
case GEU: return ARM_LS;
case GTU: return ARM_CC;
case LEU: return ARM_CS;
case LTU: return ARM_HI;
default: abort ();
}
case CC_Cmode:
switch (comp_code)
{
case LTU: return ARM_CS;
case GEU: return ARM_CC;
default: abort ();
}
case CCmode:
switch (comp_code)
{
case NE: return ARM_NE;
case EQ: return ARM_EQ;
case GE: return ARM_GE;
case GT: return ARM_GT;
case LE: return ARM_LE;
case LT: return ARM_LT;
case GEU: return ARM_CS;
case GTU: return ARM_HI;
case LEU: return ARM_LS;
case LTU: return ARM_CC;
default: abort ();
}
default: abort ();
}
abort ();
}
void
arm_final_prescan_insn (insn)
rtx insn;
{
rtx body = PATTERN (insn);
int reverse = 0;
int jump_clobbers = 0;
int seeking_return = 0;
rtx start_insn = insn;
if (arm_ccfsm_state == 4)
{
if (insn == arm_target_insn)
{
arm_target_insn = NULL;
arm_ccfsm_state = 0;
}
return;
}
if (arm_ccfsm_state == 3)
{
if (simplejump_p (insn))
{
start_insn = next_nonnote_insn (start_insn);
if (GET_CODE (start_insn) == BARRIER)
{
start_insn = next_nonnote_insn (start_insn);
}
if (GET_CODE (start_insn) == CODE_LABEL
&& CODE_LABEL_NUMBER (start_insn) == arm_target_label
&& LABEL_NUSES (start_insn) == 1)
reverse = TRUE;
else
return;
}
else if (GET_CODE (body) == RETURN)
{
start_insn = next_nonnote_insn (start_insn);
if (GET_CODE (start_insn) == BARRIER)
start_insn = next_nonnote_insn (start_insn);
if (GET_CODE (start_insn) == CODE_LABEL
&& CODE_LABEL_NUMBER (start_insn) == arm_target_label
&& LABEL_NUSES (start_insn) == 1)
{
reverse = TRUE;
seeking_return = 1;
}
else
return;
}
else
return;
}
if (arm_ccfsm_state != 0 && !reverse)
abort ();
if (GET_CODE (insn) != JUMP_INSN)
return;
if (GET_CODE (body) == PARALLEL && XVECLEN (body, 0) > 0)
body = XVECEXP (body, 0, 0);
#if 0
if (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC
&& GET_CODE (SET_SRC (body)) == IF_THEN_ELSE
&& (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN
|| GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN))
return;
#endif
if (reverse
|| (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC
&& GET_CODE (SET_SRC (body)) == IF_THEN_ELSE))
{
int insns_skipped;
int fail = FALSE, succeed = FALSE;
int then_not_else = TRUE;
rtx this_insn = start_insn, label = 0;
if (get_attr_conds (insn) == CONDS_JUMP_CLOB)
{
jump_clobbers = 1;
return;
}
if (reverse)
{
if (!seeking_return)
label = XEXP (SET_SRC (body), 0);
}
else if (GET_CODE (XEXP (SET_SRC (body), 1)) == LABEL_REF)
label = XEXP (XEXP (SET_SRC (body), 1), 0);
else if (GET_CODE (XEXP (SET_SRC (body), 2)) == LABEL_REF)
{
label = XEXP (XEXP (SET_SRC (body), 2), 0);
then_not_else = FALSE;
}
else if (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN)
seeking_return = 1;
else if (GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN)
{
seeking_return = 1;
then_not_else = FALSE;
}
else
abort ();
for (insns_skipped = 0;
!fail && !succeed && insns_skipped++ < max_insns_skipped;)
{
rtx scanbody;
this_insn = next_nonnote_insn (this_insn);
if (!this_insn)
break;
switch (GET_CODE (this_insn))
{
case CODE_LABEL:
if (this_insn == label)
{
if (jump_clobbers)
{
arm_ccfsm_state = 2;
this_insn = next_nonnote_insn (this_insn);
}
else
arm_ccfsm_state = 1;
succeed = TRUE;
}
else
fail = TRUE;
break;
case BARRIER:
this_insn = next_nonnote_insn (this_insn);
if (this_insn && this_insn == label)
{
if (jump_clobbers)
{
arm_ccfsm_state = 2;
this_insn = next_nonnote_insn (this_insn);
}
else
arm_ccfsm_state = 1;
succeed = TRUE;
}
else
fail = TRUE;
break;
case CALL_INSN:
if (TARGET_APCS_32)
{
this_insn = next_nonnote_insn (this_insn);
if (this_insn && GET_CODE (this_insn) == BARRIER)
this_insn = next_nonnote_insn (this_insn);
if (this_insn && this_insn == label
&& insns_skipped < max_insns_skipped)
{
if (jump_clobbers)
{
arm_ccfsm_state = 2;
this_insn = next_nonnote_insn (this_insn);
}
else
arm_ccfsm_state = 1;
succeed = TRUE;
}
else
fail = TRUE;
}
break;
case JUMP_INSN:
scanbody = PATTERN (this_insn);
if (GET_CODE (scanbody) == SET
&& GET_CODE (SET_DEST (scanbody)) == PC)
{
if (GET_CODE (SET_SRC (scanbody)) == LABEL_REF
&& XEXP (SET_SRC (scanbody), 0) == label && !reverse)
{
arm_ccfsm_state = 2;
succeed = TRUE;
}
else if (GET_CODE (SET_SRC (scanbody)) == IF_THEN_ELSE)
fail = TRUE;
}
else if (GET_CODE (scanbody) == RETURN
&& !use_return_insn (TRUE)
&& !optimize_size)
fail = TRUE;
else if (GET_CODE (scanbody) == RETURN
&& seeking_return)
{
arm_ccfsm_state = 2;
succeed = TRUE;
}
else if (GET_CODE (scanbody) == PARALLEL)
{
switch (get_attr_conds (this_insn))
{
case CONDS_NOCOND:
break;
default:
fail = TRUE;
break;
}
}
else
fail = TRUE;
break;
case INSN:
scanbody = PATTERN (this_insn);
if (!(GET_CODE (scanbody) == SET
|| GET_CODE (scanbody) == PARALLEL)
|| get_attr_conds (this_insn) != CONDS_NOCOND)
fail = TRUE;
break;
default:
break;
}
}
if (succeed)
{
if ((!seeking_return) && (arm_ccfsm_state == 1 || reverse))
arm_target_label = CODE_LABEL_NUMBER (label);
else if (seeking_return || arm_ccfsm_state == 2)
{
while (this_insn && GET_CODE (PATTERN (this_insn)) == USE)
{
this_insn = next_nonnote_insn (this_insn);
if (this_insn && (GET_CODE (this_insn) == BARRIER
|| GET_CODE (this_insn) == CODE_LABEL))
abort ();
}
if (!this_insn)
{
recog (PATTERN (insn), insn, NULL);
arm_ccfsm_state = 0;
arm_target_insn = NULL;
return;
}
arm_target_insn = this_insn;
}
else
abort ();
if (jump_clobbers)
{
if (reverse)
abort ();
arm_current_cc =
get_arm_condition_code (XEXP (XEXP (XEXP (SET_SRC (body),
0), 0), 1));
if (GET_CODE (XEXP (XEXP (SET_SRC (body), 0), 0)) == AND)
arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
if (GET_CODE (XEXP (SET_SRC (body), 0)) == NE)
arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
}
else
{
if (!reverse)
arm_current_cc = get_arm_condition_code (XEXP (SET_SRC (body),
0));
}
if (reverse || then_not_else)
arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
}
recog (PATTERN (insn), insn, NULL);
}
}
int
arm_hard_regno_mode_ok (regno, mode)
unsigned int regno;
enum machine_mode mode;
{
if (GET_MODE_CLASS (mode) == MODE_CC)
return regno == CC_REGNUM;
if (TARGET_THUMB)
return (ARM_NUM_REGS (mode) < 2) || (regno < LAST_LO_REGNUM);
if (regno <= LAST_ARM_REGNUM)
return 1;
if ( regno == FRAME_POINTER_REGNUM
|| regno == ARG_POINTER_REGNUM)
return GET_MODE_CLASS (mode) == MODE_INT;
return GET_MODE_CLASS (mode) == MODE_FLOAT
&& regno >= FIRST_ARM_FP_REGNUM
&& regno <= LAST_ARM_FP_REGNUM;
}
int
arm_regno_class (regno)
int regno;
{
if (TARGET_THUMB)
{
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 ( regno <= LAST_ARM_REGNUM
|| regno == FRAME_POINTER_REGNUM
|| regno == ARG_POINTER_REGNUM)
return GENERAL_REGS;
if (regno == CC_REGNUM)
return NO_REGS;
return FPU_REGS;
}
int
arm_debugger_arg_offset (value, addr)
int value;
rtx addr;
{
rtx insn;
if (value != 0)
return 0;
if (GET_CODE (addr) != REG)
return 0;
if (REGNO (addr) == (unsigned) HARD_FRAME_POINTER_REGNUM)
return 0;
if ((TARGET_THUMB || !frame_pointer_needed)
&& REGNO (addr) == SP_REGNUM)
return 0;
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
if ( GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) == SET
&& REGNO (XEXP (PATTERN (insn), 0)) == REGNO (addr)
&& GET_CODE (XEXP (PATTERN (insn), 1)) == PLUS
&& GET_CODE (XEXP (XEXP (PATTERN (insn), 1), 0)) == REG
&& REGNO (XEXP (XEXP (PATTERN (insn), 1), 0)) == (unsigned) HARD_FRAME_POINTER_REGNUM
&& GET_CODE (XEXP (XEXP (PATTERN (insn), 1), 1)) == CONST_INT
)
{
value = INTVAL (XEXP (XEXP (PATTERN (insn), 1), 1));
break;
}
}
if (value == 0)
{
debug_rtx (addr);
warning ("unable to compute real location of stacked parameter");
value = 8;
}
return value;
}
#define def_builtin(NAME, TYPE, CODE) \
builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD, NULL, NULL_TREE)
void
arm_init_builtins ()
{
tree endlink = void_list_node;
tree int_endlink = tree_cons (NULL_TREE, integer_type_node, endlink);
tree pchar_type_node = build_pointer_type (char_type_node);
tree int_ftype_int, void_ftype_pchar;
void_ftype_pchar
= build_function_type_list (void_type_node, pchar_type_node, NULL_TREE);
int_ftype_int
= build_function_type (integer_type_node, int_endlink);
if (arm_arch5)
def_builtin ("__builtin_clz", int_ftype_int, ARM_BUILTIN_CLZ);
}
rtx
arm_expand_builtin (exp, target, subtarget, mode, ignore)
tree exp;
rtx target;
rtx subtarget ATTRIBUTE_UNUSED;
enum machine_mode mode ATTRIBUTE_UNUSED;
int ignore ATTRIBUTE_UNUSED;
{
enum insn_code icode;
tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
tree arglist = TREE_OPERAND (exp, 1);
tree arg0;
rtx op0, pat;
enum machine_mode tmode, mode0;
int fcode = DECL_FUNCTION_CODE (fndecl);
switch (fcode)
{
default:
break;
case ARM_BUILTIN_CLZ:
icode = CODE_FOR_clz;
arg0 = TREE_VALUE (arglist);
op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
tmode = insn_data[icode].operand[0].mode;
mode0 = insn_data[icode].operand[1].mode;
if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
op0 = copy_to_mode_reg (mode0, op0);
if (target == 0
|| GET_MODE (target) != tmode
|| ! (*insn_data[icode].operand[0].predicate) (target, tmode))
target = gen_reg_rtx (tmode);
pat = GEN_FCN (icode) (target, op0);
if (! pat)
return 0;
emit_insn (pat);
return target;
}
return NULL_RTX;
}
static void
replace_symbols_in_block (block, orig, new)
tree block;
rtx orig;
rtx new;
{
for (; block; block = BLOCK_CHAIN (block))
{
tree sym;
if (!TREE_USED (block))
continue;
for (sym = BLOCK_VARS (block); sym; sym = TREE_CHAIN (sym))
{
if ( (DECL_NAME (sym) == 0 && TREE_CODE (sym) != TYPE_DECL)
|| DECL_IGNORED_P (sym)
|| TREE_CODE (sym) != VAR_DECL
|| DECL_EXTERNAL (sym)
|| !rtx_equal_p (DECL_RTL (sym), orig)
)
continue;
SET_DECL_RTL (sym, new);
}
replace_symbols_in_block (BLOCK_SUBBLOCKS (block), orig, new);
}
}
#ifdef __GNUC__
inline
#endif
static int
number_of_first_bit_set (mask)
int mask;
{
int bit;
for (bit = 0;
(mask & (1 << bit)) == 0;
++bit)
continue;
return bit;
}
static void
thumb_exit (f, reg_containing_return_addr, eh_ofs)
FILE * f;
int reg_containing_return_addr;
rtx eh_ofs;
{
unsigned regs_available_for_popping;
unsigned regs_to_pop;
int pops_needed;
unsigned available;
unsigned required;
int mode;
int size;
int restore_a4 = FALSE;
regs_to_pop = 0;
pops_needed = 0;
if (reg_containing_return_addr == -1 || eh_ofs)
{
if (eh_ofs && reg_containing_return_addr == -1)
abort ();
regs_to_pop |= 1 << LR_REGNUM;
++pops_needed;
}
if (TARGET_BACKTRACE)
{
regs_to_pop |= (1 << ARM_HARD_FRAME_POINTER_REGNUM) | (1 << SP_REGNUM);
pops_needed += 2;
}
if (pops_needed == 0)
{
if (eh_ofs)
asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, REGNO (eh_ofs));
asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr);
return;
}
else if (!TARGET_INTERWORK
&& !TARGET_BACKTRACE
&& !is_called_in_ARM_mode (current_function_decl))
{
if (eh_ofs)
{
asm_fprintf (f, "\tadd\t%r, #4\n", SP_REGNUM);
asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, REGNO (eh_ofs));
asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr);
}
else
asm_fprintf (f, "\tpop\t{%r}\n", PC_REGNUM);
return;
}
regs_available_for_popping = 0;
if (eh_ofs)
size = 12;
else
{
#ifdef RTX_CODE
if (current_function_return_rtx != 0)
mode = GET_MODE (current_function_return_rtx);
else
#endif
mode = DECL_MODE (DECL_RESULT (current_function_decl));
size = GET_MODE_SIZE (mode);
if (size == 0)
{
if (mode == VOIDmode)
regs_available_for_popping =
(1 << ARG_REGISTER (1))
| (1 << ARG_REGISTER (2))
| (1 << ARG_REGISTER (3));
else
regs_available_for_popping =
(1 << ARG_REGISTER (2))
| (1 << ARG_REGISTER (3));
}
else if (size <= 4)
regs_available_for_popping =
(1 << ARG_REGISTER (2))
| (1 << ARG_REGISTER (3));
else if (size <= 8)
regs_available_for_popping =
(1 << ARG_REGISTER (3));
}
for (available = regs_available_for_popping,
required = regs_to_pop;
required != 0 && available != 0;
available &= ~(available & - available),
required &= ~(required & - required))
-- pops_needed;
if (available > 0)
regs_available_for_popping &= ~available;
else if (pops_needed)
{
if (regs_available_for_popping == 0
&& reg_containing_return_addr == LAST_ARG_REGNUM)
{
asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM, LAST_ARG_REGNUM);
reg_containing_return_addr = LR_REGNUM;
}
else if (size > 12)
{
restore_a4 = TRUE;
asm_fprintf (f, "\tmov\t%r, %r\n",IP_REGNUM, LAST_ARG_REGNUM);
}
if (reg_containing_return_addr != LAST_ARG_REGNUM)
{
regs_available_for_popping |= 1 << LAST_ARG_REGNUM;
--pops_needed;
}
}
thumb_pushpop (f, regs_available_for_popping, FALSE);
if (reg_containing_return_addr == -1)
{
regs_to_pop &= ~(1 << LR_REGNUM);
reg_containing_return_addr =
number_of_first_bit_set (regs_available_for_popping);
regs_available_for_popping &= ~(1 << reg_containing_return_addr);
}
if (regs_available_for_popping)
{
int frame_pointer;
frame_pointer = number_of_first_bit_set (regs_available_for_popping);
asm_fprintf (f, "\tmov\t%r, %r\n",
ARM_HARD_FRAME_POINTER_REGNUM, frame_pointer);
regs_available_for_popping &= ~(1 << frame_pointer);
regs_to_pop &= ~(1 << ARM_HARD_FRAME_POINTER_REGNUM);
if (regs_available_for_popping)
{
int stack_pointer;
stack_pointer = number_of_first_bit_set (regs_available_for_popping);
asm_fprintf (f, "\tmov\t%r, %r\n", SP_REGNUM, stack_pointer);
}
else
{
regs_available_for_popping |= (1 << frame_pointer);
}
}
if (regs_available_for_popping == 0 && pops_needed > 0)
{
regs_available_for_popping |= 1 << reg_containing_return_addr;
asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM,
reg_containing_return_addr);
reg_containing_return_addr = LR_REGNUM;
}
if (pops_needed > 0)
{
int popped_into;
int move_to;
thumb_pushpop (f, regs_available_for_popping, FALSE);
popped_into = number_of_first_bit_set (regs_available_for_popping);
move_to = number_of_first_bit_set (regs_to_pop);
asm_fprintf (f, "\tmov\t%r, %r\n", move_to, popped_into);
regs_to_pop &= ~(1 << move_to);
--pops_needed;
}
if (pops_needed > 0)
{
int popped_into;
thumb_pushpop (f, regs_available_for_popping, FALSE);
popped_into = number_of_first_bit_set (regs_available_for_popping);
asm_fprintf (f, "\tmov\t%r, %r\n", SP_REGNUM, popped_into);
}
if (restore_a4)
{
if (reg_containing_return_addr != LR_REGNUM)
{
asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM, LAST_ARG_REGNUM);
reg_containing_return_addr = LR_REGNUM;
}
asm_fprintf (f, "\tmov\t%r, %r\n", LAST_ARG_REGNUM, IP_REGNUM);
}
if (eh_ofs)
asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, REGNO (eh_ofs));
asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr);
}
static void
thumb_pushpop (f, mask, push)
FILE * f;
int mask;
int push;
{
int regno;
int lo_mask = mask & 0xFF;
if (lo_mask == 0 && !push && (mask & (1 << 15)))
{
thumb_exit (f, -1, NULL_RTX);
return;
}
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, ", ");
}
}
if (push && (mask & (1 << LR_REGNUM)))
{
if (mask & 0xFF)
fprintf (f, ", ");
asm_fprintf (f, "%r", LR_REGNUM);
}
else if (!push && (mask & (1 << PC_REGNUM)))
{
if (TARGET_INTERWORK || TARGET_BACKTRACE)
{
fprintf (f, "}\n");
thumb_exit (f, -1, NULL_RTX);
return;
}
else
{
if (mask & 0xFF)
fprintf (f, ", ");
asm_fprintf (f, "%r", PC_REGNUM);
}
}
fprintf (f, "}\n");
}
void
thumb_final_prescan_insn (insn)
rtx insn;
{
if (flag_print_asm_name)
asm_fprintf (asm_out_file, "%@ 0x%04x\n",
INSN_ADDRESSES (INSN_UID (insn)));
}
int
thumb_shiftable_const (val)
unsigned HOST_WIDE_INT val;
{
unsigned HOST_WIDE_INT mask = 0xff;
int i;
if (val == 0)
return 0;
for (i = 0; i < 25; i++)
if ((val & (mask << i)) == val)
return 1;
return 0;
}
int
thumb_far_jump_used_p (in_prologue)
int in_prologue;
{
rtx insn;
if (cfun->machine->far_jump_used)
return 1;
if (!in_prologue)
{
if (regs_ever_live [ARG_POINTER_REGNUM])
cfun->machine->arg_pointer_live = 1;
else if (!cfun->machine->arg_pointer_live)
return 0;
}
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) != ADDR_VEC
&& GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC
&& get_attr_far_jump (insn) == FAR_JUMP_YES
)
{
cfun->machine->far_jump_used = 1;
return 1;
}
}
return 0;
}
int
is_called_in_ARM_mode (func)
tree func;
{
if (TREE_CODE (func) != FUNCTION_DECL)
abort ();
if (TARGET_CALLEE_INTERWORKING && TREE_PUBLIC (func))
return TRUE;
#ifdef ARM_PE
return lookup_attribute ("interfacearm", DECL_ATTRIBUTES (func)) != NULL_TREE;
#else
return FALSE;
#endif
}
const char *
thumb_unexpanded_epilogue ()
{
int regno;
int live_regs_mask = 0;
int high_regs_pushed = 0;
int leaf_function = leaf_function_p ();
int had_to_push_lr;
rtx eh_ofs = cfun->machine->eh_epilogue_sp_ofs;
if (return_used_this_function)
return "";
if (IS_NAKED (arm_current_func_type ()))
return "";
for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
if (THUMB_REG_PUSHED_P (regno))
live_regs_mask |= 1 << regno;
for (regno = 8; regno < 13; regno++)
if (THUMB_REG_PUSHED_P (regno))
high_regs_pushed++;
if (high_regs_pushed)
{
int mask = live_regs_mask;
int next_hi_reg;
int size;
int mode;
#ifdef RTX_CODE
if (current_function_return_rtx != 0)
mode = GET_MODE (current_function_return_rtx);
else
#endif
mode = DECL_MODE (DECL_RESULT (current_function_decl));
size = GET_MODE_SIZE (mode);
if (size < 13)
mask |= 1 << 3;
if (mask == 0)
internal_error
("no low registers available for popping high registers");
for (next_hi_reg = 8; next_hi_reg < 13; next_hi_reg++)
if (THUMB_REG_PUSHED_P (next_hi_reg))
break;
while (high_regs_pushed)
{
for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
{
if (mask & (1 << regno))
high_regs_pushed--;
if (high_regs_pushed == 0)
break;
}
mask &= (2 << regno) - 1;
thumb_pushpop (asm_out_file, mask, 0);
for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
{
if (mask & (1 << regno))
{
asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", next_hi_reg,
regno);
for (next_hi_reg++; next_hi_reg < 13; next_hi_reg++)
if (THUMB_REG_PUSHED_P (next_hi_reg))
break;
}
}
}
}
had_to_push_lr = (live_regs_mask || !leaf_function
|| thumb_far_jump_used_p (1));
if (TARGET_BACKTRACE
&& ((live_regs_mask & 0xFF) == 0)
&& regs_ever_live [LAST_ARG_REGNUM] != 0)
{
live_regs_mask |= (1 << LAST_LO_REGNUM);
}
if (current_function_pretend_args_size == 0 || TARGET_BACKTRACE)
{
if (had_to_push_lr
&& !is_called_in_ARM_mode (current_function_decl)
&& !eh_ofs)
live_regs_mask |= 1 << PC_REGNUM;
if (live_regs_mask)
thumb_pushpop (asm_out_file, live_regs_mask, FALSE);
if (eh_ofs)
thumb_exit (asm_out_file, 2, eh_ofs);
else if ((live_regs_mask & (1 << PC_REGNUM)) == 0)
thumb_exit (asm_out_file,
(had_to_push_lr
&& is_called_in_ARM_mode (current_function_decl)) ?
-1 : LR_REGNUM, NULL_RTX);
}
else
{
live_regs_mask &= ~(1 << PC_REGNUM);
if (live_regs_mask)
thumb_pushpop (asm_out_file, live_regs_mask, FALSE);
if (had_to_push_lr)
thumb_pushpop (asm_out_file, 1 << LAST_ARG_REGNUM, 0);
asm_fprintf (asm_out_file, "\tadd\t%r, %r, #%d\n",
SP_REGNUM, SP_REGNUM,
current_function_pretend_args_size);
if (eh_ofs)
thumb_exit (asm_out_file, 2, eh_ofs);
else
thumb_exit (asm_out_file,
had_to_push_lr ? LAST_ARG_REGNUM : LR_REGNUM, NULL_RTX);
}
return "";
}
static struct machine_function *
arm_init_machine_status ()
{
struct machine_function *machine;
machine = (machine_function *) ggc_alloc_cleared (sizeof (machine_function));
#if ARM_FT_UNKNOWN != 0
machine->func_type = ARM_FT_UNKNOWN;
#endif
return machine;
}
rtx
arm_return_addr (count, frame)
int count;
rtx frame ATTRIBUTE_UNUSED;
{
if (count != 0)
return NULL_RTX;
if (TARGET_APCS_32)
return get_hard_reg_initial_val (Pmode, LR_REGNUM);
else
{
rtx lr = gen_rtx_AND (Pmode, gen_rtx_REG (Pmode, LR_REGNUM),
GEN_INT (RETURN_ADDR_MASK26));
return get_func_hard_reg_initial_val (cfun, lr);
}
}
void
arm_init_expanders ()
{
init_machine_status = arm_init_machine_status;
}
HOST_WIDE_INT
thumb_get_frame_size ()
{
int regno;
int base_size = ROUND_UP (get_frame_size ());
int count_regs = 0;
int entry_size = 0;
int leaf;
if (! TARGET_THUMB)
abort ();
if (! TARGET_ATPCS)
return base_size;
if (reload_completed)
return cfun->machine->frame_size;
leaf = leaf_function_p ();
if (leaf && base_size == 0)
{
cfun->machine->frame_size = 0;
return 0;
}
if (current_function_pretend_args_size)
entry_size += current_function_pretend_args_size;
for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
if (THUMB_REG_PUSHED_P (regno))
count_regs++;
if (TARGET_BACKTRACE)
{
if (count_regs == 0 && regs_ever_live[LAST_ARG_REGNUM] != 0)
entry_size += 20;
else
entry_size += 16;
}
if (count_regs || !leaf || thumb_far_jump_used_p (1))
count_regs++;
entry_size += count_regs * 4;
count_regs = 0;
for (regno = 8; regno < 13; regno++)
if (THUMB_REG_PUSHED_P (regno))
count_regs++;
entry_size += count_regs * 4;
if ((entry_size + base_size + current_function_outgoing_args_size) & 7)
base_size += 4;
if ((entry_size + base_size + current_function_outgoing_args_size) & 7)
abort ();
cfun->machine->frame_size = base_size;
return base_size;
}
void
thumb_expand_prologue ()
{
HOST_WIDE_INT amount = (thumb_get_frame_size ()
+ current_function_outgoing_args_size);
unsigned long func_type;
func_type = arm_current_func_type ();
if (IS_NAKED (func_type))
return;
if (IS_INTERRUPT (func_type))
{
error ("interrupt Service Routines cannot be coded in Thumb mode");
return;
}
if (frame_pointer_needed)
emit_insn (gen_movsi (hard_frame_pointer_rtx, stack_pointer_rtx));
if (amount)
{
amount = ROUND_UP (amount);
if (amount < 512)
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
GEN_INT (- amount)));
else
{
int regno;
rtx reg;
for (regno = LAST_ARG_REGNUM + 1; regno <= LAST_LO_REGNUM; regno++)
if (THUMB_REG_PUSHED_P (regno)
&& !(frame_pointer_needed
&& (regno == THUMB_HARD_FRAME_POINTER_REGNUM)))
break;
if (regno > LAST_LO_REGNUM)
{
rtx spare = gen_rtx (REG, SImode, IP_REGNUM);
reg = gen_rtx (REG, SImode, LAST_LO_REGNUM);
emit_insn (gen_movsi (spare, reg));
emit_insn (gen_prologue_use (spare));
emit_insn (gen_movsi (reg, GEN_INT (- amount)));
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
reg));
emit_insn (gen_movsi (reg, spare));
emit_insn (gen_prologue_use (reg));
}
else
{
reg = gen_rtx (REG, SImode, regno);
emit_insn (gen_movsi (reg, GEN_INT (- amount)));
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
reg));
}
}
}
if (current_function_profile || TARGET_NO_SCHED_PRO)
emit_insn (gen_blockage ());
}
void
thumb_expand_epilogue ()
{
HOST_WIDE_INT amount = (thumb_get_frame_size ()
+ current_function_outgoing_args_size);
if (IS_NAKED (arm_current_func_type ()))
return;
if (frame_pointer_needed)
emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
else if (amount)
{
amount = ROUND_UP (amount);
if (amount < 512)
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
GEN_INT (amount)));
else
{
rtx reg = gen_rtx (REG, SImode, LAST_ARG_REGNUM);
emit_insn (gen_movsi (reg, GEN_INT (amount)));
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, reg));
}
}
emit_insn (gen_prologue_use (stack_pointer_rtx));
if (current_function_profile || TARGET_NO_SCHED_PRO)
emit_insn (gen_blockage ());
}
static void
thumb_output_function_prologue (f, size)
FILE * f;
HOST_WIDE_INT size ATTRIBUTE_UNUSED;
{
int live_regs_mask = 0;
int high_regs_pushed = 0;
int regno;
if (IS_NAKED (arm_current_func_type ()))
return;
if (is_called_in_ARM_mode (current_function_decl))
{
const char * name;
if (GET_CODE (DECL_RTL (current_function_decl)) != MEM)
abort ();
if (GET_CODE (XEXP (DECL_RTL (current_function_decl), 0)) != SYMBOL_REF)
abort ();
name = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
asm_fprintf (f, "\torr\t%r, %r, #1\n", IP_REGNUM, PC_REGNUM);
asm_fprintf (f, "\tbx\t%r\n", IP_REGNUM);
#define STUB_NAME ".real_start_of"
fprintf (f, "\t.code\t16\n");
#ifdef ARM_PE
if (arm_dllexport_name_p (name))
name = arm_strip_name_encoding (name);
#endif
asm_fprintf (f, "\t.globl %s%U%s\n", STUB_NAME, name);
fprintf (f, "\t.thumb_func\n");
asm_fprintf (f, "%s%U%s:\n", STUB_NAME, name);
}
if (current_function_pretend_args_size)
{
if (cfun->machine->uses_anonymous_args)
{
int num_pushes;
fprintf (f, "\tpush\t{");
num_pushes = ARM_NUM_INTS (current_function_pretend_args_size);
for (regno = LAST_ARG_REGNUM + 1 - num_pushes;
regno <= LAST_ARG_REGNUM;
regno++)
asm_fprintf (f, "%r%s", regno,
regno == LAST_ARG_REGNUM ? "" : ", ");
fprintf (f, "}\n");
}
else
asm_fprintf (f, "\tsub\t%r, %r, #%d\n",
SP_REGNUM, SP_REGNUM,
current_function_pretend_args_size);
}
for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
if (THUMB_REG_PUSHED_P (regno))
live_regs_mask |= 1 << regno;
if (live_regs_mask || !leaf_function_p () || thumb_far_jump_used_p (1))
live_regs_mask |= 1 << LR_REGNUM;
if (TARGET_BACKTRACE)
{
int offset;
int work_register = 0;
int wr;
if ((live_regs_mask & 0xFF) == 0)
{
if (regs_ever_live [LAST_ARG_REGNUM] == 0)
work_register = LAST_ARG_REGNUM;
else
live_regs_mask |= (1 << LAST_LO_REGNUM);
}
if (work_register == 0)
{
for (work_register = (LAST_LO_REGNUM + 1); work_register--;)
if ((1 << work_register) & live_regs_mask)
break;
}
asm_fprintf
(f, "\tsub\t%r, %r, #16\t%@ Create stack backtrace structure\n",
SP_REGNUM, SP_REGNUM);
if (live_regs_mask)
thumb_pushpop (f, live_regs_mask, 1);
for (offset = 0, wr = 1 << 15; wr != 0; wr >>= 1)
if (wr & live_regs_mask)
offset += 4;
asm_fprintf (f, "\tadd\t%r, %r, #%d\n", work_register, SP_REGNUM,
offset + 16 + current_function_pretend_args_size);
asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM,
offset + 4);
if (live_regs_mask)
{
asm_fprintf (f, "\tmov\t%r, %r\n", work_register, PC_REGNUM);
asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM,
offset + 12);
asm_fprintf (f, "\tmov\t%r, %r\n", work_register,
ARM_HARD_FRAME_POINTER_REGNUM);
asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM,
offset);
}
else
{
asm_fprintf (f, "\tmov\t%r, %r\n", work_register,
ARM_HARD_FRAME_POINTER_REGNUM);
asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM,
offset);
asm_fprintf (f, "\tmov\t%r, %r\n", work_register, PC_REGNUM);
asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM,
offset + 12);
}
asm_fprintf (f, "\tmov\t%r, %r\n", work_register, LR_REGNUM);
asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM,
offset + 8);
asm_fprintf (f, "\tadd\t%r, %r, #%d\n", work_register, SP_REGNUM,
offset + 12);
asm_fprintf (f, "\tmov\t%r, %r\t\t%@ Backtrace structure created\n",
ARM_HARD_FRAME_POINTER_REGNUM, work_register);
}
else if (live_regs_mask)
thumb_pushpop (f, live_regs_mask, 1);
for (regno = 8; regno < 13; regno++)
if (THUMB_REG_PUSHED_P (regno))
high_regs_pushed++;
if (high_regs_pushed)
{
int pushable_regs = 0;
int mask = live_regs_mask & 0xff;
int next_hi_reg;
for (next_hi_reg = 12; next_hi_reg > LAST_LO_REGNUM; next_hi_reg--)
if (THUMB_REG_PUSHED_P (next_hi_reg))
break;
pushable_regs = mask;
if (pushable_regs == 0)
{
if (THUMB_REG_PUSHED_P (LAST_ARG_REGNUM))
asm_fprintf (f, "\tmov\t%r, %r\n", IP_REGNUM, LAST_ARG_REGNUM);
mask = 1 << LAST_ARG_REGNUM;
}
while (high_regs_pushed > 0)
{
for (regno = LAST_LO_REGNUM; regno >= 0; regno--)
{
if (mask & (1 << regno))
{
asm_fprintf (f, "\tmov\t%r, %r\n", regno, next_hi_reg);
high_regs_pushed--;
if (high_regs_pushed)
{
for (next_hi_reg--; next_hi_reg > LAST_LO_REGNUM;
next_hi_reg--)
if (THUMB_REG_PUSHED_P (next_hi_reg))
break;
}
else
{
mask &= ~((1 << regno) - 1);
break;
}
}
}
thumb_pushpop (f, mask, 1);
}
if (pushable_regs == 0
&& (THUMB_REG_PUSHED_P (LAST_ARG_REGNUM)))
asm_fprintf (f, "\tmov\t%r, %r\n", LAST_ARG_REGNUM, IP_REGNUM);
}
}
const char *
thumb_load_double_from_address (operands)
rtx *operands;
{
rtx addr;
rtx base;
rtx offset;
rtx arg1;
rtx arg2;
if (GET_CODE (operands[0]) != REG)
abort ();
if (GET_CODE (operands[1]) != MEM)
abort ();
addr = XEXP (operands[1], 0);
switch (GET_CODE (addr))
{
case REG:
operands[2] = gen_rtx (MEM, SImode,
plus_constant (XEXP (operands[1], 0), 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] = gen_rtx (MEM, SImode,
plus_constant (XEXP (operands[1], 0), 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;
if (GET_CODE (base) != REG)
abort ();
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] = gen_rtx (MEM, SImode,
plus_constant (XEXP (operands[1], 0), 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] = gen_rtx (MEM, SImode,
plus_constant (XEXP (operands[1], 0), 4));
output_asm_insn ("ldr\t%H0, %2", operands);
output_asm_insn ("ldr\t%0, %1", operands);
break;
default:
abort ();
break;
}
return "";
}
const char *
thumb_output_move_mem_multiple (n, operands)
int n;
rtx * operands;
{
rtx tmp;
switch (n)
{
case 2:
if (REGNO (operands[4]) > REGNO (operands[5]))
{
tmp = operands[4];
operands[4] = operands[5];
operands[5] = tmp;
}
output_asm_insn ("ldmia\t%1!, {%4, %5}", operands);
output_asm_insn ("stmia\t%0!, {%4, %5}", operands);
break;
case 3:
if (REGNO (operands[4]) > REGNO (operands[5]))
{
tmp = operands[4];
operands[4] = operands[5];
operands[5] = tmp;
}
if (REGNO (operands[5]) > REGNO (operands[6]))
{
tmp = operands[5];
operands[5] = operands[6];
operands[6] = tmp;
}
if (REGNO (operands[4]) > REGNO (operands[5]))
{
tmp = operands[4];
operands[4] = operands[5];
operands[5] = tmp;
}
output_asm_insn ("ldmia\t%1!, {%4, %5, %6}", operands);
output_asm_insn ("stmia\t%0!, {%4, %5, %6}", operands);
break;
default:
abort ();
}
return "";
}
void
thumb_expand_movstrqi (operands)
rtx * operands;
{
rtx out = copy_to_mode_reg (SImode, XEXP (operands[0], 0));
rtx in = copy_to_mode_reg (SImode, XEXP (operands[1], 0));
HOST_WIDE_INT len = INTVAL (operands[2]);
HOST_WIDE_INT offset = 0;
while (len >= 12)
{
emit_insn (gen_movmem12b (out, in, out, in));
len -= 12;
}
if (len >= 8)
{
emit_insn (gen_movmem8b (out, in, out, in));
len -= 8;
}
if (len >= 4)
{
rtx reg = gen_reg_rtx (SImode);
emit_insn (gen_movsi (reg, gen_rtx (MEM, SImode, in)));
emit_insn (gen_movsi (gen_rtx (MEM, SImode, out), reg));
len -= 4;
offset += 4;
}
if (len >= 2)
{
rtx reg = gen_reg_rtx (HImode);
emit_insn (gen_movhi (reg, gen_rtx (MEM, HImode,
plus_constant (in, offset))));
emit_insn (gen_movhi (gen_rtx (MEM, HImode, plus_constant (out, offset)),
reg));
len -= 2;
offset += 2;
}
if (len)
{
rtx reg = gen_reg_rtx (QImode);
emit_insn (gen_movqi (reg, gen_rtx (MEM, QImode,
plus_constant (in, offset))));
emit_insn (gen_movqi (gen_rtx (MEM, QImode, plus_constant (out, offset)),
reg));
}
}
int
thumb_cmp_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return ((GET_CODE (op) == CONST_INT
&& (unsigned HOST_WIDE_INT) (INTVAL (op)) < 256)
|| register_operand (op, mode));
}
static const char *
thumb_condition_code (x, invert)
rtx x;
int invert;
{
static const char * const conds[] =
{
"eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
"hi", "ls", "ge", "lt", "gt", "le"
};
int val;
switch (GET_CODE (x))
{
case EQ: val = 0; break;
case NE: val = 1; break;
case GEU: val = 2; break;
case LTU: val = 3; break;
case GTU: val = 8; break;
case LEU: val = 9; break;
case GE: val = 10; break;
case LT: val = 11; break;
case GT: val = 12; break;
case LE: val = 13; break;
default:
abort ();
}
return conds[val ^ invert];
}
void
thumb_reload_out_hi (operands)
rtx * operands;
{
emit_insn (gen_thumb_movhi_clobber (operands[0], operands[1], operands[2]));
}
void
thumb_reload_in_hi (operands)
rtx * operands ATTRIBUTE_UNUSED;
{
abort ();
}
static int
arm_get_strip_length (c)
int c;
{
switch (c)
{
ARM_NAME_ENCODING_LENGTHS
default: return 0;
}
}
const char *
arm_strip_name_encoding (name)
const char * name;
{
int skip;
while ((skip = arm_get_strip_length (* name)))
name += skip;
return name;
}
void
arm_asm_output_labelref (stream, name)
FILE * stream;
const char * name;
{
int skip;
int verbatim = 0;
while ((skip = arm_get_strip_length (* name)))
{
verbatim |= (*name == '*');
name += skip;
}
if (verbatim)
fputs (name, stream);
else
asm_fprintf (stream, "%U%s", name);
}
rtx aof_pic_label;
#ifdef AOF_ASSEMBLER
struct pic_chain
{
struct pic_chain * next;
const char * symname;
};
static struct pic_chain * aof_pic_chain = NULL;
rtx
aof_pic_entry (x)
rtx x;
{
struct pic_chain ** chainp;
int offset;
if (aof_pic_label == NULL_RTX)
{
aof_pic_label = gen_rtx_SYMBOL_REF (Pmode, "x$adcons");
}
for (offset = 0, chainp = &aof_pic_chain; *chainp;
offset += 4, chainp = &(*chainp)->next)
if ((*chainp)->symname == XSTR (x, 0))
return plus_constant (aof_pic_label, offset);
*chainp = (struct pic_chain *) xmalloc (sizeof (struct pic_chain));
(*chainp)->next = NULL;
(*chainp)->symname = XSTR (x, 0);
return plus_constant (aof_pic_label, offset);
}
void
aof_dump_pic_table (f)
FILE * f;
{
struct pic_chain * chain;
if (aof_pic_chain == NULL)
return;
asm_fprintf (f, "\tAREA |%r$$adcons|, BASED %r\n",
PIC_OFFSET_TABLE_REGNUM,
PIC_OFFSET_TABLE_REGNUM);
fputs ("|x$adcons|\n", f);
for (chain = aof_pic_chain; chain; chain = chain->next)
{
fputs ("\tDCD\t", f);
assemble_name (f, chain->symname);
fputs ("\n", f);
}
}
int arm_text_section_count = 1;
char *
aof_text_section ()
{
static char buf[100];
sprintf (buf, "\tAREA |C$$code%d|, CODE, READONLY",
arm_text_section_count++);
if (flag_pic)
strcat (buf, ", PIC, REENTRANT");
return buf;
}
static int arm_data_section_count = 1;
char *
aof_data_section ()
{
static char buf[100];
sprintf (buf, "\tAREA |C$$data%d|, DATA", arm_data_section_count++);
return buf;
}
struct import
{
struct import * next;
const char * name;
};
static struct import * imports_list = NULL;
void
aof_add_import (name)
const char * name;
{
struct import * new;
for (new = imports_list; new; new = new->next)
if (new->name == name)
return;
new = (struct import *) xmalloc (sizeof (struct import));
new->next = imports_list;
imports_list = new;
new->name = name;
}
void
aof_delete_import (name)
const char * name;
{
struct import ** old;
for (old = &imports_list; *old; old = & (*old)->next)
{
if ((*old)->name == name)
{
*old = (*old)->next;
return;
}
}
}
int arm_main_function = 0;
void
aof_dump_imports (f)
FILE * f;
{
if (arm_main_function)
{
text_section ();
fputs ("\tIMPORT __main\n", f);
fputs ("\tDCD __main\n", f);
}
while (imports_list)
{
fprintf (f, "\tIMPORT\t");
assemble_name (f, imports_list->name);
fputc ('\n', f);
imports_list = imports_list->next;
}
}
static void
aof_globalize_label (stream, name)
FILE *stream;
const char *name;
{
default_globalize_label (stream, name);
if (! strcmp (name, "main"))
arm_main_function = 1;
}
#endif
#ifdef OBJECT_FORMAT_ELF
static void
arm_elf_asm_named_section (name, flags)
const char *name;
unsigned int flags;
{
char flagchars[10], *f = flagchars;
if (! named_section_first_declaration (name))
{
fprintf (asm_out_file, "\t.section\t%s\n", name);
return;
}
if (!(flags & SECTION_DEBUG))
*f++ = 'a';
if (flags & SECTION_WRITE)
*f++ = 'w';
if (flags & SECTION_CODE)
*f++ = 'x';
if (flags & SECTION_SMALL)
*f++ = 's';
if (flags & SECTION_MERGE)
*f++ = 'M';
if (flags & SECTION_STRINGS)
*f++ = 'S';
if (flags & SECTION_TLS)
*f++ = 'T';
*f = '\0';
fprintf (asm_out_file, "\t.section\t%s,\"%s\"", name, flagchars);
if (!(flags & SECTION_NOTYPE))
{
const char *type;
if (flags & SECTION_BSS)
type = "nobits";
else
type = "progbits";
fprintf (asm_out_file, ",%%%s", type);
if (flags & SECTION_ENTSIZE)
fprintf (asm_out_file, ",%d", flags & SECTION_ENTSIZE);
}
putc ('\n', asm_out_file);
}
#endif
#ifndef ARM_PE
static void
arm_encode_section_info (decl, first)
tree decl;
int first;
{
#ifndef AOF_ASSEMBLER
if (optimize > 0 && TREE_CONSTANT (decl)
&& (!flag_writable_strings || TREE_CODE (decl) != STRING_CST))
{
rtx rtl = (TREE_CODE_CLASS (TREE_CODE (decl)) != 'd'
? TREE_CST_RTL (decl) : DECL_RTL (decl));
SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1;
}
#endif
if (first && TREE_CODE_CLASS (TREE_CODE (decl)) == 'd')
{
if (TREE_CODE (decl) == FUNCTION_DECL && DECL_WEAK (decl))
arm_encode_call_attribute (decl, LONG_CALL_FLAG_CHAR);
else if (! TREE_PUBLIC (decl))
arm_encode_call_attribute (decl, SHORT_CALL_FLAG_CHAR);
}
}
#endif
static void
arm_output_mi_thunk (file, thunk, delta, vcall_offset, function)
FILE *file;
tree thunk ATTRIBUTE_UNUSED;
HOST_WIDE_INT delta;
HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED;
tree function;
{
int mi_delta = delta;
const char *const mi_op = mi_delta < 0 ? "sub" : "add";
int shift = 0;
int this_regno = (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)))
? 1 : 0);
if (mi_delta < 0)
mi_delta = - mi_delta;
while (mi_delta != 0)
{
if ((mi_delta & (3 << shift)) == 0)
shift += 2;
else
{
asm_fprintf (file, "\t%s\t%r, %r, #%d\n",
mi_op, this_regno, this_regno,
mi_delta & (0xff << shift));
mi_delta &= ~(0xff << shift);
shift += 8;
}
}
fputs ("\tb\t", file);
assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
if (NEED_PLT_RELOC)
fputs ("(PLT)", file);
fputc ('\n', file);
}