#include "config.h"
#include "system.h"
#include <signal.h>
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "insn-attr.h"
#include "recog.h"
#include "toplev.h"
#include "output.h"
#include "tree.h"
#include "function.h"
#include "expr.h"
#include "flags.h"
#include "reload.h"
#include "output.h"
#include "tm_p.h"
#include "ggc.h"
#include "gstab.h"
#include "hashtab.h"
#include "debug.h"
#include "target.h"
#include "target-def.h"
#ifdef __GNU_STAB__
#define STAB_CODE_TYPE enum __stab_debug_code
#else
#define STAB_CODE_TYPE int
#endif
extern tree lookup_name PARAMS ((tree));
enum internal_test {
ITEST_EQ,
ITEST_NE,
ITEST_GT,
ITEST_GE,
ITEST_LT,
ITEST_LE,
ITEST_GTU,
ITEST_GEU,
ITEST_LTU,
ITEST_LEU,
ITEST_MAX
};
struct constant;
struct mips_arg_info;
static enum internal_test map_test_to_internal_test PARAMS ((enum rtx_code));
static void get_float_compare_codes PARAMS ((enum rtx_code, enum rtx_code *,
enum rtx_code *));
static int mips16_simple_memory_operand PARAMS ((rtx, rtx,
enum machine_mode));
static int m16_check_op PARAMS ((rtx, int, int, int));
static void block_move_loop PARAMS ((rtx, rtx,
unsigned int,
int,
rtx, rtx));
static void block_move_call PARAMS ((rtx, rtx, rtx));
static void mips_arg_info PARAMS ((const CUMULATIVE_ARGS *,
enum machine_mode,
tree, int,
struct mips_arg_info *));
static rtx mips_add_large_offset_to_sp PARAMS ((HOST_WIDE_INT));
static void mips_annotate_frame_insn PARAMS ((rtx, rtx));
static rtx mips_frame_set PARAMS ((enum machine_mode,
int, int));
static void mips_emit_frame_related_store PARAMS ((rtx, rtx,
HOST_WIDE_INT));
static void save_restore_insns PARAMS ((int, rtx, long));
static void mips16_output_gp_offset PARAMS ((FILE *, rtx));
static void mips16_fp_args PARAMS ((FILE *, int, int));
static void build_mips16_function_stub PARAMS ((FILE *));
static void mips16_optimize_gp PARAMS ((rtx));
static rtx add_constant PARAMS ((struct constant **,
rtx,
enum machine_mode));
static void dump_constants PARAMS ((struct constant *,
rtx));
static rtx mips_find_symbol PARAMS ((rtx));
static void abort_with_insn PARAMS ((rtx, const char *))
ATTRIBUTE_NORETURN;
static int symbolic_expression_p PARAMS ((rtx));
static bool mips_assemble_integer PARAMS ((rtx, unsigned int, int));
static void mips_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
static void mips_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
static void mips_set_architecture PARAMS ((const struct mips_cpu_info *));
static void mips_set_tune PARAMS ((const struct mips_cpu_info *));
static bool mips_strict_matching_cpu_name_p PARAMS ((const char *,
const char *));
static bool mips_matching_cpu_name_p PARAMS ((const char *,
const char *));
static const struct mips_cpu_info *mips_parse_cpu PARAMS ((const char *,
const char *));
static const struct mips_cpu_info *mips_cpu_info_from_isa PARAMS ((int));
static void copy_file_data PARAMS ((FILE *, FILE *));
#ifdef TARGET_IRIX6
static void iris6_asm_named_section_1 PARAMS ((const char *,
unsigned int,
unsigned int));
static void iris6_asm_named_section PARAMS ((const char *,
unsigned int));
static int iris_section_align_entry_eq PARAMS ((const PTR, const PTR));
static hashval_t iris_section_align_entry_hash PARAMS ((const PTR));
static int iris6_section_align_1 PARAMS ((void **, void *));
#endif
static int mips_adjust_cost PARAMS ((rtx, rtx, rtx, int));
static int mips_issue_rate PARAMS ((void));
static struct machine_function * mips_init_machine_status PARAMS ((void));
static void mips_select_section PARAMS ((tree, int, unsigned HOST_WIDE_INT))
ATTRIBUTE_UNUSED;
static void mips_unique_section PARAMS ((tree, int))
ATTRIBUTE_UNUSED;
static void mips_select_rtx_section PARAMS ((enum machine_mode, rtx,
unsigned HOST_WIDE_INT));
static int mips_use_dfa_pipeline_interface PARAMS ((void));
static void mips_encode_section_info PARAMS ((tree, int));
struct mips_frame_info GTY(())
{
long total_size;
long var_size;
long args_size;
long extra_size;
int gp_reg_size;
int fp_reg_size;
long mask;
long fmask;
long gp_save_offset;
long fp_save_offset;
long gp_sp_offset;
long fp_sp_offset;
int initialized;
int num_gp;
int num_fp;
};
struct machine_function GTY(()) {
rtx embedded_pic_fnaddr_rtx;
rtx mips16_gp_pseudo_rtx;
struct mips_frame_info frame;
long insns_len;
};
struct mips_arg_info
{
bool struct_p;
bool fpr_p;
unsigned int num_bytes;
unsigned int reg_words;
unsigned int reg_offset;
unsigned int stack_words;
unsigned int stack_offset;
};
int mips_section_threshold = -1;
int num_source_filenames = 0;
int sdb_label_count = 0;
int sym_lineno = 0;
int inside_function = 0;
FILE *asm_out_data_file;
FILE *asm_out_text_file;
struct extern_list
{
struct extern_list *next;
const char *name;
int size;
} *extern_head = 0;
const char *current_function_file = "";
int file_in_function_warning = FALSE;
int ignore_line_number = FALSE;
int set_noreorder;
int set_noat;
int set_nomacro;
int set_volatile;
int mips_branch_likely;
int dslots_load_total;
int dslots_load_filled;
int dslots_jump_total;
int dslots_jump_filled;
int dslots_number_nops;
int num_refs[3];
rtx mips_load_reg, mips_load_reg2, mips_load_reg3, mips_load_reg4;
rtx branch_cmp[2];
enum cmp_type branch_type;
enum processor_type mips_arch;
const struct mips_cpu_info *mips_arch_info;
enum processor_type mips_tune;
const struct mips_cpu_info *mips_tune_info;
int mips_isa;
int mips_abi;
const char *mips_arch_string;
const char *mips_tune_string;
const char *mips_isa_string;
const char *mips_abi_string;
int mips16;
const char *mips_no_mips16_string;
int mips16_hard_float;
const char *mips_entry_string;
const char *mips_cache_flush_func = CACHE_FLUSH_FUNC;
int mips_entry;
int mips_split_addresses;
enum mips_abicalls_type mips_abicalls;
static enum machine_mode gpr_mode;
char mips_hard_regno_mode_ok[(int)MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
int mips_string_length;
static GTY(()) rtx mips16_strings;
struct string_constant
{
struct string_constant *next;
const char *label;
};
static struct string_constant *string_constants;
char mips_print_operand_punct[256];
int mips_dbx_regno[FIRST_PSEUDO_REGISTER];
static char volatile_buffer[60];
char mips_reg_names[][8] =
{
"$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7",
"$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
"$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
"$24", "$25", "$26", "$27", "$28", "$sp", "$fp", "$31",
"$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
"$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
"$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
"$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
"hi", "lo", "accum","$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",
"$fcc5","$fcc6","$fcc7","$rap", "", "", "", "",
"$c0r0", "$c0r1", "$c0r2", "$c0r3", "$c0r4", "$c0r5", "$c0r6", "$c0r7",
"$c0r8", "$c0r9", "$c0r10","$c0r11","$c0r12","$c0r13","$c0r14","$c0r15",
"$c0r16","$c0r17","$c0r18","$c0r19","$c0r20","$c0r21","$c0r22","$c0r23",
"$c0r24","$c0r25","$c0r26","$c0r27","$c0r28","$c0r29","$c0r30","$c0r31",
"$c2r0", "$c2r1", "$c2r2", "$c2r3", "$c2r4", "$c2r5", "$c2r6", "$c2r7",
"$c2r8", "$c2r9", "$c2r10","$c2r11","$c2r12","$c2r13","$c2r14","$c2r15",
"$c2r16","$c2r17","$c2r18","$c2r19","$c2r20","$c2r21","$c2r22","$c2r23",
"$c2r24","$c2r25","$c2r26","$c2r27","$c2r28","$c2r29","$c2r30","$c2r31",
"$c3r0", "$c3r1", "$c3r2", "$c3r3", "$c3r4", "$c3r5", "$c3r6", "$c3r7",
"$c3r8", "$c3r9", "$c3r10","$c3r11","$c3r12","$c3r13","$c3r14","$c3r15",
"$c3r16","$c3r17","$c3r18","$c3r19","$c3r20","$c3r21","$c3r22","$c3r23",
"$c3r24","$c3r25","$c3r26","$c3r27","$c3r28","$c3r29","$c3r30","$c3r31"
};
char mips_sw_reg_names[][8] =
{
"$zero","$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3",
"$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
"$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7",
"$t8", "$t9", "$k0", "$k1", "$gp", "$sp", "$fp", "$ra",
"$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
"$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
"$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
"$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
"hi", "lo", "accum","$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",
"$fcc5","$fcc6","$fcc7","$rap", "", "", "", "",
"$c0r0", "$c0r1", "$c0r2", "$c0r3", "$c0r4", "$c0r5", "$c0r6", "$c0r7",
"$c0r8", "$c0r9", "$c0r10","$c0r11","$c0r12","$c0r13","$c0r14","$c0r15",
"$c0r16","$c0r17","$c0r18","$c0r19","$c0r20","$c0r21","$c0r22","$c0r23",
"$c0r24","$c0r25","$c0r26","$c0r27","$c0r28","$c0r29","$c0r30","$c0r31",
"$c2r0", "$c2r1", "$c2r2", "$c2r3", "$c2r4", "$c2r5", "$c2r6", "$c2r7",
"$c2r8", "$c2r9", "$c2r10","$c2r11","$c2r12","$c2r13","$c2r14","$c2r15",
"$c2r16","$c2r17","$c2r18","$c2r19","$c2r20","$c2r21","$c2r22","$c2r23",
"$c2r24","$c2r25","$c2r26","$c2r27","$c2r28","$c2r29","$c2r30","$c2r31",
"$c3r0", "$c3r1", "$c3r2", "$c3r3", "$c3r4", "$c3r5", "$c3r6", "$c3r7",
"$c3r8", "$c3r9", "$c3r10","$c3r11","$c3r12","$c3r13","$c3r14","$c3r15",
"$c3r16","$c3r17","$c3r18","$c3r19","$c3r20","$c3r21","$c3r22","$c3r23",
"$c3r24","$c3r25","$c3r26","$c3r27","$c3r28","$c3r29","$c3r30","$c3r31"
};
const enum reg_class mips_regno_to_class[] =
{
GR_REGS, GR_REGS, M16_NA_REGS, M16_NA_REGS,
M16_REGS, M16_REGS, M16_REGS, M16_REGS,
GR_REGS, GR_REGS, GR_REGS, GR_REGS,
GR_REGS, GR_REGS, GR_REGS, GR_REGS,
M16_NA_REGS, M16_NA_REGS, GR_REGS, GR_REGS,
GR_REGS, GR_REGS, GR_REGS, GR_REGS,
T_REG, GR_REGS, GR_REGS, GR_REGS,
GR_REGS, GR_REGS, GR_REGS, GR_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
HI_REG, LO_REG, HILO_REG, ST_REGS,
ST_REGS, ST_REGS, ST_REGS, ST_REGS,
ST_REGS, ST_REGS, ST_REGS, GR_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS
};
enum reg_class mips_char_to_class[256] =
{
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
};
const struct mips_cpu_info mips_cpu_info_table[] = {
{ "mips1", PROCESSOR_R3000, 1 },
{ "mips2", PROCESSOR_R6000, 2 },
{ "mips3", PROCESSOR_R4000, 3 },
{ "mips4", PROCESSOR_R8000, 4 },
{ "mips32", PROCESSOR_R4KC, 32 },
{ "mips64", PROCESSOR_R5KC, 64 },
{ "r3000", PROCESSOR_R3000, 1 },
{ "r2000", PROCESSOR_R3000, 1 },
{ "r3900", PROCESSOR_R3900, 1 },
{ "r6000", PROCESSOR_R6000, 2 },
{ "r4000", PROCESSOR_R4000, 3 },
{ "vr4100", PROCESSOR_R4100, 3 },
{ "vr4111", PROCESSOR_R4111, 3 },
{ "vr4120", PROCESSOR_R4120, 3 },
{ "vr4300", PROCESSOR_R4300, 3 },
{ "r4400", PROCESSOR_R4000, 3 },
{ "r4600", PROCESSOR_R4600, 3 },
{ "orion", PROCESSOR_R4600, 3 },
{ "r4650", PROCESSOR_R4650, 3 },
{ "r8000", PROCESSOR_R8000, 4 },
{ "vr5000", PROCESSOR_R5000, 4 },
{ "vr5400", PROCESSOR_R5400, 4 },
{ "vr5500", PROCESSOR_R5500, 4 },
{ "4kc", PROCESSOR_R4KC, 32 },
{ "4kp", PROCESSOR_R4KC, 32 },
{ "5kc", PROCESSOR_R5KC, 64 },
{ "20kc", PROCESSOR_R20KC, 64 },
{ "sr71000", PROCESSOR_SR71000, 64 },
{ "sb1", PROCESSOR_SB1, 64 },
{ 0, 0, 0 }
};
#ifndef MIPS_MARCH_CONTROLS_SOFT_FLOAT
#define MIPS_MARCH_CONTROLS_SOFT_FLOAT 0
#endif
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
#undef TARGET_ASM_ALIGNED_SI_OP
#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
#undef TARGET_ASM_INTEGER
#define TARGET_ASM_INTEGER mips_assemble_integer
#if TARGET_IRIX5 && !TARGET_IRIX6
#undef TARGET_ASM_UNALIGNED_HI_OP
#define TARGET_ASM_UNALIGNED_HI_OP "\t.align 0\n\t.half\t"
#undef TARGET_ASM_UNALIGNED_SI_OP
#define TARGET_ASM_UNALIGNED_SI_OP "\t.align 0\n\t.word\t"
#undef TARGET_ASM_UNALIGNED_DI_OP
#define TARGET_ASM_UNALIGNED_DI_OP "\t.align 0\n\t.dword\t"
#endif
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE mips_output_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE mips_output_function_epilogue
#undef TARGET_ASM_SELECT_RTX_SECTION
#define TARGET_ASM_SELECT_RTX_SECTION mips_select_rtx_section
#undef TARGET_SCHED_ADJUST_COST
#define TARGET_SCHED_ADJUST_COST mips_adjust_cost
#undef TARGET_SCHED_ISSUE_RATE
#define TARGET_SCHED_ISSUE_RATE mips_issue_rate
#undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE
#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE mips_use_dfa_pipeline_interface
#undef TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO mips_encode_section_info
struct gcc_target targetm = TARGET_INITIALIZER;
int
uns_arith_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == CONST_INT && SMALL_INT_UNSIGNED (op))
return 1;
return register_operand (op, mode);
}
int
arith_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == CONST_INT && SMALL_INT (op))
return 1;
if (TARGET_MIPS16 && GET_CODE (op) == CONST && mips16_gp_offset_p (op))
return 1;
return register_operand (op, mode);
}
int
arith32_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == CONST_INT)
return 1;
return register_operand (op, mode);
}
int
small_int (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return (GET_CODE (op) == CONST_INT && SMALL_INT (op));
}
int
large_int (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
HOST_WIDE_INT value;
if (GET_CODE (op) != CONST_INT)
return 0;
value = INTVAL (op);
if ((value & ~ ((HOST_WIDE_INT) 0x0000ffff)) == 0)
return 0;
if (((unsigned HOST_WIDE_INT) (value + 32768)) <= 32767)
return 0;
if ((value & 0x0000ffff) == 0)
return 0;
return 1;
}
int
reg_or_0_operand (op, mode)
rtx op;
enum machine_mode mode;
{
switch (GET_CODE (op))
{
case CONST_INT:
if (TARGET_MIPS16)
return 0;
return INTVAL (op) == 0;
case CONST_DOUBLE:
if (TARGET_MIPS16)
return 0;
return op == CONST0_RTX (mode);
case REG:
case SUBREG:
return register_operand (op, mode);
default:
break;
}
return 0;
}
int
true_reg_or_0_operand (op, mode)
rtx op;
enum machine_mode mode;
{
switch (GET_CODE (op))
{
case CONST_INT:
return INTVAL (op) == 0;
case CONST_DOUBLE:
return op == CONST0_RTX (mode);
case REG:
case SUBREG:
return register_operand (op, mode);
default:
break;
}
return 0;
}
int
mips_const_double_ok (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) != CONST_DOUBLE)
return 0;
if (mode == VOIDmode)
return 1;
if (TARGET_MIPS16)
return 0;
if (mode != SFmode && mode != DFmode)
return 0;
if (op == CONST0_RTX (mode))
return 1;
return 0;
}
int
const_float_1_operand (op, mode)
rtx op;
enum machine_mode mode;
{
REAL_VALUE_TYPE d;
if (GET_CODE (op) != CONST_DOUBLE
|| mode != GET_MODE (op)
|| (mode != DFmode && mode != SFmode))
return 0;
REAL_VALUE_FROM_CONST_DOUBLE (d, op);
return REAL_VALUES_EQUAL (d, dconst1);
}
static int
mips16_simple_memory_operand (reg, offset, mode)
rtx reg;
rtx offset;
enum machine_mode mode;
{
unsigned int size;
int off;
if (mode == BLKmode)
{
return 0;
}
size = GET_MODE_SIZE (mode);
if (INTVAL (offset) % size != 0)
return 0;
if (REGNO (reg) == STACK_POINTER_REGNUM && GET_MODE_SIZE (mode) == 4)
off = 0x100;
else
off = 0x20;
if (INTVAL (offset) >= 0 && INTVAL (offset) < (HOST_WIDE_INT)(off * size))
return 1;
return 0;
}
int
simple_memory_operand (op, mode)
rtx op;
enum machine_mode mode;
{
rtx addr, plus0, plus1;
if (GET_CODE (op) != MEM)
return 0;
if (GET_MODE_SIZE (GET_MODE (op)) > (unsigned) UNITS_PER_WORD)
return 0;
addr = XEXP (op, 0);
switch (GET_CODE (addr))
{
case REG:
case LO_SUM:
return 1;
case CONST_INT:
if (TARGET_MIPS16)
return 0;
return SMALL_INT (addr);
case PLUS:
plus0 = XEXP (addr, 0);
plus1 = XEXP (addr, 1);
if (GET_CODE (plus0) == REG
&& GET_CODE (plus1) == CONST_INT && SMALL_INT (plus1)
&& (! TARGET_MIPS16
|| mips16_simple_memory_operand (plus0, plus1, mode)))
return 1;
else if (GET_CODE (plus1) == REG
&& GET_CODE (plus0) == CONST_INT && SMALL_INT (plus0)
&& (! TARGET_MIPS16
|| mips16_simple_memory_operand (plus1, plus0, mode)))
return 1;
else
return 0;
#if 0
case LABEL_REF:
break;
case CONST:
if (!TARGET_GP_OPT)
return 0;
{
rtx offset = const0_rtx;
addr = eliminate_constant_term (XEXP (addr, 0), &offset);
if (GET_CODE (op) != SYMBOL_REF)
return 0;
if (! SMALL_INT (offset))
return 0;
}
case SYMBOL_REF:
return SYMBOL_REF_FLAG (addr);
#endif
case SYMBOL_REF:
if (TARGET_MIPS16
&& CONSTANT_POOL_ADDRESS_P (addr)
&& cfun->machine->insns_len > 0)
{
long size;
size = cfun->machine->insns_len + get_pool_size ();
if (GET_MODE_SIZE (mode) == 4)
return size < 4 * 0x100;
else if (GET_MODE_SIZE (mode) == 8)
return size < 8 * 0x20;
else
return 0;
}
return 0;
default:
break;
}
return 0;
}
int
double_memory_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) != MEM
|| ! memory_operand (op, mode))
{
if (reload_in_progress
&& GET_CODE (op) == REG
&& REGNO (op) >= FIRST_PSEUDO_REGISTER
&& reg_renumber[REGNO (op)] < 0
&& reg_equiv_mem[REGNO (op)] != 0
&& double_memory_operand (reg_equiv_mem[REGNO (op)], mode))
return 1;
if (reload_in_progress
&& TARGET_64BIT
&& (GET_CODE (op) == MEM
|| (GET_CODE (op) == REG
&& REGNO (op) >= FIRST_PSEUDO_REGISTER
&& reg_renumber[REGNO (op)] < 0)))
return 1;
if (reload_in_progress
&& TARGET_MIPS16
&& GET_CODE (op) == MEM)
{
rtx addr;
addr = XEXP (op, 0);
if (GET_CODE (addr) == PLUS
&& GET_CODE (XEXP (addr, 0)) == REG
&& (REGNO (XEXP (addr, 0)) == (unsigned) HARD_FRAME_POINTER_REGNUM
|| REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
&& ((GET_CODE (XEXP (addr, 1)) == CONST_INT
&& ! SMALL_INT (XEXP (addr, 1)))
|| (GET_CODE (XEXP (addr, 1)) == SYMBOL_REF
&& CONSTANT_POOL_ADDRESS_P (XEXP (addr, 1)))))
return 1;
if (GET_CODE (addr) == MEM)
{
rtx maddr;
maddr = XEXP (addr, 0);
if (GET_CODE (maddr) == PLUS
&& GET_CODE (XEXP (maddr, 0)) == REG
&& (REGNO (XEXP (maddr, 0)) == (unsigned) HARD_FRAME_POINTER_REGNUM
|| REGNO (XEXP (maddr, 0)) == STACK_POINTER_REGNUM)
&& ((GET_CODE (XEXP (maddr, 1)) == CONST_INT
&& ! SMALL_INT (XEXP (maddr, 1)))
|| (GET_CODE (XEXP (maddr, 1)) == SYMBOL_REF
&& CONSTANT_POOL_ADDRESS_P (XEXP (maddr, 1)))))
return 1;
}
if (GET_CODE (addr) == PLUS
&& GET_CODE (XEXP (addr, 0)) == MEM
&& GET_CODE (XEXP (addr, 1)) == CONST_INT
&& SMALL_INT (XEXP (addr, 1)))
{
addr = XEXP (XEXP (addr, 0), 0);
if (GET_CODE (addr) == PLUS
&& GET_CODE (XEXP (addr, 0)) == REG
&& (REGNO (XEXP (addr, 0)) == (unsigned) HARD_FRAME_POINTER_REGNUM
|| REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
&& ((GET_CODE (XEXP (addr, 1)) == CONST_INT
&& ! SMALL_INT (XEXP (addr, 1)))
|| (GET_CODE (XEXP (addr, 1)) == SYMBOL_REF
&& CONSTANT_POOL_ADDRESS_P (XEXP (addr, 1)))))
return 1;
}
}
return 0;
}
if (TARGET_64BIT)
{
return 1;
}
if (CONSTANT_ADDRESS_P (XEXP (op, 0)))
return 1;
op = adjust_address_nv (op, GET_MODE_CLASS (mode) == MODE_INT
? SImode : SFmode, 4);
return memory_address_p (GET_MODE (op), XEXP (op, 0));
}
int
equality_op (op, mode)
rtx op;
enum machine_mode mode;
{
if (mode != GET_MODE (op))
return 0;
return GET_CODE (op) == EQ || GET_CODE (op) == NE;
}
int
cmp_op (op, mode)
rtx op;
enum machine_mode mode;
{
if (mode != GET_MODE (op))
return 0;
return GET_RTX_CLASS (GET_CODE (op)) == '<';
}
int
trap_cmp_op (op, mode)
rtx op;
enum machine_mode mode;
{
if (mode != GET_MODE (op))
return 0;
switch (GET_CODE (op))
{
case EQ:
case NE:
case LT:
case LTU:
case GE:
case GEU:
return 1;
default:
return 0;
}
}
int
pc_or_label_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (op == pc_rtx)
return 1;
if (GET_CODE (op) == LABEL_REF)
return 1;
return 0;
}
int
call_insn_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return (CONSTANT_ADDRESS_P (op)
|| (GET_CODE (op) == REG && op != arg_pointer_rtx
&& ! (REGNO (op) >= FIRST_PSEUDO_REGISTER
&& REGNO (op) <= LAST_VIRTUAL_REGISTER)));
}
int
move_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (general_operand (op, mode)
&& (! (mips_split_addresses && mips_check_split (op, mode))
|| reload_in_progress || reload_completed)
&& ! (TARGET_MIPS16
&& GET_CODE (op) == SYMBOL_REF
&& ! mips16_constant (op, mode, 1, 0)));
}
int
movdi_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (TARGET_64BIT
&& mode == DImode
&& GET_CODE (op) == SIGN_EXTEND
&& GET_MODE (op) == DImode
&& move_operand (XEXP (op, 0), SImode))
return 1;
return (general_operand (op, mode)
&& ! (TARGET_MIPS16
&& GET_CODE (op) == SYMBOL_REF
&& ! mips16_constant (op, mode, 1, 0)));
}
int
se_register_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (TARGET_64BIT
&& mode == DImode
&& GET_CODE (op) == SIGN_EXTEND
&& GET_MODE (op) == DImode
&& GET_MODE (XEXP (op, 0)) == SImode
&& register_operand (XEXP (op, 0), SImode))
return 1;
return register_operand (op, mode);
}
int
se_reg_or_0_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (TARGET_64BIT
&& mode == DImode
&& GET_CODE (op) == SIGN_EXTEND
&& GET_MODE (op) == DImode
&& GET_MODE (XEXP (op, 0)) == SImode
&& register_operand (XEXP (op, 0), SImode))
return 1;
return reg_or_0_operand (op, mode);
}
int
se_uns_arith_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (TARGET_64BIT
&& mode == DImode
&& GET_CODE (op) == SIGN_EXTEND
&& GET_MODE (op) == DImode
&& GET_MODE (XEXP (op, 0)) == SImode
&& register_operand (XEXP (op, 0), SImode))
return 1;
return uns_arith_operand (op, mode);
}
int
se_arith_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (TARGET_64BIT
&& mode == DImode
&& GET_CODE (op) == SIGN_EXTEND
&& GET_MODE (op) == DImode
&& GET_MODE (XEXP (op, 0)) == SImode
&& register_operand (XEXP (op, 0), SImode))
return 1;
return arith_operand (op, mode);
}
int
se_nonmemory_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (TARGET_64BIT
&& mode == DImode
&& GET_CODE (op) == SIGN_EXTEND
&& GET_MODE (op) == DImode
&& GET_MODE (XEXP (op, 0)) == SImode
&& register_operand (XEXP (op, 0), SImode))
return 1;
return nonmemory_operand (op, mode);
}
int
consttable_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return CONSTANT_P (op);
}
int
coprocessor_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return (GET_CODE (op) == REG
&& COP0_REG_FIRST <= REGNO (op)
&& REGNO (op) <= COP3_REG_LAST);
}
int
coprocessor2_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return (GET_CODE (op) == REG
&& COP2_REG_FIRST <= REGNO (op)
&& REGNO (op) <= COP2_REG_LAST);
}
int
symbolic_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op))
return 0;
if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)
return 1;
if (GET_CODE (op) == CONST
&& GET_CODE (XEXP (op,0)) == PLUS
&& GET_CODE (XEXP (XEXP (op,0), 0)) == SYMBOL_REF
&& GET_CODE (XEXP (XEXP (op,0), 1)) == CONST_INT)
return 1;
return 0;
}
int
mips_check_split (address, mode)
rtx address;
enum machine_mode mode;
{
if (GET_MODE_SIZE (mode) > (unsigned) UNITS_PER_WORD)
return 0;
if ((GET_CODE (address) == SYMBOL_REF && ! SYMBOL_REF_FLAG (address))
|| (GET_CODE (address) == CONST
&& GET_CODE (XEXP (XEXP (address, 0), 0)) == SYMBOL_REF
&& ! SYMBOL_REF_FLAG (XEXP (XEXP (address, 0), 0)))
|| GET_CODE (address) == LABEL_REF)
return 1;
return 0;
}
int
mips_reg_mode_ok_for_base_p (reg, mode, strict)
rtx reg;
enum machine_mode mode;
int strict;
{
return (strict
? REGNO_MODE_OK_FOR_BASE_P (REGNO (reg), mode)
: GP_REG_OR_PSEUDO_NONSTRICT_P (REGNO (reg), mode));
}
int
mips_legitimate_address_p (mode, xinsn, strict)
enum machine_mode mode;
rtx xinsn;
int strict;
{
if (TARGET_DEBUG_B_MODE)
{
GO_PRINTF2 ("\n========== GO_IF_LEGITIMATE_ADDRESS, %sstrict\n",
strict ? "" : "not ");
GO_DEBUG_RTX (xinsn);
}
if (CONSTANT_ADDRESS_P (xinsn)
&& ! (mips_split_addresses && mips_check_split (xinsn, mode))
&& (! TARGET_MIPS16 || mips16_constant (xinsn, mode, 1, 0)))
return 1;
while (GET_CODE (xinsn) == SUBREG)
xinsn = SUBREG_REG (xinsn);
if (GET_CODE (xinsn) == REG
&& mips_reg_mode_ok_for_base_p (xinsn, mode, strict))
return 1;
if (GET_CODE (xinsn) == LO_SUM && mips_split_addresses)
{
register rtx xlow0 = XEXP (xinsn, 0);
register rtx xlow1 = XEXP (xinsn, 1);
while (GET_CODE (xlow0) == SUBREG)
xlow0 = SUBREG_REG (xlow0);
if (GET_CODE (xlow0) == REG
&& mips_reg_mode_ok_for_base_p (xlow0, mode, strict)
&& mips_check_split (xlow1, mode))
return 1;
}
if (GET_CODE (xinsn) == PLUS)
{
register rtx xplus0 = XEXP (xinsn, 0);
register rtx xplus1 = XEXP (xinsn, 1);
register enum rtx_code code0;
register enum rtx_code code1;
while (GET_CODE (xplus0) == SUBREG)
xplus0 = SUBREG_REG (xplus0);
code0 = GET_CODE (xplus0);
while (GET_CODE (xplus1) == SUBREG)
xplus1 = SUBREG_REG (xplus1);
code1 = GET_CODE (xplus1);
if (code0 == REG
&& mips_reg_mode_ok_for_base_p (xplus0, mode, strict))
{
if (code1 == CONST_INT && SMALL_INT (xplus1))
return 1;
if (TARGET_MIPS16
&& mips16_gp_offset_p (xplus1))
return 1;
if (!TARGET_DEBUG_A_MODE
&& (mips_abi == ABI_32
|| mips_abi == ABI_O64
|| mips_abi == ABI_EABI)
&& CONSTANT_ADDRESS_P (xplus1)
&& ! mips_split_addresses
&& (!TARGET_EMBEDDED_PIC
|| code1 != CONST
|| GET_CODE (XEXP (xplus1, 0)) != MINUS)
&& (!TARGET_64BIT
|| (code1 == CONST_INT
&& trunc_int_for_mode (INTVAL (xplus1),
SImode) == INTVAL (xplus1)))
&& !TARGET_MIPS16)
return 1;
}
}
if (TARGET_DEBUG_B_MODE)
GO_PRINTF ("Not a legitimate address\n");
return 0;
}
static int
m16_check_op (op, low, high, mask)
rtx op;
int low;
int high;
int mask;
{
return (GET_CODE (op) == CONST_INT
&& INTVAL (op) >= low
&& INTVAL (op) <= high
&& (INTVAL (op) & mask) == 0);
}
int
m16_uimm3_b (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return m16_check_op (op, 0x1, 0x8, 0);
}
int
m16_simm4_1 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return m16_check_op (op, - 0x8, 0x7, 0);
}
int
m16_nsimm4_1 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return m16_check_op (op, - 0x7, 0x8, 0);
}
int
m16_simm5_1 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return m16_check_op (op, - 0x10, 0xf, 0);
}
int
m16_nsimm5_1 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return m16_check_op (op, - 0xf, 0x10, 0);
}
int
m16_uimm5_4 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return m16_check_op (op, (- 0x10) << 2, 0xf << 2, 3);
}
int
m16_nuimm5_4 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return m16_check_op (op, (- 0xf) << 2, 0x10 << 2, 3);
}
int
m16_simm8_1 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return m16_check_op (op, - 0x80, 0x7f, 0);
}
int
m16_nsimm8_1 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return m16_check_op (op, - 0x7f, 0x80, 0);
}
int
m16_uimm8_1 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return m16_check_op (op, 0x0, 0xff, 0);
}
int
m16_nuimm8_1 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return m16_check_op (op, - 0xff, 0x0, 0);
}
int
m16_uimm8_m1_1 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return m16_check_op (op, - 0x1, 0xfe, 0);
}
int
m16_uimm8_4 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return m16_check_op (op, 0x0, 0xff << 2, 3);
}
int
m16_nuimm8_4 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return m16_check_op (op, (- 0xff) << 2, 0x0, 3);
}
int
m16_simm8_8 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return m16_check_op (op, (- 0x80) << 3, 0x7f << 3, 7);
}
int
m16_nsimm8_8 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return m16_check_op (op, (- 0x7f) << 3, 0x80 << 3, 7);
}
int
m16_usym8_4 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) == SYMBOL_REF
&& SYMBOL_REF_FLAG (op)
&& cfun->machine->insns_len > 0
&& XSTR (op, 0)[0] == '*'
&& strncmp (XSTR (op, 0) + 1, LOCAL_LABEL_PREFIX,
sizeof LOCAL_LABEL_PREFIX - 1) == 0
&& (cfun->machine->insns_len + get_pool_size () + mips_string_length
< 4 * 0x100))
{
struct string_constant *l;
for (l = string_constants; l != NULL; l = l->next)
if (strcmp (l->label, XSTR (op, 0)) == 0)
return 1;
}
return 0;
}
int
m16_usym5_4 (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) == SYMBOL_REF
&& SYMBOL_REF_FLAG (op)
&& cfun->machine->insns_len > 0
&& XSTR (op, 0)[0] == '*'
&& strncmp (XSTR (op, 0) + 1, LOCAL_LABEL_PREFIX,
sizeof LOCAL_LABEL_PREFIX - 1) == 0
&& (cfun->machine->insns_len + get_pool_size () + mips_string_length
< 4 * 0x20))
{
struct string_constant *l;
for (l = string_constants; l != NULL; l = l->next)
if (strcmp (l->label, XSTR (op, 0)) == 0)
return 1;
}
return 0;
}
const char *
mips_fill_delay_slot (ret, type, operands, cur_insn)
const char *ret;
enum delay_type type;
rtx operands[];
rtx cur_insn;
{
register rtx set_reg;
register enum machine_mode mode;
register rtx next_insn = cur_insn ? NEXT_INSN (cur_insn) : NULL_RTX;
register int num_nops;
if (type == DELAY_LOAD || type == DELAY_FCMP)
num_nops = 1;
else if (type == DELAY_HILO)
num_nops = 2;
else
num_nops = 0;
next_insn = NEXT_INSN (cur_insn);
while (next_insn != 0 && GET_CODE (next_insn) == NOTE)
next_insn = NEXT_INSN (next_insn);
dslots_load_total += num_nops;
if (TARGET_DEBUG_F_MODE
|| !optimize
|| type == DELAY_NONE
|| operands == 0
|| cur_insn == 0
|| next_insn == 0
|| GET_CODE (next_insn) == CODE_LABEL
|| (set_reg = operands[0]) == 0)
{
dslots_number_nops = 0;
mips_load_reg = 0;
mips_load_reg2 = 0;
mips_load_reg3 = 0;
mips_load_reg4 = 0;
return ret;
}
set_reg = operands[0];
if (set_reg == 0)
return ret;
while (GET_CODE (set_reg) == SUBREG)
set_reg = SUBREG_REG (set_reg);
mode = GET_MODE (set_reg);
dslots_number_nops = num_nops;
mips_load_reg = set_reg;
if (GET_MODE_SIZE (mode)
> (unsigned) (FP_REG_P (REGNO (set_reg)) ? UNITS_PER_FPREG : UNITS_PER_WORD))
mips_load_reg2 = gen_rtx_REG (SImode, REGNO (set_reg) + 1);
else
mips_load_reg2 = 0;
if (type == DELAY_HILO)
{
mips_load_reg3 = gen_rtx_REG (SImode, MD_REG_FIRST);
mips_load_reg4 = gen_rtx_REG (SImode, MD_REG_FIRST+1);
}
else
{
mips_load_reg3 = 0;
mips_load_reg4 = 0;
}
return ret;
}
void
mips_count_memory_refs (op, num)
rtx op;
int num;
{
int additional = 0;
int n_words = 0;
rtx addr, plus0, plus1;
enum rtx_code code0, code1;
int looping;
if (TARGET_DEBUG_B_MODE)
{
fprintf (stderr, "\n========== mips_count_memory_refs:\n");
debug_rtx (op);
}
addr = (GET_CODE (op) != MEM) ? op : XEXP (op, 0);
do
{
looping = FALSE;
switch (GET_CODE (addr))
{
case REG:
case CONST_INT:
case LO_SUM:
break;
case PLUS:
plus0 = XEXP (addr, 0);
plus1 = XEXP (addr, 1);
code0 = GET_CODE (plus0);
code1 = GET_CODE (plus1);
if (code0 == REG)
{
additional++;
addr = plus1;
looping = 1;
continue;
}
if (code0 == CONST_INT)
{
addr = plus1;
looping = 1;
continue;
}
if (code1 == REG)
{
additional++;
addr = plus0;
looping = 1;
continue;
}
if (code1 == CONST_INT)
{
addr = plus0;
looping = 1;
continue;
}
if (code0 == SYMBOL_REF || code0 == LABEL_REF || code0 == CONST)
{
addr = plus0;
looping = 1;
continue;
}
if (code1 == SYMBOL_REF || code1 == LABEL_REF || code1 == CONST)
{
addr = plus1;
looping = 1;
continue;
}
break;
case LABEL_REF:
n_words = 2;
break;
case CONST:
addr = XEXP (addr, 0);
looping = 1;
continue;
case SYMBOL_REF:
n_words = SYMBOL_REF_FLAG (addr) ? 1 : 2;
break;
default:
break;
}
}
while (looping);
if (n_words == 0)
return;
n_words += additional;
if (n_words > 3)
n_words = 3;
num_refs[n_words-1] += num;
}
rtx
embedded_pic_fnaddr_reg ()
{
if (cfun->machine->embedded_pic_fnaddr_rtx == NULL)
{
rtx seq;
cfun->machine->embedded_pic_fnaddr_rtx = gen_reg_rtx (Pmode);
start_sequence ();
emit_insn (gen_get_fnaddr (cfun->machine->embedded_pic_fnaddr_rtx,
XEXP (DECL_RTL (current_function_decl), 0)));
seq = get_insns ();
end_sequence ();
push_topmost_sequence ();
emit_insn_after (seq, get_insns ());
pop_topmost_sequence ();
}
return cfun->machine->embedded_pic_fnaddr_rtx;
}
rtx
embedded_pic_offset (x)
rtx x;
{
embedded_pic_fnaddr_reg ();
return
gen_rtx_CONST (Pmode,
gen_rtx_MINUS (Pmode, x,
XEXP (DECL_RTL (current_function_decl), 0)));
}
const char *
mips_move_1word (operands, insn, unsignedp)
rtx operands[];
rtx insn;
int unsignedp;
{
const char *ret = 0;
rtx op0 = operands[0];
rtx op1 = operands[1];
enum rtx_code code0 = GET_CODE (op0);
enum rtx_code code1 = GET_CODE (op1);
enum machine_mode mode = GET_MODE (op0);
int subreg_offset0 = 0;
int subreg_offset1 = 0;
enum delay_type delay = DELAY_NONE;
while (code0 == SUBREG)
{
subreg_offset0 += subreg_regno_offset (REGNO (SUBREG_REG (op0)),
GET_MODE (SUBREG_REG (op0)),
SUBREG_BYTE (op0),
GET_MODE (op0));
op0 = SUBREG_REG (op0);
code0 = GET_CODE (op0);
}
while (code1 == SUBREG)
{
subreg_offset1 += subreg_regno_offset (REGNO (SUBREG_REG (op1)),
GET_MODE (SUBREG_REG (op1)),
SUBREG_BYTE (op1),
GET_MODE (op1));
op1 = SUBREG_REG (op1);
code1 = GET_CODE (op1);
}
if (mode == CCmode)
mode = SImode;
if (code0 == REG)
{
int regno0 = REGNO (op0) + subreg_offset0;
if (code1 == REG)
{
int regno1 = REGNO (op1) + subreg_offset1;
if (regno0 == regno1 && set_nomacro == 0)
ret = "";
else if (GP_REG_P (regno0))
{
if (GP_REG_P (regno1))
ret = "move\t%0,%1";
else if (MD_REG_P (regno1))
{
delay = DELAY_HILO;
if (regno1 != HILO_REGNUM)
ret = "mf%1\t%0";
else
ret = "mflo\t%0";
}
else if (ST_REG_P (regno1) && ISA_HAS_8CC)
ret = "li\t%0,1\n\tmovf\t%0,%.,%1";
else
{
delay = DELAY_LOAD;
if (FP_REG_P (regno1))
ret = "mfc1\t%0,%1";
else if (ALL_COP_REG_P (regno1))
{
static char retval[] = "mfc_\t%0,%1";
retval[3] = COPNUM_AS_CHAR_FROM_REGNUM (regno1);
ret = retval;
}
else if (regno1 == FPSW_REGNUM && ! ISA_HAS_8CC)
ret = "cfc1\t%0,$31";
}
}
else if (FP_REG_P (regno0))
{
if (GP_REG_P (regno1))
{
delay = DELAY_LOAD;
ret = "mtc1\t%1,%0";
}
if (FP_REG_P (regno1))
ret = "mov.s\t%0,%1";
}
else if (MD_REG_P (regno0))
{
if (GP_REG_P (regno1))
{
delay = DELAY_HILO;
if (regno0 != HILO_REGNUM && ! TARGET_MIPS16)
ret = "mt%0\t%1";
}
}
else if (regno0 == FPSW_REGNUM && ! ISA_HAS_8CC)
{
if (GP_REG_P (regno1))
{
delay = DELAY_LOAD;
ret = "ctc1\t%0,$31";
}
}
else if (ALL_COP_REG_P (regno0))
{
if (GP_REG_P (regno1))
{
static char retval[] = "mtc_\t%1,%0";
char cop = COPNUM_AS_CHAR_FROM_REGNUM (regno0);
if (cop == '0')
abort_with_insn (insn,
"mtc0 not supported; it disturbs virtual address translation");
delay = DELAY_LOAD;
retval[3] = cop;
ret = retval;
}
}
}
else if (code1 == MEM)
{
delay = DELAY_LOAD;
if (TARGET_STATS)
mips_count_memory_refs (op1, 1);
if (GP_REG_P (regno0))
{
switch (GET_MODE (op1))
{
default:
break;
case SFmode:
ret = "lw\t%0,%1";
break;
case SImode:
case CCmode:
ret = ((unsignedp && TARGET_64BIT)
? "lwu\t%0,%1"
: "lw\t%0,%1");
break;
case HImode:
ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1";
break;
case QImode:
ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1";
break;
}
}
else if (FP_REG_P (regno0) && (mode == SImode || mode == SFmode))
ret = "l.s\t%0,%1";
else if (ALL_COP_REG_P (regno0))
{
static char retval[] = "lwc_\t%0,%1";
char cop = COPNUM_AS_CHAR_FROM_REGNUM (regno0);
if (cop == '0')
abort_with_insn (insn,
"loads from memory to COP0 are illegal");
delay = DELAY_LOAD;
retval[3] = cop;
ret = retval;
}
if (ret != (char *)0 && MEM_VOLATILE_P (op1))
{
size_t i = strlen (ret);
if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
abort ();
sprintf (volatile_buffer, "%%{%s%%}", ret);
ret = volatile_buffer;
}
}
else if (code1 == CONST_INT
|| (code1 == CONST_DOUBLE
&& GET_MODE (op1) == VOIDmode))
{
if (code1 == CONST_DOUBLE)
{
operands[1] = op1 = GEN_INT (CONST_DOUBLE_LOW (op1));
}
if (INTVAL (op1) == 0 && ! TARGET_MIPS16)
{
if (GP_REG_P (regno0))
ret = "move\t%0,%z1";
else if (FP_REG_P (regno0))
{
delay = DELAY_LOAD;
ret = "mtc1\t%z1,%0";
}
else if (MD_REG_P (regno0))
{
delay = DELAY_HILO;
ret = "mt%0\t%.";
}
}
else if (GP_REG_P (regno0))
{
if (! TARGET_MIPS16)
ret = "li\t%0,%1\t\t\t# %X1";
else
{
if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff)
ret = "li\t%0,%1";
else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff)
ret = "li\t%0,%n1\n\tneg\t%0";
}
}
}
else if (code1 == CONST_DOUBLE && mode == SFmode)
{
if (op1 == CONST0_RTX (SFmode))
{
if (GP_REG_P (regno0))
ret = "move\t%0,%.";
else if (FP_REG_P (regno0))
{
delay = DELAY_LOAD;
ret = "mtc1\t%.,%0";
}
}
else
{
delay = DELAY_LOAD;
ret = "li.s\t%0,%1";
}
}
else if (code1 == LABEL_REF)
{
if (TARGET_STATS)
mips_count_memory_refs (op1, 1);
ret = "la\t%0,%a1";
}
else if (code1 == SYMBOL_REF || code1 == CONST)
{
if (TARGET_MIPS16
&& code1 == CONST
&& GET_CODE (XEXP (op1, 0)) == REG
&& REGNO (XEXP (op1, 0)) == GP_REG_FIRST + 28)
{
ret = "move\t%0,%+";
}
else if (TARGET_MIPS16
&& code1 == SYMBOL_REF
&& SYMBOL_REF_FLAG (op1)
&& (XSTR (op1, 0)[0] != '*'
|| strncmp (XSTR (op1, 0) + 1,
LOCAL_LABEL_PREFIX,
sizeof LOCAL_LABEL_PREFIX - 1) != 0))
{
ret = "move\t%0,%+\n\taddu\t%0,%%gprel(%a1)";
}
else
{
if (TARGET_STATS)
mips_count_memory_refs (op1, 1);
ret = "la\t%0,%a1";
}
}
else if (code1 == PLUS)
{
rtx add_op0 = XEXP (op1, 0);
rtx add_op1 = XEXP (op1, 1);
if (GET_CODE (XEXP (op1, 1)) == REG
&& GET_CODE (XEXP (op1, 0)) == CONST_INT)
add_op0 = XEXP (op1, 1), add_op1 = XEXP (op1, 0);
operands[2] = add_op0;
operands[3] = add_op1;
ret = "add%:\t%0,%2,%3";
}
else if (code1 == HIGH)
{
operands[1] = XEXP (op1, 0);
ret = "lui\t%0,%%hi(%1)";
}
}
else if (code0 == MEM)
{
if (TARGET_STATS)
mips_count_memory_refs (op0, 1);
if (code1 == REG)
{
int regno1 = REGNO (op1) + subreg_offset1;
if (GP_REG_P (regno1))
{
switch (mode)
{
case SFmode: ret = "sw\t%1,%0"; break;
case SImode: ret = "sw\t%1,%0"; break;
case HImode: ret = "sh\t%1,%0"; break;
case QImode: ret = "sb\t%1,%0"; break;
default: break;
}
}
else if (FP_REG_P (regno1) && (mode == SImode || mode == SFmode))
ret = "s.s\t%1,%0";
else if (ALL_COP_REG_P (regno1))
{
static char retval[] = "swc_\t%1,%0";
retval[3] = COPNUM_AS_CHAR_FROM_REGNUM (regno1);
ret = retval;
}
}
else if (code1 == CONST_INT && INTVAL (op1) == 0)
{
switch (mode)
{
case SFmode: ret = "sw\t%z1,%0"; break;
case SImode: ret = "sw\t%z1,%0"; break;
case HImode: ret = "sh\t%z1,%0"; break;
case QImode: ret = "sb\t%z1,%0"; break;
default: break;
}
}
else if (code1 == CONST_DOUBLE && op1 == CONST0_RTX (mode))
{
switch (mode)
{
case SFmode: ret = "sw\t%.,%0"; break;
case SImode: ret = "sw\t%.,%0"; break;
case HImode: ret = "sh\t%.,%0"; break;
case QImode: ret = "sb\t%.,%0"; break;
default: break;
}
}
if (ret != 0 && MEM_VOLATILE_P (op0))
{
size_t i = strlen (ret);
if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
abort ();
sprintf (volatile_buffer, "%%{%s%%}", ret);
ret = volatile_buffer;
}
}
if (ret == 0)
{
abort_with_insn (insn, "bad move");
return 0;
}
if (delay != DELAY_NONE)
return mips_fill_delay_slot (ret, delay, operands, insn);
return ret;
}
const char *
mips_restore_gp (operands, insn)
rtx *operands, insn;
{
rtx loc;
operands[0] = pic_offset_table_rtx;
if (frame_pointer_needed)
loc = hard_frame_pointer_rtx;
else
loc = stack_pointer_rtx;
loc = plus_constant (loc, cfun->machine->frame.args_size);
operands[1] = gen_rtx_MEM (Pmode, loc);
return mips_move_1word (operands, insn, 0);
}
const char *
mips_sign_extend (insn, dest, src)
rtx insn, dest, src;
{
rtx operands[MAX_RECOG_OPERANDS];
if ((register_operand (src, SImode) && FP_REG_P (true_regnum (src)))
|| memory_operand (src, SImode))
{
operands[0] = gen_lowpart_SUBREG (SImode, dest);
operands[1] = src;
return mips_move_1word (operands, insn, false);
}
else
{
operands[0] = dest;
operands[1] = src;
return mips_move_2words (operands, insn);
}
}
const char *
mips_move_2words (operands, insn)
rtx operands[];
rtx insn;
{
const char *ret = 0;
rtx op0 = operands[0];
rtx op1 = operands[1];
enum rtx_code code0 = GET_CODE (operands[0]);
enum rtx_code code1 = GET_CODE (operands[1]);
int subreg_offset0 = 0;
int subreg_offset1 = 0;
enum delay_type delay = DELAY_NONE;
if (code1 == SIGN_EXTEND)
return mips_sign_extend (insn, op0, XEXP (op1, 0));
while (code0 == SUBREG)
{
subreg_offset0 += subreg_regno_offset (REGNO (SUBREG_REG (op0)),
GET_MODE (SUBREG_REG (op0)),
SUBREG_BYTE (op0),
GET_MODE (op0));
op0 = SUBREG_REG (op0);
code0 = GET_CODE (op0);
}
while (code1 == SUBREG)
{
subreg_offset1 += subreg_regno_offset (REGNO (SUBREG_REG (op1)),
GET_MODE (SUBREG_REG (op1)),
SUBREG_BYTE (op1),
GET_MODE (op1));
op1 = SUBREG_REG (op1);
code1 = GET_CODE (op1);
}
if (code0 == REG)
{
int regno0 = REGNO (op0) + subreg_offset0;
if (code1 == REG)
{
int regno1 = REGNO (op1) + subreg_offset1;
if (regno0 == regno1 && set_nomacro == 0)
ret = "";
else if (FP_REG_P (regno0))
{
if (FP_REG_P (regno1))
ret = "mov.d\t%0,%1";
else
{
delay = DELAY_LOAD;
if (TARGET_FLOAT64)
{
if (!TARGET_64BIT)
abort_with_insn (insn, "bad move");
#ifdef TARGET_FP_CALL_32
if (FP_CALL_GP_REG_P (regno1))
ret = "dsll\t%1,32\n\tor\t%1,%D1\n\tdmtc1\t%1,%0";
else
#endif
ret = "dmtc1\t%1,%0";
}
else
ret = "mtc1\t%L1,%0\n\tmtc1\t%M1,%D0";
}
}
else if (FP_REG_P (regno1))
{
delay = DELAY_LOAD;
if (TARGET_FLOAT64)
{
if (!TARGET_64BIT)
abort_with_insn (insn, "bad move");
#ifdef TARGET_FP_CALL_32
if (FP_CALL_GP_REG_P (regno0))
ret = "dmfc1\t%0,%1\n\tmfc1\t%D0,%1\n\tdsrl\t%0,32";
else
#endif
ret = "dmfc1\t%0,%1";
}
else
ret = "mfc1\t%L0,%1\n\tmfc1\t%M0,%D1";
}
else if (MD_REG_P (regno0) && GP_REG_P (regno1) && !TARGET_MIPS16)
{
delay = DELAY_HILO;
if (TARGET_64BIT)
{
if (regno0 != HILO_REGNUM)
ret = "mt%0\t%1";
else if (regno1 == 0)
ret = "mtlo\t%.\n\tmthi\t%.";
}
else
ret = "mthi\t%M1\n\tmtlo\t%L1";
}
else if (GP_REG_P (regno0) && MD_REG_P (regno1))
{
delay = DELAY_HILO;
if (TARGET_64BIT)
{
if (regno1 != HILO_REGNUM)
ret = "mf%1\t%0";
}
else
ret = "mfhi\t%M0\n\tmflo\t%L0";
}
else if (GP_REG_P (regno0) && ALL_COP_REG_P (regno1)
&& TARGET_64BIT)
{
static char retval[] = "dmfc_\t%0,%1";
delay = DELAY_LOAD;
retval[4] = COPNUM_AS_CHAR_FROM_REGNUM (regno1);
ret = retval;
}
else if (ALL_COP_REG_P (regno0) && GP_REG_P (regno1)
&& TARGET_64BIT)
{
static char retval[] = "dmtc_\t%1,%0";
char cop = COPNUM_AS_CHAR_FROM_REGNUM (regno0);
if (cop == '0')
abort_with_insn (insn,
"dmtc0 not supported; it disturbs virtual address translation");
delay = DELAY_LOAD;
retval[4] = cop;
ret = retval;
}
else if (TARGET_64BIT)
ret = "move\t%0,%1";
else if (regno0 != (regno1+1))
ret = "move\t%0,%1\n\tmove\t%D0,%D1";
else
ret = "move\t%D0,%D1\n\tmove\t%0,%1";
}
else if (code1 == CONST_DOUBLE)
{
if (op1 != CONST0_RTX (GET_MODE (op1))
|| (TARGET_FLOAT64 && !TARGET_64BIT && FP_REG_P (regno0)))
{
if (GET_MODE (op1) == DFmode)
{
delay = DELAY_LOAD;
#ifdef TARGET_FP_CALL_32
if (FP_CALL_GP_REG_P (regno0))
{
if (TARGET_FLOAT64 && !TARGET_64BIT)
{
split_double (op1, operands + 2, operands + 3);
ret = "li\t%0,%2\n\tli\t%D0,%3";
}
else
ret = "li.d\t%0,%1\n\tdsll\t%D0,%0,32\n\tdsrl\t%D0,32\n\tdsrl\t%0,32";
}
else
#endif
if (ISA_HAS_64BIT_REGS
&& ! TARGET_64BIT
&& ! FP_REG_P (regno0))
{
split_double (op1, operands + 2, operands + 3);
ret = "li\t%0,%2\n\tli\t%D0,%3";
}
else
ret = "li.d\t%0,%1";
}
else if (TARGET_64BIT)
{
if (! TARGET_MIPS16)
ret = "dli\t%0,%1";
}
else
{
split_double (op1, operands + 2, operands + 3);
ret = "li\t%0,%2\n\tli\t%D0,%3";
}
}
else
{
if (GP_REG_P (regno0))
ret = (TARGET_64BIT
#ifdef TARGET_FP_CALL_32
&& ! FP_CALL_GP_REG_P (regno0)
#endif
? "move\t%0,%."
: "move\t%0,%.\n\tmove\t%D0,%.");
else if (FP_REG_P (regno0))
{
delay = DELAY_LOAD;
ret = (TARGET_64BIT
? "dmtc1\t%.,%0"
: "mtc1\t%.,%0\n\tmtc1\t%.,%D0");
}
}
}
else if (code1 == CONST_INT && INTVAL (op1) == 0 && ! TARGET_MIPS16)
{
if (GP_REG_P (regno0))
ret = (TARGET_64BIT
? "move\t%0,%."
: "move\t%0,%.\n\tmove\t%D0,%.");
else if (FP_REG_P (regno0))
{
delay = DELAY_LOAD;
ret = (TARGET_64BIT
? "dmtc1\t%.,%0"
: (TARGET_FLOAT64
? "li.d\t%0,%1"
: "mtc1\t%.,%0\n\tmtc1\t%.,%D0"));
}
else if (MD_REG_P (regno0))
{
delay = DELAY_HILO;
ret = (regno0 == HILO_REGNUM
? "mtlo\t%.\n\tmthi\t%."
: "mt%0\t%.\n");
}
}
else if (code1 == CONST_INT && GET_MODE (op0) == DImode
&& GP_REG_P (regno0))
{
if (TARGET_64BIT)
{
if (TARGET_MIPS16)
{
if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff)
ret = "li\t%0,%1";
else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff)
ret = "li\t%0,%n1\n\tneg\t%0";
}
else if (GET_CODE (operands[1]) == SIGN_EXTEND)
ret = "li\t%0,%1\t\t# %X1";
else if (HOST_BITS_PER_WIDE_INT < 64)
ret = (INTVAL (op1) < 0
? "dli\t%0,%1\t\t\t# %X1"
: "dli\t%0,%X1\t\t# %1");
else
ret = "dli\t%0,%X1\t\t# %1";
}
else if (HOST_BITS_PER_WIDE_INT < 64)
{
operands[2] = GEN_INT (INTVAL (operands[1]) >= 0 ? 0 : -1);
if (TARGET_MIPS16)
{
if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff)
ret = "li\t%M0,%2\n\tli\t%L0,%1";
else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff)
{
operands[2] = GEN_INT (1);
ret = "li\t%M0,%2\n\tneg\t%M0\n\tli\t%L0,%n1\n\tneg\t%L0";
}
}
else
ret = "li\t%M0,%2\n\tli\t%L0,%1";
}
else
{
operands[2] = GEN_INT (INTVAL (operands[1]) >> 16 >> 16);
operands[1]
= GEN_INT (INTVAL (operands[1]) << 16 << 16 >> 16 >> 16);
if (TARGET_MIPS16)
{
if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff)
ret = "li\t%M0,%2\n\tli\t%L0,%1";
else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff)
{
operands[2] = GEN_INT (1);
ret = "li\t%M0,%2\n\tneg\t%M0\n\tli\t%L0,%n1\n\tneg\t%L0";
}
}
else
ret = "li\t%M0,%2\n\tli\t%L0,%1";
}
}
else if (code1 == MEM)
{
delay = DELAY_LOAD;
if (TARGET_STATS)
mips_count_memory_refs (op1, 2);
if (FP_REG_P (regno0))
ret = "l.d\t%0,%1";
else if (ALL_COP_REG_P (regno0) && TARGET_64BIT)
{
static char retval[] = "ldc_\t%0,%1";
char cop = COPNUM_AS_CHAR_FROM_REGNUM (regno0);
if (cop == '0')
abort_with_insn (insn,
"loads from memory to COP0 are illegal");
delay = DELAY_LOAD;
retval[3] = cop;
ret = retval;
}
else if (TARGET_64BIT)
{
#ifdef TARGET_FP_CALL_32
if (FP_CALL_GP_REG_P (regno0))
ret = (double_memory_operand (op1, GET_MODE (op1))
? "lwu\t%0,%1\n\tlwu\t%D0,4+%1"
: "ld\t%0,%1\n\tdsll\t%D0,%0,32\n\tdsrl\t%D0,32\n\tdsrl\t%0,32");
else
#endif
ret = "ld\t%0,%1";
}
else if (double_memory_operand (op1, GET_MODE (op1)))
ret = (reg_mentioned_p (op0, op1)
? "lw\t%D0,%D1\n\tlw\t%0,%1"
: "lw\t%0,%1\n\tlw\t%D0,%D1");
if (ret != 0 && MEM_VOLATILE_P (op1))
{
size_t i = strlen (ret);
if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
abort ();
sprintf (volatile_buffer, "%%{%s%%}", ret);
ret = volatile_buffer;
}
}
else if (code1 == LABEL_REF)
{
if (TARGET_STATS)
mips_count_memory_refs (op1, 2);
if (GET_CODE (operands[1]) == SIGN_EXTEND)
ret = "la\t%0,%1";
else
ret = "dla\t%0,%a1";
}
else if (code1 == SYMBOL_REF || code1 == CONST)
{
if (TARGET_MIPS16
&& code1 == CONST
&& GET_CODE (XEXP (op1, 0)) == REG
&& REGNO (XEXP (op1, 0)) == GP_REG_FIRST + 28)
{
ret = "move\t%0,%+";
}
else if (TARGET_MIPS16
&& code1 == SYMBOL_REF
&& SYMBOL_REF_FLAG (op1)
&& (XSTR (op1, 0)[0] != '*'
|| strncmp (XSTR (op1, 0) + 1,
LOCAL_LABEL_PREFIX,
sizeof LOCAL_LABEL_PREFIX - 1) != 0))
{
ret = "move\t%0,%+\n\taddu\t%0,%%gprel(%a1)";
}
else
{
if (TARGET_STATS)
mips_count_memory_refs (op1, 2);
if (GET_CODE (operands[1]) == SIGN_EXTEND)
ret = "la\t%0,%1";
else
ret = "dla\t%0,%a1";
}
}
}
else if (code0 == MEM)
{
if (code1 == REG)
{
int regno1 = REGNO (op1) + subreg_offset1;
if (FP_REG_P (regno1))
ret = "s.d\t%1,%0";
else if (ALL_COP_REG_P (regno1) && TARGET_64BIT)
{
static char retval[] = "sdc_\t%1,%0";
retval[3] = COPNUM_AS_CHAR_FROM_REGNUM (regno1);
ret = retval;
}
else if (TARGET_64BIT)
{
#ifdef TARGET_FP_CALL_32
if (FP_CALL_GP_REG_P (regno1))
ret = "dsll\t%1,32\n\tor\t%1,%D1\n\tsd\t%1,%0";
else
#endif
ret = "sd\t%1,%0";
}
else if (double_memory_operand (op0, GET_MODE (op0)))
ret = "sw\t%1,%0\n\tsw\t%D1,%D0";
}
else if (((code1 == CONST_INT && INTVAL (op1) == 0)
|| (code1 == CONST_DOUBLE
&& op1 == CONST0_RTX (GET_MODE (op1))))
&& (TARGET_64BIT
|| double_memory_operand (op0, GET_MODE (op0))))
{
if (TARGET_64BIT)
ret = "sd\t%.,%0";
else
ret = "sw\t%.,%0\n\tsw\t%.,%D0";
}
if (TARGET_STATS)
mips_count_memory_refs (op0, 2);
if (ret != 0 && MEM_VOLATILE_P (op0))
{
size_t i = strlen (ret);
if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
abort ();
sprintf (volatile_buffer, "%%{%s%%}", ret);
ret = volatile_buffer;
}
}
if (ret == 0)
{
abort_with_insn (insn, "bad move");
return 0;
}
if (delay != DELAY_NONE)
return mips_fill_delay_slot (ret, delay, operands, insn);
return ret;
}
int
mips_address_cost (addr)
rtx addr;
{
switch (GET_CODE (addr))
{
case LO_SUM:
return 1;
case LABEL_REF:
return 2;
case CONST:
{
rtx offset = const0_rtx;
addr = eliminate_constant_term (XEXP (addr, 0), &offset);
if (GET_CODE (addr) == LABEL_REF)
return 2;
if (GET_CODE (addr) != SYMBOL_REF)
return 4;
if (! SMALL_INT (offset))
return 2;
}
case SYMBOL_REF:
return SYMBOL_REF_FLAG (addr) ? 1 : 2;
case PLUS:
{
register rtx plus0 = XEXP (addr, 0);
register rtx plus1 = XEXP (addr, 1);
if (GET_CODE (plus0) != REG && GET_CODE (plus1) == REG)
plus0 = XEXP (addr, 1), plus1 = XEXP (addr, 0);
if (GET_CODE (plus0) != REG)
break;
switch (GET_CODE (plus1))
{
case CONST_INT:
return SMALL_INT (plus1) ? 1 : 2;
case CONST:
case SYMBOL_REF:
case LABEL_REF:
case HIGH:
case LO_SUM:
return mips_address_cost (plus1) + 1;
default:
break;
}
}
default:
break;
}
return 4;
}
int
pic_address_needs_scratch (x)
rtx x;
{
if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& ! SMALL_INT (XEXP (XEXP (x, 0), 1)))
return 1;
return 0;
}
static enum internal_test
map_test_to_internal_test (test_code)
enum rtx_code test_code;
{
enum internal_test test = ITEST_MAX;
switch (test_code)
{
case EQ: test = ITEST_EQ; break;
case NE: test = ITEST_NE; break;
case GT: test = ITEST_GT; break;
case GE: test = ITEST_GE; break;
case LT: test = ITEST_LT; break;
case LE: test = ITEST_LE; break;
case GTU: test = ITEST_GTU; break;
case GEU: test = ITEST_GEU; break;
case LTU: test = ITEST_LTU; break;
case LEU: test = ITEST_LEU; break;
default: break;
}
return test;
}
rtx
gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
enum rtx_code test_code;
rtx result;
rtx cmp0;
rtx cmp1;
int *p_invert;
{
struct cmp_info
{
enum rtx_code test_code;
int const_low;
int const_high;
int const_add;
int reverse_regs;
int invert_const;
int invert_reg;
int unsignedp;
};
static const struct cmp_info info[ (int)ITEST_MAX ] = {
{ XOR, 0, 65535, 0, 0, 0, 0, 0 },
{ XOR, 0, 65535, 0, 0, 1, 1, 0 },
{ LT, -32769, 32766, 1, 1, 1, 0, 0 },
{ LT, -32768, 32767, 0, 0, 1, 1, 0 },
{ LT, -32768, 32767, 0, 0, 0, 0, 0 },
{ LT, -32769, 32766, 1, 1, 0, 1, 0 },
{ LTU, -32769, 32766, 1, 1, 1, 0, 1 },
{ LTU, -32768, 32767, 0, 0, 1, 1, 1 },
{ LTU, -32768, 32767, 0, 0, 0, 0, 1 },
{ LTU, -32769, 32766, 1, 1, 0, 1, 1 },
};
enum internal_test test;
enum machine_mode mode;
const struct cmp_info *p_info;
int branch_p;
int eqne_p;
int invert;
rtx reg;
rtx reg2;
test = map_test_to_internal_test (test_code);
if (test == ITEST_MAX)
abort ();
p_info = &info[(int) test];
eqne_p = (p_info->test_code == XOR);
mode = GET_MODE (cmp0);
if (mode == VOIDmode)
mode = GET_MODE (cmp1);
branch_p = (result == 0);
if (branch_p)
{
if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG)
{
if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0
&& (! TARGET_MIPS16 || eqne_p))
return 0;
if (eqne_p && ! TARGET_MIPS16)
return 0;
}
result = gen_reg_rtx (mode);
}
if (GET_CODE (cmp0) == CONST_INT)
cmp0 = force_reg (mode, cmp0);
if (GET_CODE (cmp1) == CONST_INT)
{
HOST_WIDE_INT value = INTVAL (cmp1);
if (value < p_info->const_low
|| value > p_info->const_high
|| (TARGET_64BIT
&& HOST_BITS_PER_WIDE_INT < 64
&& p_info->const_add != 0
&& ((p_info->unsignedp
? ((unsigned HOST_WIDE_INT) (value + p_info->const_add)
> (unsigned HOST_WIDE_INT) INTVAL (cmp1))
: (value + p_info->const_add) > INTVAL (cmp1))
!= (p_info->const_add > 0))))
cmp1 = force_reg (mode, cmp1);
}
invert = (GET_CODE (cmp1) == CONST_INT
? p_info->invert_const : p_info->invert_reg);
if (p_invert != (int *)0)
{
*p_invert = invert;
invert = 0;
}
if (GET_CODE (cmp1) == CONST_INT)
{
if (p_info->const_add != 0)
{
HOST_WIDE_INT new = INTVAL (cmp1) + p_info->const_add;
if ((p_info->unsignedp
? (unsigned HOST_WIDE_INT) new >
(unsigned HOST_WIDE_INT) INTVAL (cmp1)
: new > INTVAL (cmp1))
!= (p_info->const_add > 0))
{
emit_move_insn (result, invert ? const0_rtx : const_true_rtx);
return result;
}
else
cmp1 = GEN_INT (new);
}
}
else if (p_info->reverse_regs)
{
rtx temp = cmp0;
cmp0 = cmp1;
cmp1 = temp;
}
if (test == ITEST_NE && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
reg = cmp0;
else
{
reg = (invert || eqne_p) ? gen_reg_rtx (mode) : result;
convert_move (reg, gen_rtx (p_info->test_code, mode, cmp0, cmp1), 0);
}
if (test == ITEST_NE)
{
if (! TARGET_MIPS16)
{
convert_move (result, gen_rtx (GTU, mode, reg, const0_rtx), 0);
if (p_invert != NULL)
*p_invert = 0;
invert = 0;
}
else
{
reg2 = invert ? gen_reg_rtx (mode) : result;
convert_move (reg2, gen_rtx (LTU, mode, reg, const1_rtx), 0);
reg = reg2;
}
}
else if (test == ITEST_EQ)
{
reg2 = invert ? gen_reg_rtx (mode) : result;
convert_move (reg2, gen_rtx_LTU (mode, reg, const1_rtx), 0);
reg = reg2;
}
if (invert)
{
rtx one;
if (! TARGET_MIPS16)
one = const1_rtx;
else
{
reg2 = gen_reg_rtx (mode);
emit_move_insn (reg2, reg);
reg = reg2;
one = force_reg (mode, const1_rtx);
}
convert_move (result, gen_rtx (XOR, mode, reg, one), 0);
}
return result;
}
static void
get_float_compare_codes (in_code, cmp_code, action_code)
enum rtx_code in_code, *cmp_code, *action_code;
{
switch (in_code)
{
case NE:
case UNGE:
case UNGT:
case LTGT:
case ORDERED:
*cmp_code = reverse_condition_maybe_unordered (in_code);
*action_code = EQ;
break;
default:
*cmp_code = in_code;
*action_code = NE;
break;
}
}
void
gen_conditional_branch (operands, test_code)
rtx operands[];
enum rtx_code test_code;
{
enum cmp_type type = branch_type;
rtx cmp0 = branch_cmp[0];
rtx cmp1 = branch_cmp[1];
enum machine_mode mode;
enum rtx_code cmp_code;
rtx reg;
int invert;
rtx label1, label2;
switch (type)
{
case CMP_SI:
case CMP_DI:
mode = type == CMP_SI ? SImode : DImode;
invert = 0;
reg = gen_int_relational (test_code, NULL_RTX, cmp0, cmp1, &invert);
if (reg)
{
cmp0 = reg;
cmp1 = const0_rtx;
test_code = NE;
}
else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
cmp1 = force_reg (mode, cmp1);
break;
case CMP_SF:
case CMP_DF:
if (! ISA_HAS_8CC)
reg = gen_rtx_REG (CCmode, FPSW_REGNUM);
else
reg = gen_reg_rtx (CCmode);
get_float_compare_codes (test_code, &cmp_code, &test_code);
emit_insn (gen_rtx_SET (VOIDmode, reg,
gen_rtx (cmp_code, CCmode, cmp0, cmp1)));
mode = CCmode;
cmp0 = reg;
cmp1 = const0_rtx;
invert = 0;
break;
default:
abort_with_insn (gen_rtx (test_code, VOIDmode, cmp0, cmp1), "bad test");
}
label1 = gen_rtx_LABEL_REF (VOIDmode, operands[0]);
label2 = pc_rtx;
if (invert)
{
label2 = label1;
label1 = pc_rtx;
}
emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
gen_rtx_IF_THEN_ELSE (VOIDmode,
gen_rtx (test_code, mode,
cmp0, cmp1),
label1, label2)));
}
void
gen_conditional_move (operands)
rtx *operands;
{
rtx op0 = branch_cmp[0];
rtx op1 = branch_cmp[1];
enum machine_mode mode = GET_MODE (branch_cmp[0]);
enum rtx_code cmp_code = GET_CODE (operands[1]);
enum rtx_code move_code = NE;
enum machine_mode op_mode = GET_MODE (operands[0]);
enum machine_mode cmp_mode;
rtx cmp_reg;
if (GET_MODE_CLASS (mode) != MODE_FLOAT)
{
switch (cmp_code)
{
case EQ:
cmp_code = XOR;
move_code = EQ;
break;
case NE:
cmp_code = XOR;
break;
case LT:
break;
case GE:
cmp_code = LT;
move_code = EQ;
break;
case GT:
cmp_code = LT;
op0 = force_reg (mode, branch_cmp[1]);
op1 = branch_cmp[0];
break;
case LE:
cmp_code = LT;
op0 = force_reg (mode, branch_cmp[1]);
op1 = branch_cmp[0];
move_code = EQ;
break;
case LTU:
break;
case GEU:
cmp_code = LTU;
move_code = EQ;
break;
case GTU:
cmp_code = LTU;
op0 = force_reg (mode, branch_cmp[1]);
op1 = branch_cmp[0];
break;
case LEU:
cmp_code = LTU;
op0 = force_reg (mode, branch_cmp[1]);
op1 = branch_cmp[0];
move_code = EQ;
break;
default:
abort ();
}
}
else
get_float_compare_codes (cmp_code, &cmp_code, &move_code);
if (mode == SImode || mode == DImode)
cmp_mode = mode;
else if (mode == SFmode || mode == DFmode)
cmp_mode = CCmode;
else
abort ();
cmp_reg = gen_reg_rtx (cmp_mode);
emit_insn (gen_rtx_SET (cmp_mode, cmp_reg,
gen_rtx (cmp_code, cmp_mode, op0, op1)));
emit_insn (gen_rtx_SET (op_mode, operands[0],
gen_rtx_IF_THEN_ELSE (op_mode,
gen_rtx (move_code, VOIDmode,
cmp_reg,
CONST0_RTX (SImode)),
operands[2], operands[3])));
}
void
mips_gen_conditional_trap (operands)
rtx operands[];
{
rtx op0, op1;
enum rtx_code cmp_code = GET_CODE (operands[0]);
enum machine_mode mode = GET_MODE (branch_cmp[0]);
switch (cmp_code)
{
case GT: cmp_code = LT; break;
case LE: cmp_code = GE; break;
case GTU: cmp_code = LTU; break;
case LEU: cmp_code = GEU; break;
default: break;
}
if (cmp_code == GET_CODE (operands[0]))
{
op0 = force_reg (mode, branch_cmp[0]);
op1 = branch_cmp[1];
}
else
{
op0 = force_reg (mode, branch_cmp[1]);
op1 = branch_cmp[0];
}
if (GET_CODE (op1) == CONST_INT && ! SMALL_INT (op1))
op1 = force_reg (mode, op1);
emit_insn (gen_rtx_TRAP_IF (VOIDmode,
gen_rtx (cmp_code, GET_MODE (operands[0]), op0, op1),
operands[1]));
}
int
fcc_register_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return ((mode == VOIDmode || mode == GET_MODE (op))
&& (reload_in_progress || reload_completed)
&& (GET_CODE (op) == REG || GET_CODE (op) == SUBREG)
&& ST_REG_P (true_regnum (op)));
}
void
mips_emit_fcc_reload (dest, src, scratch)
rtx dest, src, scratch;
{
rtx fp1, fp2;
if (GET_CODE (src) == MEM)
src = adjust_address (src, SFmode, 0);
else if (GET_CODE (src) == REG || GET_CODE (src) == SUBREG)
src = gen_rtx_REG (SFmode, true_regnum (src));
fp1 = gen_rtx_REG (SFmode, REGNO (scratch));
fp2 = gen_rtx_REG (SFmode, REGNO (scratch) + FP_INC);
emit_move_insn (copy_rtx (fp1), src);
emit_move_insn (copy_rtx (fp2), CONST0_RTX (SFmode));
emit_insn (gen_slt_sf (dest, fp2, fp1));
}
void
mips_set_return_address (address, scratch)
rtx address, scratch;
{
HOST_WIDE_INT gp_offset;
compute_frame_size (get_frame_size ());
if (((cfun->machine->frame.mask >> 31) & 1) == 0)
abort ();
gp_offset = cfun->machine->frame.gp_sp_offset;
if (gp_offset < 32768)
scratch = plus_constant (stack_pointer_rtx, gp_offset);
else
{
emit_move_insn (scratch, GEN_INT (gp_offset));
if (Pmode == DImode)
emit_insn (gen_adddi3 (scratch, scratch, stack_pointer_rtx));
else
emit_insn (gen_addsi3 (scratch, scratch, stack_pointer_rtx));
}
emit_move_insn (gen_rtx_MEM (GET_MODE (address), scratch), address);
}
#define MAX_MOVE_REGS 4
#define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)
static void
block_move_loop (dest_reg, src_reg, bytes, align, orig_dest, orig_src)
rtx dest_reg;
rtx src_reg;
unsigned int bytes;
int align;
rtx orig_dest;
rtx orig_src;
{
rtx dest_mem = replace_equiv_address (orig_dest, dest_reg);
rtx src_mem = replace_equiv_address (orig_src, src_reg);
rtx align_rtx = GEN_INT (align);
rtx label;
rtx final_src;
rtx bytes_rtx;
int leftover;
if (bytes < (unsigned)2 * MAX_MOVE_BYTES)
abort ();
leftover = bytes % MAX_MOVE_BYTES;
bytes -= leftover;
label = gen_label_rtx ();
final_src = gen_reg_rtx (Pmode);
bytes_rtx = GEN_INT (bytes);
if (bytes > 0x7fff)
{
if (Pmode == DImode)
{
emit_insn (gen_movdi (final_src, bytes_rtx));
emit_insn (gen_adddi3 (final_src, final_src, src_reg));
}
else
{
emit_insn (gen_movsi (final_src, bytes_rtx));
emit_insn (gen_addsi3 (final_src, final_src, src_reg));
}
}
else
{
if (Pmode == DImode)
emit_insn (gen_adddi3 (final_src, src_reg, bytes_rtx));
else
emit_insn (gen_addsi3 (final_src, src_reg, bytes_rtx));
}
emit_label (label);
bytes_rtx = GEN_INT (MAX_MOVE_BYTES);
emit_insn (gen_movstrsi_internal (dest_mem, src_mem, bytes_rtx, align_rtx));
if (Pmode == DImode)
{
emit_insn (gen_adddi3 (src_reg, src_reg, bytes_rtx));
emit_insn (gen_adddi3 (dest_reg, dest_reg, bytes_rtx));
emit_insn (gen_cmpdi (src_reg, final_src));
}
else
{
emit_insn (gen_addsi3 (src_reg, src_reg, bytes_rtx));
emit_insn (gen_addsi3 (dest_reg, dest_reg, bytes_rtx));
emit_insn (gen_cmpsi (src_reg, final_src));
}
emit_jump_insn (gen_bne (label));
if (leftover)
emit_insn (gen_movstrsi_internal (dest_mem, src_mem, GEN_INT (leftover),
align_rtx));
}
static void
block_move_call (dest_reg, src_reg, bytes_rtx)
rtx dest_reg;
rtx src_reg;
rtx bytes_rtx;
{
if (GET_MODE (bytes_rtx) != VOIDmode
&& GET_MODE (bytes_rtx) != (unsigned) Pmode)
bytes_rtx = convert_to_mode (Pmode, bytes_rtx, 1);
#ifdef TARGET_MEM_FUNCTIONS
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "memcpy"), 0,
VOIDmode, 3, dest_reg, Pmode, src_reg, Pmode,
convert_to_mode (TYPE_MODE (sizetype), bytes_rtx,
TREE_UNSIGNED (sizetype)),
TYPE_MODE (sizetype));
#else
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "bcopy"), 0,
VOIDmode, 3, src_reg, Pmode, dest_reg, Pmode,
convert_to_mode (TYPE_MODE (integer_type_node), bytes_rtx,
TREE_UNSIGNED (integer_type_node)),
TYPE_MODE (integer_type_node));
#endif
}
void
expand_block_move (operands)
rtx operands[];
{
rtx bytes_rtx = operands[2];
rtx align_rtx = operands[3];
int constp = GET_CODE (bytes_rtx) == CONST_INT;
unsigned HOST_WIDE_INT bytes = constp ? INTVAL (bytes_rtx) : 0;
unsigned int align = INTVAL (align_rtx);
rtx orig_src = operands[1];
rtx orig_dest = operands[0];
rtx src_reg;
rtx dest_reg;
if (constp && bytes == 0)
return;
if (align > (unsigned) UNITS_PER_WORD)
align = UNITS_PER_WORD;
dest_reg = copy_addr_to_reg (XEXP (orig_dest, 0));
src_reg = copy_addr_to_reg (XEXP (orig_src, 0));
if (TARGET_MEMCPY)
block_move_call (dest_reg, src_reg, bytes_rtx);
else if (constp && bytes <= (unsigned)2 * MAX_MOVE_BYTES
&& align == (unsigned) UNITS_PER_WORD)
move_by_pieces (orig_dest, orig_src, bytes, align * BITS_PER_WORD);
else if (constp && bytes <= (unsigned)2 * MAX_MOVE_BYTES)
emit_insn (gen_movstrsi_internal (replace_equiv_address (orig_dest,
dest_reg),
replace_equiv_address (orig_src,
src_reg),
bytes_rtx, align_rtx));
else if (constp && align >= (unsigned) UNITS_PER_WORD && optimize)
block_move_loop (dest_reg, src_reg, bytes, align, orig_dest, orig_src);
else if (constp && optimize)
{
rtx temp = gen_reg_rtx (Pmode);
rtx aligned_label = gen_label_rtx ();
rtx join_label = gen_label_rtx ();
int leftover = bytes % MAX_MOVE_BYTES;
bytes -= leftover;
if (Pmode == DImode)
{
emit_insn (gen_iordi3 (temp, src_reg, dest_reg));
emit_insn (gen_anddi3 (temp, temp, GEN_INT (UNITS_PER_WORD - 1)));
emit_insn (gen_cmpdi (temp, const0_rtx));
}
else
{
emit_insn (gen_iorsi3 (temp, src_reg, dest_reg));
emit_insn (gen_andsi3 (temp, temp, GEN_INT (UNITS_PER_WORD - 1)));
emit_insn (gen_cmpsi (temp, const0_rtx));
}
emit_jump_insn (gen_beq (aligned_label));
block_move_loop (dest_reg, src_reg, bytes, 1, orig_dest, orig_src);
emit_jump_insn (gen_jump (join_label));
emit_barrier ();
emit_label (aligned_label);
block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD, orig_dest,
orig_src);
emit_label (join_label);
if (leftover)
emit_insn (gen_movstrsi_internal (replace_equiv_address (orig_dest,
dest_reg),
replace_equiv_address (orig_src,
src_reg),
GEN_INT (leftover),
GEN_INT (align)));
}
else
block_move_call (dest_reg, src_reg, bytes_rtx);
}
const char *
output_block_move (insn, operands, num_regs, move_type)
rtx insn;
rtx operands[];
int num_regs;
enum block_move_type move_type;
{
rtx dest_reg = XEXP (operands[0], 0);
rtx src_reg = XEXP (operands[1], 0);
HOST_WIDE_INT bytes = INTVAL (operands[2]);
int align = INTVAL (operands[3]);
int num = 0;
int offset = 0;
int use_lwl_lwr = 0;
int last_operand = num_regs + 4;
int safe_regs = 4;
int i;
rtx xoperands[10];
struct {
const char *load;
const char *load_nop;
const char *store;
const char *final;
const char *last_store;
int offset;
enum machine_mode mode;
} load_store[4];
for (i = 4; i < last_operand && safe_regs < (int) ARRAY_SIZE (xoperands); i++)
if (! reg_mentioned_p (operands[i], operands[0])
&& ! reg_mentioned_p (operands[i], operands[1]))
xoperands[safe_regs++] = operands[i];
if (safe_regs < last_operand)
{
xoperands[0] = operands[0];
xoperands[1] = operands[1];
xoperands[2] = operands[2];
xoperands[3] = operands[3];
return output_block_move (insn, xoperands, safe_regs - 4, move_type);
}
if (num_regs > 2 && (bytes > 2 * align || move_type != BLOCK_MOVE_NORMAL
|| mips_abi == ABI_MEABI
|| mips_abi == ABI_N32
|| mips_abi == ABI_64))
{
if (CONSTANT_P (src_reg))
{
if (TARGET_STATS)
mips_count_memory_refs (operands[1], 1);
src_reg = operands[3 + num_regs--];
if (move_type != BLOCK_MOVE_LAST)
{
xoperands[1] = operands[1];
xoperands[0] = src_reg;
if (Pmode == DImode)
output_asm_insn ("dla\t%0,%1", xoperands);
else
output_asm_insn ("la\t%0,%1", xoperands);
}
}
if (CONSTANT_P (dest_reg))
{
if (TARGET_STATS)
mips_count_memory_refs (operands[0], 1);
dest_reg = operands[3 + num_regs--];
if (move_type != BLOCK_MOVE_LAST)
{
xoperands[1] = operands[0];
xoperands[0] = dest_reg;
if (Pmode == DImode)
output_asm_insn ("dla\t%0,%1", xoperands);
else
output_asm_insn ("la\t%0,%1", xoperands);
}
}
}
if (GET_CODE (src_reg) == LO_SUM)
{
src_reg = operands[3 + num_regs--];
if (move_type != BLOCK_MOVE_LAST)
{
xoperands[2] = XEXP (XEXP (operands[1], 0), 1);
xoperands[1] = XEXP (XEXP (operands[1], 0), 0);
xoperands[0] = src_reg;
if (Pmode == DImode)
output_asm_insn ("daddiu\t%0,%1,%%lo(%2)", xoperands);
else
output_asm_insn ("addiu\t%0,%1,%%lo(%2)", xoperands);
}
}
if (GET_CODE (dest_reg) == LO_SUM)
{
dest_reg = operands[3 + num_regs--];
if (move_type != BLOCK_MOVE_LAST)
{
xoperands[2] = XEXP (XEXP (operands[0], 0), 1);
xoperands[1] = XEXP (XEXP (operands[0], 0), 0);
xoperands[0] = dest_reg;
if (Pmode == DImode)
output_asm_insn ("daddiu\t%0,%1,%%lo(%2)", xoperands);
else
output_asm_insn ("addiu\t%0,%1,%%lo(%2)", xoperands);
}
}
if (num_regs > (int) ARRAY_SIZE (load_store))
num_regs = ARRAY_SIZE (load_store);
else if (num_regs < 1)
abort_with_insn (insn,
"cannot do block move, not enough scratch registers");
while (bytes > 0)
{
load_store[num].offset = offset;
if (TARGET_64BIT && bytes >= 8 && align >= 8)
{
load_store[num].load = "ld\t%0,%1";
load_store[num].load_nop = "ld\t%0,%1%#";
load_store[num].store = "sd\t%0,%1";
load_store[num].last_store = "sd\t%0,%1";
load_store[num].final = 0;
load_store[num].mode = DImode;
offset += 8;
bytes -= 8;
}
else if (TARGET_64BIT && bytes >= 8
&& ! TARGET_SR71K
&& ! TARGET_MIPS16)
{
if (BYTES_BIG_ENDIAN)
{
load_store[num].load = "ldl\t%0,%1\n\tldr\t%0,%2";
load_store[num].load_nop = "ldl\t%0,%1\n\tldr\t%0,%2%#";
load_store[num].store = "sdl\t%0,%1\n\tsdr\t%0,%2";
load_store[num].last_store = "sdr\t%0,%2";
load_store[num].final = "sdl\t%0,%1";
}
else
{
load_store[num].load = "ldl\t%0,%2\n\tldr\t%0,%1";
load_store[num].load_nop = "ldl\t%0,%2\n\tldr\t%0,%1%#";
load_store[num].store = "sdl\t%0,%2\n\tsdr\t%0,%1";
load_store[num].last_store = "sdr\t%0,%1";
load_store[num].final = "sdl\t%0,%2";
}
load_store[num].mode = DImode;
offset += 8;
bytes -= 8;
use_lwl_lwr = 1;
}
else if (bytes >= 4 && align >= 4)
{
load_store[num].load = "lw\t%0,%1";
load_store[num].load_nop = "lw\t%0,%1%#";
load_store[num].store = "sw\t%0,%1";
load_store[num].last_store = "sw\t%0,%1";
load_store[num].final = 0;
load_store[num].mode = SImode;
offset += 4;
bytes -= 4;
}
else if (bytes >= 4
&& ! TARGET_SR71K
&& ! TARGET_MIPS16)
{
if (BYTES_BIG_ENDIAN)
{
load_store[num].load = "lwl\t%0,%1\n\tlwr\t%0,%2";
load_store[num].load_nop = "lwl\t%0,%1\n\tlwr\t%0,%2%#";
load_store[num].store = "swl\t%0,%1\n\tswr\t%0,%2";
load_store[num].last_store = "swr\t%0,%2";
load_store[num].final = "swl\t%0,%1";
}
else
{
load_store[num].load = "lwl\t%0,%2\n\tlwr\t%0,%1";
load_store[num].load_nop = "lwl\t%0,%2\n\tlwr\t%0,%1%#";
load_store[num].store = "swl\t%0,%2\n\tswr\t%0,%1";
load_store[num].last_store = "swr\t%0,%1";
load_store[num].final = "swl\t%0,%2";
}
load_store[num].mode = SImode;
offset += 4;
bytes -= 4;
use_lwl_lwr = 1;
}
else if (bytes >= 2 && align >= 2)
{
load_store[num].load = "lh\t%0,%1";
load_store[num].load_nop = "lh\t%0,%1%#";
load_store[num].store = "sh\t%0,%1";
load_store[num].last_store = "sh\t%0,%1";
load_store[num].final = 0;
load_store[num].mode = HImode;
offset += 2;
bytes -= 2;
}
else
{
load_store[num].load = "lb\t%0,%1";
load_store[num].load_nop = "lb\t%0,%1%#";
load_store[num].store = "sb\t%0,%1";
load_store[num].last_store = "sb\t%0,%1";
load_store[num].final = 0;
load_store[num].mode = QImode;
offset++;
bytes--;
}
if (TARGET_STATS && move_type != BLOCK_MOVE_LAST)
{
dslots_load_total++;
dslots_load_filled++;
if (CONSTANT_P (src_reg))
mips_count_memory_refs (src_reg, 1);
if (CONSTANT_P (dest_reg))
mips_count_memory_refs (dest_reg, 1);
}
if (++num == num_regs || bytes == 0)
{
if (num == 1)
{
load_store[0].load = load_store[0].load_nop;
if (TARGET_STATS && move_type != BLOCK_MOVE_LAST)
dslots_load_filled--;
}
if (move_type != BLOCK_MOVE_LAST)
{
for (i = 0; i < num; i++)
{
int offset;
if (!operands[i + 4])
abort ();
if (GET_MODE (operands[i + 4]) != load_store[i].mode)
operands[i + 4] = gen_rtx_REG (load_store[i].mode,
REGNO (operands[i + 4]));
offset = load_store[i].offset;
xoperands[0] = operands[i + 4];
xoperands[1] = gen_rtx_MEM (load_store[i].mode,
plus_constant (src_reg, offset));
if (use_lwl_lwr)
{
int extra_offset
= GET_MODE_SIZE (load_store[i].mode) - 1;
xoperands[2] = gen_rtx_MEM (load_store[i].mode,
plus_constant (src_reg,
extra_offset
+ offset));
}
output_asm_insn (load_store[i].load, xoperands);
}
}
for (i = 0; i < num; i++)
{
int last_p = (i == num-1 && bytes == 0);
int offset = load_store[i].offset;
xoperands[0] = operands[i + 4];
xoperands[1] = gen_rtx_MEM (load_store[i].mode,
plus_constant (dest_reg, offset));
if (use_lwl_lwr)
{
int extra_offset = GET_MODE_SIZE (load_store[i].mode) - 1;
xoperands[2] = gen_rtx_MEM (load_store[i].mode,
plus_constant (dest_reg,
extra_offset
+ offset));
}
if (move_type == BLOCK_MOVE_NORMAL)
output_asm_insn (load_store[i].store, xoperands);
else if (move_type == BLOCK_MOVE_NOT_LAST)
{
if (!last_p)
output_asm_insn (load_store[i].store, xoperands);
else if (load_store[i].final != 0)
output_asm_insn (load_store[i].final, xoperands);
}
else if (last_p)
output_asm_insn (load_store[i].last_store, xoperands);
}
num = 0;
use_lwl_lwr = 0;
}
}
return "";
}
void
init_cumulative_args (cum, fntype, libname)
CUMULATIVE_ARGS *cum;
tree fntype;
rtx libname ATTRIBUTE_UNUSED;
{
static CUMULATIVE_ARGS zero_cum;
tree param, next_param;
if (TARGET_DEBUG_E_MODE)
{
fprintf (stderr,
"\ninit_cumulative_args, fntype = 0x%.8lx", (long)fntype);
if (!fntype)
fputc ('\n', stderr);
else
{
tree ret_type = TREE_TYPE (fntype);
fprintf (stderr, ", fntype code = %s, ret code = %s\n",
tree_code_name[(int)TREE_CODE (fntype)],
tree_code_name[(int)TREE_CODE (ret_type)]);
}
}
*cum = zero_cum;
cum->prototype = (fntype && TYPE_ARG_TYPES (fntype));
for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0;
param != 0; param = next_param)
{
next_param = TREE_CHAIN (param);
if (next_param == 0 && TREE_VALUE (param) != void_type_node)
cum->gp_reg_found = 1;
}
}
static void
mips_arg_info (cum, mode, type, named, info)
const CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int named;
struct mips_arg_info *info;
{
bool even_reg_p;
unsigned int num_words, max_regs;
info->struct_p = (type != 0
&& (TREE_CODE (type) == RECORD_TYPE
|| TREE_CODE (type) == UNION_TYPE
|| TREE_CODE (type) == QUAL_UNION_TYPE));
info->fpr_p = false;
if (GET_MODE_CLASS (mode) == MODE_FLOAT
&& GET_MODE_SIZE (mode) <= UNITS_PER_FPVALUE)
{
switch (mips_abi)
{
case ABI_32:
case ABI_O64:
info->fpr_p = (!cum->gp_reg_found && cum->arg_number < 2);
break;
case ABI_EABI:
info->fpr_p = true;
break;
case ABI_MEABI:
info->fpr_p = (named && !(mode == SFmode && info->struct_p));
break;
default:
info->fpr_p = named;
break;
}
}
even_reg_p = false;
if (info->fpr_p)
{
even_reg_p = (GET_MODE_SIZE (mode) > UNITS_PER_HWFPVALUE
|| (mips_abi == ABI_O64 && mode == SFmode)
|| FP_INC > 1);
}
else if (!TARGET_64BIT || LONG_DOUBLE_TYPE_SIZE == 128)
{
if (GET_MODE_CLASS (mode) == MODE_INT
|| GET_MODE_CLASS (mode) == MODE_FLOAT)
even_reg_p = (GET_MODE_SIZE (mode) > UNITS_PER_WORD);
else if (type != NULL_TREE && TYPE_ALIGN (type) > BITS_PER_WORD)
even_reg_p = true;
}
info->reg_offset = (mips_abi == ABI_EABI && info->fpr_p
? cum->num_fprs
: cum->num_gprs);
if (even_reg_p)
info->reg_offset += info->reg_offset & 1;
info->stack_offset = cum->stack_words;
if (even_reg_p)
info->stack_offset += info->stack_offset & 1;
if (mode == BLKmode)
info->num_bytes = int_size_in_bytes (type);
else
info->num_bytes = GET_MODE_SIZE (mode);
num_words = (info->num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
max_regs = MAX_ARGS_IN_REGISTERS - info->reg_offset;
info->reg_words = MIN (num_words, max_regs);
info->stack_words = num_words - info->reg_words;
}
void
function_arg_advance (cum, mode, type, named)
CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int named;
{
struct mips_arg_info info;
mips_arg_info (cum, mode, type, named, &info);
if (info.struct_p
&& info.reg_words == 1
&& info.num_bytes < UNITS_PER_WORD
&& !TARGET_64BIT
&& mips_abi != ABI_EABI
&& mips_abi != ABI_MEABI)
{
rtx amount = GEN_INT (BITS_PER_WORD - info.num_bytes * BITS_PER_UNIT);
rtx reg = gen_rtx_REG (word_mode, GP_ARG_FIRST + info.reg_offset);
if (TARGET_64BIT)
cum->adjust[cum->num_adjusts++] = PATTERN (gen_ashldi3 (reg, reg, amount));
else
cum->adjust[cum->num_adjusts++] = PATTERN (gen_ashlsi3 (reg, reg, amount));
}
if (!info.fpr_p)
cum->gp_reg_found = true;
if (cum->arg_number < 2 && info.fpr_p)
cum->fp_code += (mode == SFmode ? 1 : 2) << ((cum->arg_number - 1) * 2);
if (mips_abi != ABI_EABI || !info.fpr_p)
cum->num_gprs = info.reg_offset + info.reg_words;
else if (info.reg_words > 0)
cum->num_fprs += FP_INC;
if (info.stack_words > 0)
cum->stack_words = info.stack_offset + info.stack_words;
cum->arg_number++;
}
struct rtx_def *
function_arg (cum, mode, type, named)
const CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int named;
{
struct mips_arg_info info;
if (mode == VOIDmode)
{
if (cum->num_adjusts > 0)
return gen_rtx_PARALLEL ((enum machine_mode) cum->fp_code,
gen_rtvec_v (cum->num_adjusts,
(rtx *) cum->adjust));
else if (TARGET_MIPS16 && cum->fp_code != 0)
return gen_rtx_REG ((enum machine_mode) cum->fp_code, 0);
else
return 0;
}
mips_arg_info (cum, mode, type, named, &info);
if (info.reg_offset == MAX_ARGS_IN_REGISTERS)
return 0;
if (type != 0
&& TREE_CODE (type) == RECORD_TYPE
&& (mips_abi == ABI_N32 || mips_abi == ABI_64)
&& TYPE_SIZE_UNIT (type)
&& host_integerp (TYPE_SIZE_UNIT (type), 1)
&& named
&& mode != DFmode)
{
tree field;
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL
&& TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
&& TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
&& host_integerp (bit_position (field), 0)
&& int_bit_position (field) % BITS_PER_WORD == 0)
break;
if (field != 0)
{
unsigned int i;
HOST_WIDE_INT bitpos;
rtx ret;
ret = gen_rtx_PARALLEL (mode, rtvec_alloc (info.reg_words));
bitpos = 0;
field = TYPE_FIELDS (type);
for (i = 0; i < info.reg_words; i++)
{
rtx reg;
for (; field; field = TREE_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL
&& int_bit_position (field) >= bitpos)
break;
if (field
&& int_bit_position (field) == bitpos
&& TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
&& !TARGET_SOFT_FLOAT
&& TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
reg = gen_rtx_REG (DFmode, FP_ARG_FIRST + info.reg_offset + i);
else
reg = gen_rtx_REG (DImode, GP_ARG_FIRST + info.reg_offset + i);
XVECEXP (ret, 0, i)
= gen_rtx_EXPR_LIST (VOIDmode, reg,
GEN_INT (bitpos / BITS_PER_UNIT));
bitpos += BITS_PER_WORD;
}
return ret;
}
}
if (mips_abi == ABI_MEABI && info.fpr_p && !cum->prototype)
{
return gen_rtx_PARALLEL
(mode,
gen_rtvec (2,
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (mode,
GP_ARG_FIRST
+ info.reg_offset),
const0_rtx),
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (mode,
FP_ARG_FIRST
+ info.reg_offset),
const0_rtx)));
}
if (info.fpr_p)
return gen_rtx_REG (mode, FP_ARG_FIRST + info.reg_offset);
else
return gen_rtx_REG (mode, GP_ARG_FIRST + info.reg_offset);
}
int
function_arg_partial_nregs (cum, mode, type, named)
const CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int named;
{
struct mips_arg_info info;
mips_arg_info (cum, mode, type, named, &info);
return info.stack_words > 0 ? info.reg_words : 0;
}
int
mips_setup_incoming_varargs (cum, mode, type, no_rtl)
const CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int no_rtl;
{
CUMULATIVE_ARGS local_cum;
int gp_saved, fp_saved;
if (mips_abi == ABI_32 || mips_abi == ABI_O64)
return 0;
local_cum = *cum;
FUNCTION_ARG_ADVANCE (local_cum, mode, type, 1);
gp_saved = MAX_ARGS_IN_REGISTERS - local_cum.num_gprs;
fp_saved = (EABI_FLOAT_VARARGS_P
? MAX_ARGS_IN_REGISTERS - local_cum.num_fprs
: 0);
if (!no_rtl)
{
if (gp_saved > 0)
{
rtx ptr, mem;
ptr = virtual_incoming_args_rtx;
if (mips_abi == ABI_EABI)
ptr = plus_constant (ptr, -gp_saved * UNITS_PER_WORD);
mem = gen_rtx_MEM (BLKmode, ptr);
if (mips_abi != ABI_EABI && BYTES_BIG_ENDIAN)
MEM_SET_IN_STRUCT_P (mem, 1);
move_block_from_reg (local_cum.num_gprs + GP_ARG_FIRST, mem,
gp_saved, gp_saved * UNITS_PER_WORD);
}
if (fp_saved > 0)
{
enum machine_mode mode;
int off, i;
off = -gp_saved * UNITS_PER_WORD;
off &= ~(UNITS_PER_FPVALUE - 1);
off -= fp_saved * UNITS_PER_FPREG;
mode = TARGET_SINGLE_FLOAT ? SFmode : DFmode;
for (i = local_cum.num_fprs; i < MAX_ARGS_IN_REGISTERS; i += FP_INC)
{
rtx ptr = plus_constant (virtual_incoming_args_rtx, off);
emit_move_insn (gen_rtx_MEM (mode, ptr),
gen_rtx_REG (mode, FP_ARG_FIRST + i));
off += UNITS_PER_HWFPVALUE;
}
}
}
return (gp_saved * UNITS_PER_WORD) + (fp_saved * UNITS_PER_FPREG);
}
tree
mips_build_va_list ()
{
if (EABI_FLOAT_VARARGS_P)
{
tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff, record;
record = make_node (RECORD_TYPE);
f_ovfl = build_decl (FIELD_DECL, get_identifier ("__overflow_argptr"),
ptr_type_node);
f_gtop = build_decl (FIELD_DECL, get_identifier ("__gpr_top"),
ptr_type_node);
f_ftop = build_decl (FIELD_DECL, get_identifier ("__fpr_top"),
ptr_type_node);
f_goff = build_decl (FIELD_DECL, get_identifier ("__gpr_offset"),
unsigned_char_type_node);
f_foff = build_decl (FIELD_DECL, get_identifier ("__fpr_offset"),
unsigned_char_type_node);
DECL_FIELD_CONTEXT (f_ovfl) = record;
DECL_FIELD_CONTEXT (f_gtop) = record;
DECL_FIELD_CONTEXT (f_ftop) = record;
DECL_FIELD_CONTEXT (f_goff) = record;
DECL_FIELD_CONTEXT (f_foff) = record;
TYPE_FIELDS (record) = f_ovfl;
TREE_CHAIN (f_ovfl) = f_gtop;
TREE_CHAIN (f_gtop) = f_ftop;
TREE_CHAIN (f_ftop) = f_goff;
TREE_CHAIN (f_goff) = f_foff;
layout_type (record);
return record;
}
else
return ptr_type_node;
}
void
mips_va_start (valist, nextarg)
tree valist;
rtx nextarg;
{
const CUMULATIVE_ARGS *cum = ¤t_function_args_info;
if (cfun && cfun->emit->regno_pointer_align)
while (((current_function_pretend_args_size * BITS_PER_UNIT)
& (REGNO_POINTER_ALIGN (ARG_POINTER_REGNUM) - 1)) != 0)
REGNO_POINTER_ALIGN (ARG_POINTER_REGNUM) /= 2;
if (mips_abi == ABI_EABI)
{
int gpr_save_area_size;
gpr_save_area_size
= (MAX_ARGS_IN_REGISTERS - cum->num_gprs) * UNITS_PER_WORD;
if (EABI_FLOAT_VARARGS_P)
{
tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
tree ovfl, gtop, ftop, goff, foff;
tree t;
int fpr_offset;
int fpr_save_area_size;
f_ovfl = TYPE_FIELDS (va_list_type_node);
f_gtop = TREE_CHAIN (f_ovfl);
f_ftop = TREE_CHAIN (f_gtop);
f_goff = TREE_CHAIN (f_ftop);
f_foff = TREE_CHAIN (f_goff);
ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl);
gtop = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop);
ftop = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop);
goff = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
foff = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
t = make_tree (TREE_TYPE (ovfl), virtual_incoming_args_rtx);
if (cum->stack_words > 0)
t = build (PLUS_EXPR, TREE_TYPE (ovfl), t,
build_int_2 (cum->stack_words * UNITS_PER_WORD, 0));
t = build (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
t = make_tree (TREE_TYPE (gtop), virtual_incoming_args_rtx);
t = build (MODIFY_EXPR, TREE_TYPE (gtop), gtop, t);
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
t = make_tree (TREE_TYPE (ftop), virtual_incoming_args_rtx);
fpr_offset = gpr_save_area_size + UNITS_PER_FPVALUE - 1;
fpr_offset &= ~(UNITS_PER_FPVALUE - 1);
if (fpr_offset)
t = build (PLUS_EXPR, TREE_TYPE (ftop), t,
build_int_2 (-fpr_offset, -1));
t = build (MODIFY_EXPR, TREE_TYPE (ftop), ftop, t);
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
t = build (MODIFY_EXPR, TREE_TYPE (goff), goff,
build_int_2 (gpr_save_area_size, 0));
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
fpr_save_area_size
= (MAX_ARGS_IN_REGISTERS - cum->num_fprs) * UNITS_PER_FPREG;
t = build (MODIFY_EXPR, TREE_TYPE (foff), foff,
build_int_2 (fpr_save_area_size, 0));
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
else
{
nextarg = plus_constant (nextarg, -gpr_save_area_size);
std_expand_builtin_va_start (valist, nextarg);
}
}
else
std_expand_builtin_va_start (valist, nextarg);
}
rtx
mips_va_arg (valist, type)
tree valist, type;
{
HOST_WIDE_INT size, rsize;
rtx addr_rtx;
tree t;
size = int_size_in_bytes (type);
rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
if (mips_abi == ABI_EABI)
{
bool indirect;
rtx r;
indirect
= function_arg_pass_by_reference (NULL, TYPE_MODE (type), type, 0);
if (indirect)
{
size = POINTER_SIZE / BITS_PER_UNIT;
rsize = UNITS_PER_WORD;
}
addr_rtx = gen_reg_rtx (Pmode);
if (!EABI_FLOAT_VARARGS_P)
{
tree gpr = valist;
if (!indirect
&& !TARGET_64BIT
&& TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD)
{
t = build (PLUS_EXPR, TREE_TYPE (gpr), gpr,
build_int_2 (2 * UNITS_PER_WORD - 1, 0));
t = build (BIT_AND_EXPR, TREE_TYPE (t), t,
build_int_2 (-2 * UNITS_PER_WORD, -1));
t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, t);
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
t = build (POSTINCREMENT_EXPR, TREE_TYPE (gpr), gpr,
size_int (rsize));
r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
if (r != addr_rtx)
emit_move_insn (addr_rtx, r);
emit_queue();
}
else
{
tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
tree ovfl, top, off;
rtx lab_over = NULL_RTX, lab_false;
HOST_WIDE_INT osize;
f_ovfl = TYPE_FIELDS (va_list_type_node);
f_gtop = TREE_CHAIN (f_ovfl);
f_ftop = TREE_CHAIN (f_gtop);
f_goff = TREE_CHAIN (f_ftop);
f_foff = TREE_CHAIN (f_goff);
lab_false = gen_label_rtx ();
lab_over = gen_label_rtx ();
ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl);
if (TREE_CODE (type) == REAL_TYPE)
{
top = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop);
off = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
rsize = UNITS_PER_HWFPVALUE;
}
else
{
top = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop);
off = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
if (rsize > UNITS_PER_WORD)
{
t = build (BIT_AND_EXPR, TREE_TYPE (off), off,
build_int_2 (-rsize, -1));
t = build (MODIFY_EXPR, TREE_TYPE (off), off, t);
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
}
osize = MAX (rsize, UNITS_PER_WORD);
r = expand_expr (off, NULL_RTX, TYPE_MODE (TREE_TYPE (off)),
EXPAND_NORMAL);
emit_cmp_and_jump_insns (r, const0_rtx, EQ, const1_rtx, GET_MODE (r),
1, lab_false);
t = build (MINUS_EXPR, TREE_TYPE (top), top, off);
r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
if (r != addr_rtx)
emit_move_insn (addr_rtx, r);
t = build (MINUS_EXPR, TREE_TYPE (off), off, build_int_2 (rsize, 0));
t = build (MODIFY_EXPR, TREE_TYPE (off), off, t);
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
emit_queue();
emit_jump (lab_over);
emit_barrier ();
emit_label (lab_false);
if (osize > UNITS_PER_WORD)
{
t = build (PLUS_EXPR, TREE_TYPE (ovfl), ovfl,
build_int_2 (osize - 1, 0));
t = build (BIT_AND_EXPR, TREE_TYPE (ovfl), t,
build_int_2 (-osize, -1));
t = build (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
t = build (POSTINCREMENT_EXPR, TREE_TYPE (ovfl), ovfl,
size_int (osize));
if (BYTES_BIG_ENDIAN && osize > rsize)
t = build (PLUS_EXPR, TREE_TYPE (t), t,
build_int_2 (osize - rsize, 0));
r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
if (r != addr_rtx)
emit_move_insn (addr_rtx, r);
emit_queue();
emit_label (lab_over);
}
if (indirect)
{
addr_rtx = force_reg (Pmode, addr_rtx);
r = gen_rtx_MEM (Pmode, addr_rtx);
set_mem_alias_set (r, get_varargs_alias_set ());
emit_move_insn (addr_rtx, r);
}
else
{
if (BYTES_BIG_ENDIAN && rsize != size)
addr_rtx = plus_constant (addr_rtx, rsize - size);
}
return addr_rtx;
}
else
{
int align;
if ((mips_abi == ABI_N32 || mips_abi == ABI_64)
&& TYPE_ALIGN (type) > 64)
align = 16;
else if (TARGET_64BIT)
align = 8;
else if (TYPE_ALIGN (type) > 32)
align = 8;
else
align = 4;
t = build (PLUS_EXPR, TREE_TYPE (valist), valist,
build_int_2 (align - 1, 0));
t = build (BIT_AND_EXPR, TREE_TYPE (t), t, build_int_2 (-align, -1));
t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, t);
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
return std_expand_builtin_va_arg (valist, type);
}
}
static void
abort_with_insn (insn, reason)
rtx insn;
const char *reason;
{
error (reason);
debug_rtx (insn);
abort ();
}
static void
mips_set_architecture (info)
const struct mips_cpu_info *info;
{
if (info != 0)
{
mips_arch_info = info;
mips_arch = info->cpu;
mips_isa = info->isa;
}
}
static void
mips_set_tune (info)
const struct mips_cpu_info *info;
{
if (info != 0)
{
mips_tune_info = info;
mips_tune = info->cpu;
}
}
void
override_options ()
{
int i, start, regno;
enum machine_mode mode;
mips_section_threshold = g_switch_set ? g_switch_value : MIPS_DEFAULT_GVALUE;
if (mips_section_threshold <= 0)
target_flags &= ~MASK_GPOPT;
else if (optimize)
target_flags |= MASK_GPOPT;
#ifdef TARGET_DEFAULT
if (TARGET_SINGLE_FLOAT && TARGET_SOFT_FLOAT)
target_flags &= ~((TARGET_DEFAULT) & (MASK_SOFT_FLOAT | MASK_SINGLE_FLOAT));
#endif
mips_abi = MIPS_ABI_DEFAULT;
if (mips_abi_string != 0)
{
if (strcmp (mips_abi_string, "32") == 0)
mips_abi = ABI_32;
else if (strcmp (mips_abi_string, "o64") == 0)
mips_abi = ABI_O64;
else if (strcmp (mips_abi_string, "n32") == 0)
mips_abi = ABI_N32;
else if (strcmp (mips_abi_string, "64") == 0)
mips_abi = ABI_64;
else if (strcmp (mips_abi_string, "eabi") == 0)
mips_abi = ABI_EABI;
else if (strcmp (mips_abi_string, "meabi") == 0)
mips_abi = ABI_MEABI;
else
fatal_error ("bad value (%s) for -mabi= switch", mips_abi_string);
}
if (mips_arch_string != 0)
mips_set_architecture (mips_parse_cpu ("-march", mips_arch_string));
if (mips_tune_string != 0)
mips_set_tune (mips_parse_cpu ("-mtune", mips_tune_string));
if (mips_isa_string != 0)
{
int level = atoi (mips_isa_string);
if (level == 16)
{
if (mips_no_mips16_string == NULL)
target_flags |= MASK_MIPS16;
}
else if (mips_arch_info != 0)
{
if (mips_isa != level)
error ("-mips%d conflicts with the other architecture options, which specify a MIPS%d processor",
level, mips_isa);
}
else
{
mips_set_architecture (mips_cpu_info_from_isa (level));
if (mips_arch_info == 0)
error ("bad value (%s) for -mips switch", mips_isa_string);
}
}
if (mips_arch_info == 0)
{
#ifdef MIPS_CPU_STRING_DEFAULT
mips_set_architecture (mips_parse_cpu ("default CPU",
MIPS_CPU_STRING_DEFAULT));
#else
mips_set_architecture (mips_cpu_info_from_isa (MIPS_ISA_DEFAULT));
#endif
}
if (ABI_NEEDS_64BIT_REGS && !ISA_HAS_64BIT_REGS)
error ("-march=%s is not compatible with the selected ABI",
mips_arch_info->name);
if (mips_tune_info == 0)
mips_set_tune (mips_arch_info);
if ((target_flags_explicit & MASK_64BIT) != 0)
{
if (TARGET_64BIT && !ISA_HAS_64BIT_REGS)
error ("-mgp64 used with a 32-bit processor");
else if (!TARGET_64BIT && ABI_NEEDS_64BIT_REGS)
error ("-mgp32 used with a 64-bit ABI");
else if (TARGET_64BIT && ABI_NEEDS_32BIT_REGS)
error ("-mgp64 used with a 32-bit ABI");
}
else
{
if (ABI_NEEDS_32BIT_REGS || !ISA_HAS_64BIT_REGS)
target_flags &= ~MASK_64BIT;
else
target_flags |= MASK_64BIT;
}
if ((target_flags_explicit & MASK_FLOAT64) != 0)
{
if (TARGET_64BIT && TARGET_DOUBLE_FLOAT && !TARGET_FLOAT64)
error ("unsupported combination: %s", "-mgp64 -mfp32 -mdouble-float");
else if (!TARGET_64BIT && TARGET_FLOAT64)
error ("unsupported combination: %s", "-mgp32 -mfp64");
else if (TARGET_SINGLE_FLOAT && TARGET_FLOAT64)
error ("unsupported combination: %s", "-mfp64 -msingle-float");
}
else
{
if (TARGET_64BIT && TARGET_DOUBLE_FLOAT)
target_flags |= MASK_FLOAT64;
else
target_flags &= ~MASK_FLOAT64;
}
if ((target_flags_explicit & MASK_LONG64) == 0)
{
if ((mips_abi == ABI_EABI && TARGET_64BIT) || mips_abi == ABI_64)
target_flags |= MASK_LONG64;
else
target_flags &= ~MASK_LONG64;
}
if (MIPS_MARCH_CONTROLS_SOFT_FLOAT
&& (target_flags_explicit & MASK_SOFT_FLOAT) == 0)
{
switch ((int) mips_arch)
{
case PROCESSOR_R4100:
case PROCESSOR_R4120:
target_flags |= MASK_SOFT_FLOAT;
break;
default:
target_flags &= ~MASK_SOFT_FLOAT;
break;
}
}
if (mips_abi != ABI_32 && mips_abi != ABI_O64)
flag_pcc_struct_return = 0;
if ((target_flags_explicit & MASK_BRANCHLIKELY) == 0)
{
if (ISA_HAS_BRANCHLIKELY && !(ISA_MIPS32 || ISA_MIPS64))
target_flags |= MASK_BRANCHLIKELY;
else
target_flags &= ~MASK_BRANCHLIKELY;
}
if (TARGET_BRANCHLIKELY && !ISA_HAS_BRANCHLIKELY)
warning ("generation of Branch Likely instructions enabled, but not supported by architecture");
if (TARGET_ABICALLS)
{
mips_abicalls = MIPS_ABICALLS_YES;
flag_pic = 1;
if (mips_section_threshold > 0)
warning ("-G is incompatible with PIC code which is the default");
}
else
mips_abicalls = MIPS_ABICALLS_NO;
if (TARGET_EMBEDDED_PIC)
{
flag_pic = 1;
if (TARGET_ABICALLS)
warning ("-membedded-pic and -mabicalls are incompatible");
if (g_switch_set)
warning ("-G and -membedded-pic are incompatible");
mips_section_threshold = 0x7fffffff;
}
if (TARGET_GAS && ! TARGET_MIPS16 && TARGET_SPLIT_ADDRESSES && optimize && ! flag_pic
&& Pmode == SImode)
mips_split_addresses = 1;
else
mips_split_addresses = 0;
if (TARGET_NAME_REGS)
memcpy (mips_reg_names, mips_sw_reg_names, sizeof (mips_reg_names));
if (TARGET_MIPS16)
{
if (TARGET_SOFT_FLOAT)
mips16_hard_float = 0;
else
mips16_hard_float = 1;
target_flags |= MASK_SOFT_FLOAT;
flag_schedule_insns = 0;
}
if (mips_entry_string != NULL)
{
if (*mips_entry_string != '\0')
error ("invalid option `entry%s'", mips_entry_string);
if (! TARGET_MIPS16)
warning ("-mentry is only meaningful with -mips-16");
else
mips_entry = 1;
}
if (TARGET_MIPS16)
mips16 = 1;
else
mips16 = 0;
#ifdef MIPS_TFMODE_FORMAT
real_format_for_mode[TFmode - QFmode] = &MIPS_TFMODE_FORMAT;
#endif
mips_print_operand_punct['?'] = 1;
mips_print_operand_punct['#'] = 1;
mips_print_operand_punct['&'] = 1;
mips_print_operand_punct['!'] = 1;
mips_print_operand_punct['*'] = 1;
mips_print_operand_punct['@'] = 1;
mips_print_operand_punct['.'] = 1;
mips_print_operand_punct['('] = 1;
mips_print_operand_punct[')'] = 1;
mips_print_operand_punct['['] = 1;
mips_print_operand_punct[']'] = 1;
mips_print_operand_punct['<'] = 1;
mips_print_operand_punct['>'] = 1;
mips_print_operand_punct['{'] = 1;
mips_print_operand_punct['}'] = 1;
mips_print_operand_punct['^'] = 1;
mips_print_operand_punct['$'] = 1;
mips_print_operand_punct['+'] = 1;
mips_print_operand_punct['~'] = 1;
mips_char_to_class['d'] = TARGET_MIPS16 ? M16_REGS : GR_REGS;
mips_char_to_class['e'] = M16_NA_REGS;
mips_char_to_class['t'] = T_REG;
mips_char_to_class['f'] = (TARGET_HARD_FLOAT ? FP_REGS : NO_REGS);
mips_char_to_class['h'] = HI_REG;
mips_char_to_class['l'] = LO_REG;
mips_char_to_class['a'] = HILO_REG;
mips_char_to_class['x'] = MD_REGS;
mips_char_to_class['b'] = ALL_REGS;
mips_char_to_class['y'] = GR_REGS;
mips_char_to_class['z'] = ST_REGS;
mips_char_to_class['B'] = COP0_REGS;
mips_char_to_class['C'] = COP2_REGS;
mips_char_to_class['D'] = COP3_REGS;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
mips_dbx_regno[i] = -1;
start = GP_DBX_FIRST - GP_REG_FIRST;
for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++)
mips_dbx_regno[i] = i + start;
start = FP_DBX_FIRST - FP_REG_FIRST;
for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++)
mips_dbx_regno[i] = i + start;
for (mode = VOIDmode;
mode != MAX_MACHINE_MODE;
mode = (enum machine_mode) ((int)mode + 1))
{
register int size = GET_MODE_SIZE (mode);
register enum mode_class class = GET_MODE_CLASS (mode);
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
register int temp;
if (mode == CCmode)
{
if (! ISA_HAS_8CC)
temp = (regno == FPSW_REGNUM);
else
temp = (ST_REG_P (regno) || GP_REG_P (regno)
|| FP_REG_P (regno));
}
else if (GP_REG_P (regno))
temp = ((regno & 1) == 0 || size <= UNITS_PER_WORD);
else if (FP_REG_P (regno))
temp = (((regno % FP_INC) == 0
|| (mips_abi == ABI_MEABI && size <= 4))
&& (((class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
&& size <= UNITS_PER_FPVALUE)
|| (class == MODE_INT && size <= UNITS_PER_FPREG)
|| (ISA_HAS_8CC && mode == TFmode)));
else if (MD_REG_P (regno))
temp = (class == MODE_INT
&& (size <= UNITS_PER_WORD
|| (regno == MD_REG_FIRST
&& size == 2 * UNITS_PER_WORD)));
else if (ALL_COP_REG_P (regno))
temp = (class == MODE_INT && size <= UNITS_PER_WORD);
else
temp = 0;
mips_hard_regno_mode_ok[(int)mode][regno] = temp;
}
}
gpr_mode = TARGET_64BIT ? DImode : SImode;
if (TARGET_64BIT && !TARGET_MIPS16)
{
if (align_loops == 0)
align_loops = 8;
if (align_jumps == 0)
align_jumps = 8;
if (align_functions == 0)
align_functions = 8;
}
init_machine_status = &mips_init_machine_status;
}
void
mips_conditional_register_usage ()
{
if (!TARGET_HARD_FLOAT)
{
int regno;
for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++)
fixed_regs[regno] = call_used_regs[regno] = 1;
for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++)
fixed_regs[regno] = call_used_regs[regno] = 1;
}
else if (! ISA_HAS_8CC)
{
int regno;
for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++)
fixed_regs[regno] = call_used_regs[regno] = 1;
}
if (TARGET_MIPS16)
{
fixed_regs[18] = call_used_regs[18] = 1;
fixed_regs[19] = call_used_regs[19] = 1;
fixed_regs[20] = call_used_regs[20] = 1;
fixed_regs[21] = call_used_regs[21] = 1;
fixed_regs[22] = call_used_regs[22] = 1;
fixed_regs[23] = call_used_regs[23] = 1;
fixed_regs[26] = call_used_regs[26] = 1;
fixed_regs[27] = call_used_regs[27] = 1;
fixed_regs[30] = call_used_regs[30] = 1;
}
if (mips_abi == ABI_64)
{
int regno;
for (regno = FP_REG_FIRST + 20; regno < FP_REG_FIRST + 24; regno++)
call_really_used_regs[regno] = call_used_regs[regno] = 1;
}
if (mips_abi == ABI_N32 || mips_abi == ABI_MEABI)
{
int regno;
for (regno = FP_REG_FIRST + 21; regno <= FP_REG_FIRST + 31; regno+=2)
call_really_used_regs[regno] = call_used_regs[regno] = 1;
}
}
static struct machine_function *
mips_init_machine_status ()
{
return ((struct machine_function *)
ggc_alloc_cleared (sizeof (struct machine_function)));
}
void
mips_order_regs_for_local_alloc ()
{
register int i;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
reg_alloc_order[i] = i;
if (TARGET_MIPS16)
{
reg_alloc_order[0] = 24;
reg_alloc_order[24] = 0;
}
}
HOST_WIDE_INT
mips_debugger_offset (addr, offset)
rtx addr;
HOST_WIDE_INT offset;
{
rtx offset2 = const0_rtx;
rtx reg = eliminate_constant_term (addr, &offset2);
if (offset == 0)
offset = INTVAL (offset2);
if (reg == stack_pointer_rtx || reg == frame_pointer_rtx
|| reg == hard_frame_pointer_rtx)
{
HOST_WIDE_INT frame_size = (!cfun->machine->frame.initialized)
? compute_frame_size (get_frame_size ())
: cfun->machine->frame.total_size;
if (frame_pointer_needed && TARGET_MIPS16)
frame_size -= current_function_outgoing_args_size;
offset = offset - frame_size;
}
#if 0
else if (reg != arg_pointer_rtx)
abort_with_insn (addr, "mips_debugger_offset called with non stack/frame/arg pointer");
#endif
return offset;
}
void
print_operand (file, op, letter)
FILE *file;
rtx op;
int letter;
{
register enum rtx_code code;
if (PRINT_OPERAND_PUNCT_VALID_P (letter))
{
switch (letter)
{
case '?':
if (mips_branch_likely)
putc ('l', file);
break;
case '@':
fputs (reg_names [GP_REG_FIRST + 1], file);
break;
case '^':
fputs (reg_names [PIC_FUNCTION_ADDR_REGNUM], file);
break;
case '.':
fputs (reg_names [GP_REG_FIRST + 0], file);
break;
case '$':
fputs (reg_names[STACK_POINTER_REGNUM], file);
break;
case '+':
fputs (reg_names[GP_REG_FIRST + 28], file);
break;
case '&':
if (final_sequence != 0 && set_noreorder++ == 0)
fputs (".set\tnoreorder\n\t", file);
break;
case '*':
if (final_sequence != 0)
{
if (set_noreorder++ == 0)
fputs (".set\tnoreorder\n\t", file);
if (set_nomacro++ == 0)
fputs (".set\tnomacro\n\t", file);
}
break;
case '!':
if (final_sequence != 0 && set_nomacro++ == 0)
fputs ("\n\t.set\tnomacro", file);
break;
case '#':
if (set_noreorder != 0)
fputs ("\n\tnop", file);
else if (TARGET_STATS)
fputs ("\n\t#nop", file);
break;
case '(':
if (set_noreorder++ == 0)
fputs (".set\tnoreorder\n\t", file);
break;
case ')':
if (set_noreorder == 0)
error ("internal error: %%) found without a %%( in assembler pattern");
else if (--set_noreorder == 0)
fputs ("\n\t.set\treorder", file);
break;
case '[':
if (set_noat++ == 0)
fputs (".set\tnoat\n\t", file);
break;
case ']':
if (set_noat == 0)
error ("internal error: %%] found without a %%[ in assembler pattern");
else if (--set_noat == 0)
fputs ("\n\t.set\tat", file);
break;
case '<':
if (set_nomacro++ == 0)
fputs (".set\tnomacro\n\t", file);
break;
case '>':
if (set_nomacro == 0)
error ("internal error: %%> found without a %%< in assembler pattern");
else if (--set_nomacro == 0)
fputs ("\n\t.set\tmacro", file);
break;
case '{':
if (set_volatile++ == 0)
fprintf (file, "%s.set\tvolatile\n\t", TARGET_MIPS_AS ? "" : "#");
break;
case '}':
if (set_volatile == 0)
error ("internal error: %%} found without a %%{ in assembler pattern");
else if (--set_volatile == 0)
fprintf (file, "\n\t%s.set\tnovolatile", (TARGET_MIPS_AS) ? "" : "#");
break;
case '~':
{
if (align_labels_log > 0)
ASM_OUTPUT_ALIGN (file, align_labels_log);
}
break;
default:
error ("PRINT_OPERAND: unknown punctuation '%c'", letter);
break;
}
return;
}
if (! op)
{
error ("PRINT_OPERAND null pointer");
return;
}
code = GET_CODE (op);
if (code == SIGN_EXTEND)
op = XEXP (op, 0), code = GET_CODE (op);
if (letter == 'C')
switch (code)
{
case EQ: fputs ("eq", file); break;
case NE: fputs ("ne", file); break;
case GT: fputs ("gt", file); break;
case GE: fputs ("ge", file); break;
case LT: fputs ("lt", file); break;
case LE: fputs ("le", file); break;
case GTU: fputs ("gtu", file); break;
case GEU: fputs ("geu", file); break;
case LTU: fputs ("ltu", file); break;
case LEU: fputs ("leu", file); break;
default:
abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%C");
}
else if (letter == 'N')
switch (code)
{
case EQ: fputs ("ne", file); break;
case NE: fputs ("eq", file); break;
case GT: fputs ("le", file); break;
case GE: fputs ("lt", file); break;
case LT: fputs ("ge", file); break;
case LE: fputs ("gt", file); break;
case GTU: fputs ("leu", file); break;
case GEU: fputs ("ltu", file); break;
case LTU: fputs ("geu", file); break;
case LEU: fputs ("gtu", file); break;
default:
abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%N");
}
else if (letter == 'F')
switch (code)
{
case EQ: fputs ("c1f", file); break;
case NE: fputs ("c1t", file); break;
default:
abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%F");
}
else if (letter == 'W')
switch (code)
{
case EQ: fputs ("c1t", file); break;
case NE: fputs ("c1f", file); break;
default:
abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%W");
}
else if (letter == 'S')
{
char buffer[100];
ASM_GENERATE_INTERNAL_LABEL (buffer, "LS", CODE_LABEL_NUMBER (op));
assemble_name (file, buffer);
}
else if (letter == 'Z')
{
register int regnum;
if (code != REG)
abort ();
regnum = REGNO (op);
if (! ST_REG_P (regnum))
abort ();
if (regnum != ST_REG_FIRST)
fprintf (file, "%s,", reg_names[regnum]);
}
else if (code == REG || code == SUBREG)
{
register int regnum;
if (code == REG)
regnum = REGNO (op);
else
regnum = true_regnum (op);
if ((letter == 'M' && ! WORDS_BIG_ENDIAN)
|| (letter == 'L' && WORDS_BIG_ENDIAN)
|| letter == 'D')
regnum++;
fprintf (file, "%s", reg_names[regnum]);
}
else if (code == MEM)
{
if (letter == 'D')
output_address (plus_constant (XEXP (op, 0), 4));
else
output_address (XEXP (op, 0));
}
else if (code == CONST_DOUBLE
&& GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT)
{
char s[60];
real_to_decimal (s, CONST_DOUBLE_REAL_VALUE (op), sizeof (s), 0, 1);
fputs (s, file);
}
else if (letter == 'x' && GET_CODE (op) == CONST_INT)
fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & INTVAL(op));
else if (letter == 'X' && GET_CODE(op) == CONST_INT)
fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op));
else if (letter == 'd' && GET_CODE(op) == CONST_INT)
fprintf (file, HOST_WIDE_INT_PRINT_DEC, (INTVAL(op)));
else if (letter == 'z' && GET_CODE (op) == CONST_INT && INTVAL (op) == 0)
fputs (reg_names[GP_REG_FIRST], file);
else if (letter == 'd' || letter == 'x' || letter == 'X')
output_operand_lossage ("invalid use of %%d, %%x, or %%X");
else if (letter == 'B')
fputs (code == EQ ? "z" : "n", file);
else if (letter == 'b')
fputs (code == EQ ? "n" : "z", file);
else if (letter == 'T')
fputs (code == EQ ? "f" : "t", file);
else if (letter == 't')
fputs (code == EQ ? "t" : "f", file);
else if (code == CONST && GET_CODE (XEXP (op, 0)) == REG)
{
print_operand (file, XEXP (op, 0), letter);
}
else if (TARGET_MIPS16 && code == CONST && mips16_gp_offset_p (op))
{
fputs ("%gprel(", file);
mips16_output_gp_offset (file, op);
fputs (")", file);
}
else
output_addr_const (file, op);
}
void
print_operand_address (file, addr)
FILE *file;
rtx addr;
{
if (!addr)
error ("PRINT_OPERAND_ADDRESS, null pointer");
else
switch (GET_CODE (addr))
{
case REG:
if (! TARGET_MIPS16 && REGNO (addr) == ARG_POINTER_REGNUM)
abort_with_insn (addr, "arg pointer not eliminated");
fprintf (file, "0(%s)", reg_names [REGNO (addr)]);
break;
case LO_SUM:
{
register rtx arg0 = XEXP (addr, 0);
register rtx arg1 = XEXP (addr, 1);
if (! mips_split_addresses)
abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, Spurious LO_SUM");
if (GET_CODE (arg0) != REG)
abort_with_insn (addr,
"PRINT_OPERAND_ADDRESS, LO_SUM with #1 not REG");
fprintf (file, "%%lo(");
print_operand_address (file, arg1);
fprintf (file, ")(%s)", reg_names [REGNO (arg0)]);
}
break;
case PLUS:
{
register rtx reg = 0;
register rtx offset = 0;
register rtx arg0 = XEXP (addr, 0);
register rtx arg1 = XEXP (addr, 1);
if (GET_CODE (arg0) == REG)
{
reg = arg0;
offset = arg1;
if (GET_CODE (offset) == REG)
abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, 2 regs");
}
else if (GET_CODE (arg1) == REG)
reg = arg1, offset = arg0;
else if (CONSTANT_P (arg0) && CONSTANT_P (arg1))
{
output_addr_const (file, addr);
break;
}
else
abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, no regs");
if (! CONSTANT_P (offset))
abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #2");
if (REGNO (reg) == ARG_POINTER_REGNUM)
abort_with_insn (addr, "arg pointer not eliminated");
if (TARGET_MIPS16
&& GET_CODE (offset) == CONST
&& mips16_gp_offset_p (offset))
{
fputs ("%gprel(", file);
mips16_output_gp_offset (file, offset);
fputs (")", file);
}
else
output_addr_const (file, offset);
fprintf (file, "(%s)", reg_names [REGNO (reg)]);
}
break;
case LABEL_REF:
case SYMBOL_REF:
case CONST_INT:
case CONST:
output_addr_const (file, addr);
break;
default:
abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #1");
break;
}
}
static bool
mips_assemble_integer (x, size, aligned_p)
rtx x;
unsigned int size;
int aligned_p;
{
if ((TARGET_64BIT || TARGET_GAS) && size == 8 && aligned_p)
{
fputs ("\t.dword\t", asm_out_file);
if (HOST_BITS_PER_WIDE_INT < 64 || GET_CODE (x) != CONST_INT)
output_addr_const (asm_out_file, x);
else
print_operand (asm_out_file, x, 'X');
fputc ('\n', asm_out_file);
return true;
}
return default_assemble_integer (x, size, aligned_p);
}
int
mips_output_external (file, decl, name)
FILE *file ATTRIBUTE_UNUSED;
tree decl;
const char *name;
{
register struct extern_list *p;
int len;
tree section_name;
if (TARGET_GP_OPT
&& TREE_CODE (decl) != FUNCTION_DECL
&& !DECL_COMDAT (decl)
&& (len = int_size_in_bytes (TREE_TYPE (decl))) > 0
&& ((section_name = DECL_SECTION_NAME (decl)) == NULL
|| strcmp (TREE_STRING_POINTER (section_name), ".sbss") == 0
|| strcmp (TREE_STRING_POINTER (section_name), ".sdata") == 0))
{
p = (struct extern_list *) xmalloc (sizeof (struct extern_list));
p->next = extern_head;
p->name = name;
p->size = len;
extern_head = p;
}
#ifdef ASM_OUTPUT_UNDEF_FUNCTION
if (TREE_CODE (decl) == FUNCTION_DECL
&& strcmp (name, "alloca")
&& strcmp (name, "__builtin_next_arg"))
{
p = (struct extern_list *) xmalloc (sizeof (struct extern_list));
p->next = extern_head;
p->name = name;
p->size = -1;
extern_head = p;
}
#endif
return 0;
}
#ifdef ASM_OUTPUT_UNDEF_FUNCTION
int
mips_output_external_libcall (file, name)
FILE *file ATTRIBUTE_UNUSED;
const char *name;
{
register struct extern_list *p;
p = (struct extern_list *) xmalloc (sizeof (struct extern_list));
p->next = extern_head;
p->name = name;
p->size = -1;
extern_head = p;
return 0;
}
#endif
void
mips_output_filename (stream, name)
FILE *stream;
const char *name;
{
static int first_time = 1;
char ltext_label_name[100];
if (write_symbols == DWARF2_DEBUG)
return;
else if (first_time)
{
first_time = 0;
SET_FILE_NUMBER ();
current_function_file = name;
ASM_OUTPUT_FILENAME (stream, num_source_filenames, name);
if (!TARGET_GAS && write_symbols == DBX_DEBUG)
fprintf (stream, "\t#@stabs\n");
}
else if (write_symbols == DBX_DEBUG)
{
ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0);
fprintf (stream, "%s", ASM_STABS_OP);
output_quoted_string (stream, name);
fprintf (stream, ",%d,0,0,%s\n", N_SOL, <ext_label_name[1]);
}
else if (name != current_function_file
&& strcmp (name, current_function_file) != 0)
{
if (inside_function && !TARGET_GAS)
{
if (!file_in_function_warning)
{
file_in_function_warning = 1;
ignore_line_number = 1;
warning ("MIPS ECOFF format does not allow changing filenames within functions with #line");
}
}
else
{
SET_FILE_NUMBER ();
current_function_file = name;
ASM_OUTPUT_FILENAME (stream, num_source_filenames, name);
}
}
}
void
mips_output_lineno (stream, line)
FILE *stream;
int line;
{
if (write_symbols == DBX_DEBUG)
{
++sym_lineno;
fprintf (stream, "%sLM%d:\n%s%d,0,%d,%sLM%d\n",
LOCAL_LABEL_PREFIX, sym_lineno, ASM_STABN_OP, N_SLINE, line,
LOCAL_LABEL_PREFIX, sym_lineno);
}
else
{
fprintf (stream, "\n\t%s.loc\t%d %d\n",
(ignore_line_number) ? "#" : "",
num_source_filenames, line);
LABEL_AFTER_LOC (stream);
}
}
void
mips_output_ascii (stream, string_param, len)
FILE *stream;
const char *string_param;
size_t len;
{
size_t i;
int cur_pos = 17;
register const unsigned char *string =
(const unsigned char *)string_param;
fprintf (stream, "\t.ascii\t\"");
for (i = 0; i < len; i++)
{
register int c = string[i];
switch (c)
{
case '\"':
case '\\':
putc ('\\', stream);
putc (c, stream);
cur_pos += 2;
break;
case TARGET_NEWLINE:
fputs ("\\n", stream);
if (i+1 < len
&& (((c = string[i+1]) >= '\040' && c <= '~')
|| c == TARGET_TAB))
cur_pos = 32767;
else
cur_pos += 2;
break;
case TARGET_TAB:
fputs ("\\t", stream);
cur_pos += 2;
break;
case TARGET_FF:
fputs ("\\f", stream);
cur_pos += 2;
break;
case TARGET_BS:
fputs ("\\b", stream);
cur_pos += 2;
break;
case TARGET_CR:
fputs ("\\r", stream);
cur_pos += 2;
break;
default:
if (c >= ' ' && c < 0177)
{
putc (c, stream);
cur_pos++;
}
else
{
fprintf (stream, "\\%03o", c);
cur_pos += 4;
}
}
if (cur_pos > 72 && i+1 < len)
{
cur_pos = 17;
fprintf (stream, "\"\n\t.ascii\t\"");
}
}
fprintf (stream, "\"\n");
}
void
final_prescan_insn (insn, opvec, noperands)
rtx insn;
rtx opvec[] ATTRIBUTE_UNUSED;
int noperands ATTRIBUTE_UNUSED;
{
if (dslots_number_nops > 0)
{
rtx pattern = PATTERN (insn);
int length = get_attr_length (insn);
if (length == 0
|| (mips_load_reg != 0 && reg_mentioned_p (mips_load_reg, pattern))
|| (mips_load_reg2 != 0 && reg_mentioned_p (mips_load_reg2, pattern))
|| (mips_load_reg3 != 0 && reg_mentioned_p (mips_load_reg3, pattern))
|| (mips_load_reg4 != 0
&& reg_mentioned_p (mips_load_reg4, pattern)))
fputs ("\t#nop\n", asm_out_file);
else
dslots_load_filled++;
while (--dslots_number_nops > 0)
fputs ("\t#nop\n", asm_out_file);
mips_load_reg = 0;
mips_load_reg2 = 0;
mips_load_reg3 = 0;
mips_load_reg4 = 0;
}
if (TARGET_STATS
&& (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CALL_INSN))
dslots_jump_total++;
}
void
mips_asm_file_start (stream)
FILE *stream;
{
ASM_OUTPUT_SOURCE_FILENAME (stream, main_input_filename);
if (TARGET_MIPS_AS && optimize && flag_delayed_branch)
fprintf (stream, "\t.set\tnobopt\n");
if (TARGET_GAS)
{
#if defined(OBJECT_FORMAT_ELF) && !(TARGET_IRIX5 || TARGET_IRIX6)
const char * abi_string = NULL;
switch (mips_abi)
{
case ABI_32: abi_string = "abi32"; break;
case ABI_N32: abi_string = "abiN32"; break;
case ABI_64: abi_string = "abi64"; break;
case ABI_O64: abi_string = "abiO64"; break;
case ABI_EABI: abi_string = TARGET_64BIT ? "eabi64" : "eabi32"; break;
case ABI_MEABI:abi_string = TARGET_64BIT ? "meabi64" : "meabi32"; break;
default:
abort ();
}
fprintf (stream, "\t.section .mdebug.%s\n", abi_string);
fprintf (stream, "\t.previous\n");
#endif
}
#ifndef ABICALLS_ASM_OP
#define ABICALLS_ASM_OP "\t.abicalls"
#endif
if (TARGET_ABICALLS)
fprintf (stream, "%s\n", ABICALLS_ASM_OP);
if (TARGET_MIPS16)
fprintf (stream, "\t.set\tmips16\n");
if (TARGET_FILE_SWITCHING)
{
asm_out_data_file = stream;
asm_out_text_file = tmpfile ();
}
else
asm_out_data_file = asm_out_text_file = stream;
if (flag_verbose_asm)
fprintf (stream, "\n%s -G value = %d, Arch = %s, ISA = %d\n",
ASM_COMMENT_START,
mips_section_threshold, mips_arch_info->name, mips_isa);
}
void
mips_asm_file_end (file)
FILE *file;
{
tree name_tree;
struct extern_list *p;
if (extern_head)
{
fputs ("\n", file);
for (p = extern_head; p != 0; p = p->next)
{
name_tree = get_identifier (p->name);
if (! TREE_ASM_WRITTEN (name_tree))
{
TREE_ASM_WRITTEN (name_tree) = 1;
#ifdef ASM_OUTPUT_UNDEF_FUNCTION
if (p->size == -1)
ASM_OUTPUT_UNDEF_FUNCTION (file, p->name);
else
#endif
{
fputs ("\t.extern\t", file);
assemble_name (file, p->name);
fprintf (file, ", %d\n", p->size);
}
}
}
}
if (TARGET_FILE_SWITCHING)
{
fprintf (file, "\n\t.text\n");
copy_file_data (file, asm_out_text_file);
}
}
static void
copy_file_data (to, from)
FILE *to, *from;
{
char buffer[8192];
size_t len;
rewind (from);
if (ferror (from))
fatal_io_error ("can't rewind temp file");
while ((len = fread (buffer, 1, sizeof (buffer), from)) > 0)
if (fwrite (buffer, 1, len, to) != len)
fatal_io_error ("can't write to output file");
if (ferror (from))
fatal_io_error ("can't read from temp file");
if (fclose (from))
fatal_io_error ("can't close temp file");
}
void
mips_declare_object (stream, name, init_string, final_string, size)
FILE *stream;
const char *name;
const char *init_string;
const char *final_string;
int size;
{
fputs (init_string, stream);
assemble_name (stream, name);
fprintf (stream, final_string, size);
if (TARGET_GP_OPT)
{
tree name_tree = get_identifier (name);
TREE_ASM_WRITTEN (name_tree) = 1;
}
}
HOST_WIDE_INT
compute_frame_size (size)
HOST_WIDE_INT size;
{
unsigned int regno;
HOST_WIDE_INT total_size;
HOST_WIDE_INT var_size;
HOST_WIDE_INT args_size;
HOST_WIDE_INT extra_size;
HOST_WIDE_INT gp_reg_rounded;
HOST_WIDE_INT gp_reg_size;
HOST_WIDE_INT fp_reg_size;
long mask;
long fmask;
tree return_type;
gp_reg_size = 0;
fp_reg_size = 0;
mask = 0;
fmask = 0;
extra_size = MIPS_STACK_ALIGN (((TARGET_ABICALLS) ? UNITS_PER_WORD : 0));
var_size = MIPS_STACK_ALIGN (size);
args_size = MIPS_STACK_ALIGN (current_function_outgoing_args_size);
if (args_size == 0 && current_function_calls_alloca)
args_size = 4 * UNITS_PER_WORD;
total_size = var_size + args_size + extra_size;
return_type = DECL_RESULT (current_function_decl);
for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
{
if (MUST_SAVE_REGISTER (regno)
|| (TARGET_MIPS16
&& regno == GP_REG_FIRST + 18
&& regs_ever_live[regno])
|| (TARGET_MIPS16
&& regno == GP_REG_FIRST + 31
&& mips16_hard_float
&& ! mips_entry
&& ! aggregate_value_p (return_type)
&& GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT
&& GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE))
{
gp_reg_size += GET_MODE_SIZE (gpr_mode);
mask |= 1L << (regno - GP_REG_FIRST);
if (mips_entry
&& regno == GP_REG_FIRST + 17
&& ! MUST_SAVE_REGISTER (GP_REG_FIRST + 16))
{
gp_reg_size += UNITS_PER_WORD;
mask |= 1L << 16;
}
}
}
if (current_function_calls_eh_return)
{
unsigned int i;
for (i = 0; ; ++i)
{
regno = EH_RETURN_DATA_REGNO (i);
if (regno == INVALID_REGNUM)
break;
gp_reg_size += GET_MODE_SIZE (gpr_mode);
mask |= 1L << (regno - GP_REG_FIRST);
}
}
for (regno = (FP_REG_LAST - FP_INC + 1);
regno >= FP_REG_FIRST;
regno -= FP_INC)
{
if (regs_ever_live[regno] && !call_used_regs[regno])
{
fp_reg_size += FP_INC * UNITS_PER_FPREG;
fmask |= ((1 << FP_INC) - 1) << (regno - FP_REG_FIRST);
}
}
gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size);
total_size += gp_reg_rounded + MIPS_STACK_ALIGN (fp_reg_size);
if (total_size == extra_size
&& (mips_abi == ABI_32 || mips_abi == ABI_O64 || mips_abi == ABI_EABI)
&& ! current_function_profile)
total_size = extra_size = 0;
else if (TARGET_ABICALLS)
{
gp_reg_size += UNITS_PER_WORD;
mask |= 1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST);
total_size -= gp_reg_rounded;
gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size);
total_size += gp_reg_rounded;
}
if (mips_abi != ABI_32 && mips_abi != ABI_O64)
total_size += MIPS_STACK_ALIGN (current_function_pretend_args_size);
if (mips_entry && total_size > 0 && total_size < 32)
total_size = 32;
cfun->machine->frame.total_size = total_size;
cfun->machine->frame.var_size = var_size;
cfun->machine->frame.args_size = args_size;
cfun->machine->frame.extra_size = extra_size;
cfun->machine->frame.gp_reg_size = gp_reg_size;
cfun->machine->frame.fp_reg_size = fp_reg_size;
cfun->machine->frame.mask = mask;
cfun->machine->frame.fmask = fmask;
cfun->machine->frame.initialized = reload_completed;
cfun->machine->frame.num_gp = gp_reg_size / UNITS_PER_WORD;
cfun->machine->frame.num_fp = fp_reg_size / (FP_INC * UNITS_PER_FPREG);
if (mask)
{
unsigned long offset;
if (! mips_entry)
offset = (args_size + extra_size + var_size
+ gp_reg_size - GET_MODE_SIZE (gpr_mode));
else
offset = total_size - GET_MODE_SIZE (gpr_mode);
cfun->machine->frame.gp_sp_offset = offset;
cfun->machine->frame.gp_save_offset = offset - total_size;
}
else
{
cfun->machine->frame.gp_sp_offset = 0;
cfun->machine->frame.gp_save_offset = 0;
}
if (fmask)
{
unsigned long offset = (args_size + extra_size + var_size
+ gp_reg_rounded + fp_reg_size
- FP_INC * UNITS_PER_FPREG);
cfun->machine->frame.fp_sp_offset = offset;
cfun->machine->frame.fp_save_offset = offset - total_size;
}
else
{
cfun->machine->frame.fp_sp_offset = 0;
cfun->machine->frame.fp_save_offset = 0;
}
return total_size;
}
int
mips_initial_elimination_offset (from, to)
int from, to;
{
int offset;
switch (from)
{
case FRAME_POINTER_REGNUM:
offset = 0;
break;
case ARG_POINTER_REGNUM:
compute_frame_size (get_frame_size ());
offset = cfun->machine->frame.total_size;
if (mips_abi == ABI_N32 || mips_abi == ABI_64 || mips_abi == ABI_MEABI)
offset -= current_function_pretend_args_size;
break;
case RETURN_ADDRESS_POINTER_REGNUM:
compute_frame_size (get_frame_size ());
offset = cfun->machine->frame.gp_sp_offset;
if (BYTES_BIG_ENDIAN)
offset += UNITS_PER_WORD - (POINTER_SIZE / BITS_PER_UNIT);
break;
default:
abort ();
}
if (TARGET_MIPS16 && to == HARD_FRAME_POINTER_REGNUM)
offset -= current_function_outgoing_args_size;
return offset;
}
#define BITSET_P(VALUE,BIT) (((VALUE) & (1L << (BIT))) != 0)
static rtx
mips_add_large_offset_to_sp (offset)
HOST_WIDE_INT offset;
{
rtx reg = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
rtx offset_rtx = GEN_INT (offset);
emit_move_insn (reg, offset_rtx);
if (Pmode == DImode)
emit_insn (gen_adddi3 (reg, reg, stack_pointer_rtx));
else
emit_insn (gen_addsi3 (reg, reg, stack_pointer_rtx));
return reg;
}
static void
mips_annotate_frame_insn (insn, dwarf_pattern)
rtx insn, dwarf_pattern;
{
RTX_FRAME_RELATED_P (insn) = 1;
REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
dwarf_pattern,
REG_NOTES (insn));
}
static rtx
mips_frame_set (mode, regno, offset)
enum machine_mode mode;
int regno;
int offset;
{
rtx address = plus_constant (stack_pointer_rtx, offset);
rtx set = gen_rtx_SET (mode,
gen_rtx_MEM (mode, address),
gen_rtx_REG (mode, regno));
RTX_FRAME_RELATED_P (set) = 1;
return set;
}
static void
mips_emit_frame_related_store (mem, reg, offset)
rtx mem;
rtx reg;
HOST_WIDE_INT offset;
{
rtx dwarf_expr;
if (GET_MODE (reg) == DFmode && ! TARGET_FLOAT64)
{
int regno1 = TARGET_BIG_ENDIAN ? REGNO (reg) + 1 : REGNO (reg);
int regno2 = TARGET_BIG_ENDIAN ? REGNO (reg) : REGNO (reg) + 1;
rtx set1 = mips_frame_set (SFmode, regno1, offset);
rtx set2 = mips_frame_set (SFmode, regno2, offset + UNITS_PER_FPREG);
dwarf_expr = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set1, set2));
}
else
dwarf_expr = mips_frame_set (GET_MODE (reg), REGNO (reg), offset);
mips_annotate_frame_insn (emit_move_insn (mem, reg), dwarf_expr);
}
static void
save_restore_insns (store_p, large_reg, large_offset)
int store_p;
rtx large_reg;
long large_offset;
{
long mask = cfun->machine->frame.mask;
long fmask = cfun->machine->frame.fmask;
long real_mask = mask;
int regno;
rtx base_reg_rtx;
HOST_WIDE_INT base_offset;
HOST_WIDE_INT gp_offset;
HOST_WIDE_INT fp_offset;
HOST_WIDE_INT end_offset;
rtx insn;
if (frame_pointer_needed
&& ! BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST))
abort ();
if (! store_p
&& TARGET_ABICALLS
&& (mips_abi == ABI_32 || mips_abi == ABI_O64))
mask &= ~(1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST));
if (mask == 0 && fmask == 0)
return;
if (mask)
{
gp_offset = cfun->machine->frame.gp_sp_offset;
end_offset
= gp_offset - (cfun->machine->frame.gp_reg_size
- GET_MODE_SIZE (gpr_mode));
if (gp_offset < 0 || end_offset < 0)
internal_error
("gp_offset (%ld) or end_offset (%ld) is less than zero",
(long) gp_offset, (long) end_offset);
else if (TARGET_MIPS16 && large_offset > 32767)
base_reg_rtx = stack_pointer_rtx, base_offset = large_offset;
else if (gp_offset < 32768)
base_reg_rtx = stack_pointer_rtx, base_offset = 0;
else if (large_reg != 0
&& (unsigned HOST_WIDE_INT) (large_offset - gp_offset) < 32768
&& (unsigned HOST_WIDE_INT) (large_offset - end_offset) < 32768)
{
base_reg_rtx = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
base_offset = large_offset;
if (Pmode == DImode)
insn = emit_insn (gen_adddi3 (base_reg_rtx, large_reg,
stack_pointer_rtx));
else
insn = emit_insn (gen_addsi3 (base_reg_rtx, large_reg,
stack_pointer_rtx));
}
else
{
base_offset = gp_offset;
base_reg_rtx = mips_add_large_offset_to_sp (base_offset);
}
if (TARGET_MIPS16
&& ! store_p
&& frame_pointer_needed
&& large_offset <= 32767)
base_offset += current_function_outgoing_args_size;
for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
{
if (BITSET_P (mask, regno - GP_REG_FIRST))
{
rtx reg_rtx;
rtx mem_rtx
= gen_rtx (MEM, gpr_mode,
gen_rtx (PLUS, Pmode, base_reg_rtx,
GEN_INT (gp_offset - base_offset)));
if (! current_function_calls_eh_return)
RTX_UNCHANGING_P (mem_rtx) = 1;
if (TARGET_MIPS16 && ! store_p && regno == GP_REG_FIRST + 31)
reg_rtx = gen_rtx (REG, gpr_mode, GP_REG_FIRST + 7);
else if (TARGET_MIPS16
&& regno != GP_REG_FIRST + 31
&& ! M16_REG_P (regno))
{
if (! store_p)
reg_rtx = gen_rtx (REG, gpr_mode, 6);
else
{
reg_rtx = gen_rtx (REG, gpr_mode, 3);
emit_move_insn (reg_rtx,
gen_rtx (REG, gpr_mode, regno));
}
}
else
reg_rtx = gen_rtx (REG, gpr_mode, regno);
if (store_p)
mips_emit_frame_related_store (mem_rtx, reg_rtx, gp_offset);
else
{
emit_move_insn (reg_rtx, mem_rtx);
if (TARGET_MIPS16
&& regno != GP_REG_FIRST + 31
&& ! M16_REG_P (regno))
emit_move_insn (gen_rtx (REG, gpr_mode, regno),
reg_rtx);
}
}
if (BITSET_P (real_mask, regno - GP_REG_FIRST))
gp_offset -= GET_MODE_SIZE (gpr_mode);
}
}
else
base_reg_rtx = 0, base_offset = 0;
if (fmask)
{
fp_offset = cfun->machine->frame.fp_sp_offset;
end_offset = fp_offset - (cfun->machine->frame.fp_reg_size
- UNITS_PER_HWFPVALUE);
if (fp_offset < 0 || end_offset < 0)
internal_error
("fp_offset (%ld) or end_offset (%ld) is less than zero",
(long) fp_offset, (long) end_offset);
else if (fp_offset < 32768)
base_reg_rtx = stack_pointer_rtx, base_offset = 0;
else if (base_reg_rtx != 0
&& (unsigned HOST_WIDE_INT) (base_offset - fp_offset) < 32768
&& (unsigned HOST_WIDE_INT) (base_offset - end_offset) < 32768)
;
else if (large_reg != 0
&& (unsigned HOST_WIDE_INT) (large_offset - fp_offset) < 32768
&& (unsigned HOST_WIDE_INT) (large_offset - end_offset) < 32768)
{
base_reg_rtx = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
base_offset = large_offset;
if (Pmode == DImode)
insn = emit_insn (gen_adddi3 (base_reg_rtx, large_reg,
stack_pointer_rtx));
else
insn = emit_insn (gen_addsi3 (base_reg_rtx, large_reg,
stack_pointer_rtx));
}
else
{
base_offset = fp_offset;
base_reg_rtx = mips_add_large_offset_to_sp (fp_offset);
}
for (regno = (FP_REG_LAST - FP_INC + 1);
regno >= FP_REG_FIRST;
regno -= FP_INC)
if (BITSET_P (fmask, regno - FP_REG_FIRST))
{
enum machine_mode sz = TARGET_SINGLE_FLOAT ? SFmode : DFmode;
rtx reg_rtx = gen_rtx (REG, sz, regno);
rtx mem_rtx = gen_rtx (MEM, sz,
gen_rtx (PLUS, Pmode, base_reg_rtx,
GEN_INT (fp_offset
- base_offset)));
if (! current_function_calls_eh_return)
RTX_UNCHANGING_P (mem_rtx) = 1;
if (store_p)
mips_emit_frame_related_store (mem_rtx, reg_rtx, fp_offset);
else
emit_move_insn (reg_rtx, mem_rtx);
fp_offset -= UNITS_PER_HWFPVALUE;
}
}
}
static void
mips_output_function_prologue (file, size)
FILE *file;
HOST_WIDE_INT size ATTRIBUTE_UNUSED;
{
#ifndef FUNCTION_NAME_ALREADY_DECLARED
const char *fnname;
#endif
HOST_WIDE_INT tsize = cfun->machine->frame.total_size;
if (!TARGET_GAS)
ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl));
#ifdef SDB_DEBUGGING_INFO
if (debug_info_level != DINFO_LEVEL_TERSE && write_symbols == SDB_DEBUG)
ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl));
#endif
if (TARGET_MIPS16 && !TARGET_SOFT_FLOAT
&& current_function_args_info.fp_code != 0)
build_mips16_function_stub (file);
inside_function = 1;
#ifndef FUNCTION_NAME_ALREADY_DECLARED
fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
if (!flag_inhibit_size_directive)
{
fputs ("\t.ent\t", file);
assemble_name (file, fnname);
fputs ("\n", file);
}
assemble_name (file, fnname);
fputs (":\n", file);
#endif
if (!flag_inhibit_size_directive)
{
fprintf (file,
"\t.frame\t%s,%ld,%s\t\t# vars= %ld, regs= %d/%d, args= %d, extra= %ld\n",
(reg_names[(frame_pointer_needed)
? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM]),
((frame_pointer_needed && TARGET_MIPS16)
? ((long) tsize - current_function_outgoing_args_size)
: (long) tsize),
reg_names[GP_REG_FIRST + 31],
cfun->machine->frame.var_size,
cfun->machine->frame.num_gp,
cfun->machine->frame.num_fp,
current_function_outgoing_args_size,
cfun->machine->frame.extra_size);
fprintf (file, "\t.mask\t0x%08lx,%ld\n\t.fmask\t0x%08lx,%ld\n",
cfun->machine->frame.mask,
cfun->machine->frame.gp_save_offset,
cfun->machine->frame.fmask,
cfun->machine->frame.fp_save_offset);
}
if (mips_entry && ! mips_can_use_return_insn ())
{
int save16 = BITSET_P (cfun->machine->frame.mask, 16);
int save17 = BITSET_P (cfun->machine->frame.mask, 17);
int save31 = BITSET_P (cfun->machine->frame.mask, 31);
int savearg = 0;
rtx insn;
for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
{
rtx note, set, src, dest, base, offset;
int hireg;
if (GET_CODE (insn) == CODE_LABEL
|| GET_CODE (insn) == JUMP_INSN
|| GET_CODE (insn) == CALL_INSN)
break;
if (GET_CODE (insn) != INSN)
continue;
set = PATTERN (insn);
if (GET_CODE (set) != SET)
continue;
note = find_reg_note (insn, REG_EQUIV, NULL_RTX);
if (note == NULL_RTX)
continue;
if (! reg_mentioned_p (arg_pointer_rtx, XEXP (note, 0)))
continue;
src = SET_SRC (set);
if (GET_CODE (src) != REG
|| REGNO (src) < GP_REG_FIRST + 4
|| REGNO (src) > GP_REG_FIRST + 7)
continue;
dest = SET_DEST (set);
if (GET_CODE (dest) != MEM)
continue;
if (GET_MODE_SIZE (GET_MODE (dest)) == (unsigned) UNITS_PER_WORD)
;
else if (GET_MODE_SIZE (GET_MODE (dest)) == (unsigned)2 * UNITS_PER_WORD
&& REGNO (src) < GP_REG_FIRST + 7)
;
else
continue;
offset = const0_rtx;
base = eliminate_constant_term (XEXP (dest, 0), &offset);
if (GET_CODE (base) != REG
|| GET_CODE (offset) != CONST_INT)
continue;
if (REGNO (base) == (unsigned) STACK_POINTER_REGNUM
&& INTVAL (offset) == tsize + (REGNO (src) - 4) * UNITS_PER_WORD)
;
else if (REGNO (base) == (unsigned) HARD_FRAME_POINTER_REGNUM
&& (INTVAL (offset)
== (tsize
+ (REGNO (src) - 4) * UNITS_PER_WORD
- current_function_outgoing_args_size)))
;
else
continue;
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
hireg = (REGNO (src)
+ HARD_REGNO_NREGS (REGNO (src), GET_MODE (dest))
- 1);
if (hireg > savearg)
savearg = hireg;
}
if (current_function_stdarg)
savearg = GP_REG_FIRST + 7;
fprintf (file, "\tentry\t");
if (savearg > 0)
{
if (savearg == GP_REG_FIRST + 4)
fprintf (file, "%s", reg_names[savearg]);
else
fprintf (file, "%s-%s", reg_names[GP_REG_FIRST + 4],
reg_names[savearg]);
}
if (save16 || save17)
{
if (savearg > 0)
fprintf (file, ",");
fprintf (file, "%s", reg_names[GP_REG_FIRST + 16]);
if (save17)
fprintf (file, "-%s", reg_names[GP_REG_FIRST + 17]);
}
if (save31)
{
if (savearg > 0 || save16 || save17)
fprintf (file, ",");
fprintf (file, "%s", reg_names[GP_REG_FIRST + 31]);
}
fprintf (file, "\n");
}
if (TARGET_ABICALLS && (mips_abi == ABI_32 || mips_abi == ABI_O64))
{
const char *const sp_str = reg_names[STACK_POINTER_REGNUM];
fprintf (file, "\t.set\tnoreorder\n\t.cpload\t%s\n\t.set\treorder\n",
reg_names[PIC_FUNCTION_ADDR_REGNUM]);
if (tsize > 0)
{
fprintf (file, "\t%s\t%s,%s,%ld\n",
(Pmode == DImode ? "dsubu" : "subu"),
sp_str, sp_str, (long) tsize);
fprintf (file, "\t.cprestore %ld\n", cfun->machine->frame.args_size);
}
if (dwarf2out_do_frame ())
dwarf2out_def_cfa ("", STACK_POINTER_REGNUM, tsize);
}
}
void
mips_expand_prologue ()
{
int regno;
HOST_WIDE_INT tsize;
rtx tmp_rtx = 0;
int last_arg_is_vararg_marker = 0;
tree fndecl = current_function_decl;
tree fntype = TREE_TYPE (fndecl);
tree fnargs = DECL_ARGUMENTS (fndecl);
rtx next_arg_reg;
int i;
tree next_arg;
tree cur_arg;
CUMULATIVE_ARGS args_so_far;
rtx reg_18_save = NULL_RTX;
int store_args_on_stack = (mips_abi == ABI_32 || mips_abi == ABI_O64)
&& (! mips_entry || mips_can_use_return_insn ());
if (aggregate_value_p (DECL_RESULT (fndecl))
&& ! current_function_returns_pcc_struct
&& struct_value_incoming_rtx == 0)
{
tree type = build_pointer_type (fntype);
tree function_result_decl = build_decl (PARM_DECL, NULL_TREE, type);
DECL_ARG_TYPE (function_result_decl) = type;
TREE_CHAIN (function_result_decl) = fnargs;
fnargs = function_result_decl;
}
INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, 0);
regno = GP_ARG_FIRST;
for (cur_arg = fnargs; cur_arg != 0; cur_arg = next_arg)
{
tree passed_type = DECL_ARG_TYPE (cur_arg);
enum machine_mode passed_mode = TYPE_MODE (passed_type);
rtx entry_parm;
if (TREE_ADDRESSABLE (passed_type))
{
passed_type = build_pointer_type (passed_type);
passed_mode = Pmode;
}
entry_parm = FUNCTION_ARG (args_so_far, passed_mode, passed_type, 1);
FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, passed_type, 1);
next_arg = TREE_CHAIN (cur_arg);
if (entry_parm && store_args_on_stack)
{
if (next_arg == 0
&& DECL_NAME (cur_arg)
&& ((0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)),
"__builtin_va_alist"))
|| (0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)),
"va_alist"))))
{
last_arg_is_vararg_marker = 1;
if (GET_CODE (entry_parm) == REG)
regno = REGNO (entry_parm);
else
regno = GP_ARG_LAST + 1;
break;
}
else
{
int words;
if (GET_CODE (entry_parm) != REG)
abort ();
if (GET_MODE (entry_parm) == BLKmode)
words = (int_size_in_bytes (passed_type) + 3) / 4;
else
words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
regno = REGNO (entry_parm) + words - 1;
}
}
else
{
regno = GP_ARG_LAST+1;
break;
}
}
next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1);
if (next_arg_reg != 0 && GET_CODE (next_arg_reg) == PARALLEL)
{
rtvec adjust = XVEC (next_arg_reg, 0);
int num = GET_NUM_ELEM (adjust);
for (i = 0; i < num; i++)
{
rtx insn, pattern;
pattern = RTVEC_ELT (adjust, i);
if (GET_CODE (pattern) != SET
|| GET_CODE (SET_SRC (pattern)) != ASHIFT)
abort_with_insn (pattern, "insn is not a shift");
PUT_CODE (SET_SRC (pattern), ASHIFTRT);
insn = emit_insn (pattern);
REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
REG_NOTES (insn));
}
}
tsize = compute_frame_size (get_frame_size ());
if (store_args_on_stack
&& ((TYPE_ARG_TYPES (fntype) != 0
&& (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
!= void_type_node))
|| last_arg_is_vararg_marker))
{
int offset = (regno - GP_ARG_FIRST) * UNITS_PER_WORD;
rtx ptr = stack_pointer_rtx;
if (TARGET_ABICALLS)
offset += tsize;
for (; regno <= GP_ARG_LAST; regno++)
{
if (offset != 0)
ptr = gen_rtx (PLUS, Pmode, stack_pointer_rtx, GEN_INT (offset));
emit_move_insn (gen_rtx (MEM, gpr_mode, ptr),
gen_rtx (REG, gpr_mode, regno));
offset += GET_MODE_SIZE (gpr_mode);
}
}
if (mips_entry && ! mips_can_use_return_insn ())
{
if (tsize > 0 && tsize <= 32 && frame_pointer_needed)
{
rtx insn;
if (TARGET_MIPS16 && current_function_outgoing_args_size != 0)
{
rtx incr = GEN_INT (current_function_outgoing_args_size);
if (Pmode == DImode)
insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
stack_pointer_rtx,
incr));
else
insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
stack_pointer_rtx,
incr));
}
else if (Pmode == DImode)
insn = emit_insn (gen_movdi (hard_frame_pointer_rtx,
stack_pointer_rtx));
else
insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
stack_pointer_rtx));
RTX_FRAME_RELATED_P (insn) = 1;
}
if (TARGET_MIPS16 && BITSET_P (cfun->machine->frame.mask, 18))
{
rtx reg_rtx = gen_rtx (REG, gpr_mode, GP_REG_FIRST + 3);
long gp_offset, base_offset;
gp_offset = cfun->machine->frame.gp_sp_offset;
if (BITSET_P (cfun->machine->frame.mask, 16))
gp_offset -= UNITS_PER_WORD;
if (BITSET_P (cfun->machine->frame.mask, 17))
gp_offset -= UNITS_PER_WORD;
if (BITSET_P (cfun->machine->frame.mask, 31))
gp_offset -= UNITS_PER_WORD;
if (tsize > 32767)
base_offset = tsize;
else
base_offset = 0;
start_sequence ();
emit_move_insn (reg_rtx,
gen_rtx (REG, gpr_mode, GP_REG_FIRST + 18));
emit_move_insn (gen_rtx (MEM, gpr_mode,
gen_rtx (PLUS, Pmode, stack_pointer_rtx,
GEN_INT (gp_offset
- base_offset))),
reg_rtx);
reg_18_save = get_insns ();
end_sequence ();
}
if (tsize > 32)
tsize -= 32;
else
{
tsize = 0;
if (reg_18_save != NULL_RTX)
emit_insn (reg_18_save);
}
}
if (tsize > 0)
{
rtx tsize_rtx = GEN_INT (tsize);
if ((!TARGET_ABICALLS || (mips_abi != ABI_32 && mips_abi != ABI_O64))
&& (!TARGET_MIPS16 || tsize <= 32767))
{
rtx adjustment_rtx, insn, dwarf_pattern;
if (tsize > 32767)
{
adjustment_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM);
emit_move_insn (adjustment_rtx, tsize_rtx);
}
else
adjustment_rtx = tsize_rtx;
if (Pmode == DImode)
insn = emit_insn (gen_subdi3 (stack_pointer_rtx, stack_pointer_rtx,
adjustment_rtx));
else
insn = emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx,
adjustment_rtx));
dwarf_pattern = gen_rtx_SET (Pmode, stack_pointer_rtx,
plus_constant (stack_pointer_rtx,
-tsize));
mips_annotate_frame_insn (insn, dwarf_pattern);
}
if (! mips_entry)
save_restore_insns (1, tmp_rtx, tsize);
else if (reg_18_save != NULL_RTX)
emit_insn (reg_18_save);
if ((!TARGET_ABICALLS || (mips_abi != ABI_32 && mips_abi != ABI_O64))
&& TARGET_MIPS16
&& tsize > 32767)
{
rtx reg_rtx;
if (!frame_pointer_needed)
abort ();
reg_rtx = gen_rtx (REG, Pmode, 3);
emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
emit_move_insn (reg_rtx, tsize_rtx);
if (Pmode == DImode)
emit_insn (gen_subdi3 (hard_frame_pointer_rtx,
hard_frame_pointer_rtx,
reg_rtx));
else
emit_insn (gen_subsi3 (hard_frame_pointer_rtx,
hard_frame_pointer_rtx,
reg_rtx));
emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
}
if (frame_pointer_needed)
{
rtx insn = 0;
if ((! TARGET_ABICALLS || (mips_abi != ABI_32 && mips_abi != ABI_O64))
&& TARGET_MIPS16
&& tsize > 32767)
{
if (current_function_outgoing_args_size != 0)
{
rtx incr = GEN_INT (current_function_outgoing_args_size);
if (Pmode == DImode)
insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
hard_frame_pointer_rtx,
incr));
else
insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
hard_frame_pointer_rtx,
incr));
}
}
else if (TARGET_MIPS16 && current_function_outgoing_args_size != 0)
{
rtx incr = GEN_INT (current_function_outgoing_args_size);
if (Pmode == DImode)
insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
stack_pointer_rtx,
incr));
else
insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
stack_pointer_rtx,
incr));
}
else if (Pmode == DImode)
insn = emit_insn (gen_movdi (hard_frame_pointer_rtx,
stack_pointer_rtx));
else
insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
stack_pointer_rtx));
if (insn)
RTX_FRAME_RELATED_P (insn) = 1;
}
if (TARGET_ABICALLS && (mips_abi != ABI_32 && mips_abi != ABI_O64))
emit_insn (gen_loadgp (XEXP (DECL_RTL (current_function_decl), 0),
gen_rtx_REG (DImode, 25)));
}
if (current_function_profile)
emit_insn (gen_blockage ());
}
#define RA_MASK BITMASK_HIGH
#define PIC_OFFSET_TABLE_MASK (1 << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))
static void
mips_output_function_epilogue (file, size)
FILE *file ATTRIBUTE_UNUSED;
HOST_WIDE_INT size ATTRIBUTE_UNUSED;
{
const char *fnname = "";
rtx string;
#ifndef FUNCTION_NAME_ALREADY_DECLARED
fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
if (!flag_inhibit_size_directive)
{
fputs ("\t.end\t", file);
assemble_name (file, fnname);
fputs ("\n", file);
}
#endif
if (TARGET_STATS)
{
int num_gp_regs = cfun->machine->frame.gp_reg_size / 4;
int num_fp_regs = cfun->machine->frame.fp_reg_size / 8;
int num_regs = num_gp_regs + num_fp_regs;
const char *name = fnname;
if (name[0] == '*')
name++;
dslots_load_total += num_regs;
fprintf (stderr,
"%-20s fp=%c leaf=%c alloca=%c setjmp=%c stack=%4ld arg=%3d reg=%2d/%d delay=%3d/%3dL %3d/%3dJ refs=%3d/%3d/%3d",
name, frame_pointer_needed ? 'y' : 'n',
(cfun->machine->frame.mask & RA_MASK) != 0 ? 'n' : 'y',
current_function_calls_alloca ? 'y' : 'n',
current_function_calls_setjmp ? 'y' : 'n',
cfun->machine->frame.total_size,
current_function_outgoing_args_size, num_gp_regs, num_fp_regs,
dslots_load_total, dslots_load_filled,
dslots_jump_total, dslots_jump_filled,
num_refs[0], num_refs[1], num_refs[2]);
fputc ('\n', stderr);
}
inside_function = 0;
ignore_line_number = 0;
dslots_load_total = 0;
dslots_jump_total = 0;
dslots_load_filled = 0;
dslots_jump_filled = 0;
num_refs[0] = 0;
num_refs[1] = 0;
num_refs[2] = 0;
mips_load_reg = 0;
mips_load_reg2 = 0;
while (string_constants != NULL)
{
struct string_constant *next;
next = string_constants->next;
free (string_constants);
string_constants = next;
}
for (string = mips16_strings; string != 0; string = XEXP (string, 1))
SYMBOL_REF_FLAG (XEXP (string, 0)) = 0;
free_EXPR_LIST_list (&mips16_strings);
if (TARGET_FILE_SWITCHING)
{
asm_out_file = asm_out_data_file;
data_section ();
}
}
void
mips_expand_epilogue ()
{
HOST_WIDE_INT tsize = cfun->machine->frame.total_size;
rtx tsize_rtx = GEN_INT (tsize);
rtx tmp_rtx = (rtx)0;
if (mips_can_use_return_insn ())
{
emit_jump_insn (gen_return ());
return;
}
if (mips_entry && ! mips_can_use_return_insn ())
tsize -= 32;
if (tsize > 32767 && ! TARGET_MIPS16)
{
tmp_rtx = gen_rtx_REG (Pmode, MIPS_TEMP1_REGNUM);
emit_move_insn (tmp_rtx, tsize_rtx);
tsize_rtx = tmp_rtx;
}
if (tsize > 0)
{
long orig_tsize = tsize;
if (frame_pointer_needed)
{
emit_insn (gen_blockage ());
if (TARGET_MIPS16)
{
tsize -= current_function_outgoing_args_size;
if (orig_tsize > 32767)
{
rtx g6_rtx = gen_rtx (REG, Pmode, GP_REG_FIRST + 6);
emit_move_insn (g6_rtx, GEN_INT (tsize));
if (Pmode == DImode)
emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
hard_frame_pointer_rtx,
g6_rtx));
else
emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
hard_frame_pointer_rtx,
g6_rtx));
tsize = 0;
}
if (tsize && tsize != orig_tsize)
tsize_rtx = GEN_INT (tsize);
}
if (Pmode == DImode)
emit_insn (gen_movdi (stack_pointer_rtx, hard_frame_pointer_rtx));
else
emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
}
else if (TARGET_ABICALLS && mips_abi != ABI_32 && mips_abi != ABI_O64
&& (cfun->machine->frame.mask
& (1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))))
emit_insn (gen_blockage ());
save_restore_insns (0, tmp_rtx, orig_tsize);
if (tsize > 32767 && TARGET_MIPS16)
abort ();
if (current_function_calls_eh_return)
{
rtx eh_ofs = EH_RETURN_STACKADJ_RTX;
if (Pmode == DImode)
emit_insn (gen_adddi3 (eh_ofs, eh_ofs, tsize_rtx));
else
emit_insn (gen_addsi3 (eh_ofs, eh_ofs, tsize_rtx));
tsize_rtx = eh_ofs;
}
emit_insn (gen_blockage ());
if (tsize != 0 || current_function_calls_eh_return)
{
if (!TARGET_MIPS16)
{
if (Pmode == DImode)
emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx,
tsize_rtx));
else
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
tsize_rtx));
}
else
{
rtx g6_rtx = gen_rtx (REG, Pmode, GP_REG_FIRST + 6);
if (Pmode == DImode)
{
emit_insn (gen_movdi (g6_rtx, stack_pointer_rtx));
emit_insn (gen_adddi3 (g6_rtx, g6_rtx, tsize_rtx));
emit_insn (gen_movdi (stack_pointer_rtx, g6_rtx));
}
else
{
emit_insn (gen_movsi (g6_rtx, stack_pointer_rtx));
emit_insn (gen_addsi3 (g6_rtx, g6_rtx, tsize_rtx));
emit_insn (gen_movsi (stack_pointer_rtx, g6_rtx));
}
}
}
}
if (TARGET_MIPS16 && (cfun->machine->frame.mask & RA_MASK) != 0)
emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode,
GP_REG_FIRST + 7)));
else
emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode,
GP_REG_FIRST + 31)));
}
int
mips_can_use_return_insn ()
{
tree return_type;
if (! reload_completed)
return 0;
if (regs_ever_live[31] || current_function_profile)
return 0;
return_type = DECL_RESULT (current_function_decl);
if (TARGET_MIPS16
&& mips16_hard_float
&& ! aggregate_value_p (return_type)
&& GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT
&& GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE)
return 0;
if (cfun->machine->frame.initialized)
return cfun->machine->frame.total_size == 0;
return compute_frame_size (get_frame_size ()) == 0;
}
static int
symbolic_expression_p (x)
rtx x;
{
if (GET_CODE (x) == SYMBOL_REF)
return 1;
if (GET_CODE (x) == CONST)
return symbolic_expression_p (XEXP (x, 0));
if (GET_RTX_CLASS (GET_CODE (x)) == '1')
return symbolic_expression_p (XEXP (x, 0));
if (GET_RTX_CLASS (GET_CODE (x)) == 'c'
|| GET_RTX_CLASS (GET_CODE (x)) == '2')
return (symbolic_expression_p (XEXP (x, 0))
|| symbolic_expression_p (XEXP (x, 1)));
return 0;
}
static void
mips_select_rtx_section (mode, x, align)
enum machine_mode mode;
rtx x;
unsigned HOST_WIDE_INT align;
{
if (TARGET_MIPS16)
{
function_section (current_function_decl);
}
else if (TARGET_EMBEDDED_DATA)
{
mergeable_constant_section (mode, align, 0);
}
else
{
if (GET_MODE_SIZE (mode) <= (unsigned) mips_section_threshold
&& mips_section_threshold > 0)
SMALL_DATA_SECTION ();
else if (flag_pic && symbolic_expression_p (x))
{
if (targetm.have_named_sections)
named_section (NULL_TREE, ".data.rel.ro", 3);
else
data_section ();
}
else
mergeable_constant_section (mode, align, 0);
}
}
static void
mips_select_section (decl, reloc, align)
tree decl;
int reloc;
unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
{
int size = int_size_in_bytes (TREE_TYPE (decl));
if ((TARGET_EMBEDDED_PIC || TARGET_MIPS16)
&& TREE_CODE (decl) == STRING_CST
&& !flag_writable_strings)
text_section ();
else if (TARGET_EMBEDDED_DATA)
{
if (((TREE_CODE (decl) == VAR_DECL
&& TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
&& DECL_INITIAL (decl)
&& (DECL_INITIAL (decl) == error_mark_node
|| TREE_CONSTANT (DECL_INITIAL (decl))))
|| (TREE_CODE (decl) != VAR_DECL
&& (TREE_CODE (decl) != STRING_CST
|| !flag_writable_strings)))
&& ! (flag_pic && reloc))
readonly_data_section ();
else if (size > 0 && size <= mips_section_threshold)
SMALL_DATA_SECTION ();
else
data_section ();
}
else
{
if (size > 0 && size <= mips_section_threshold)
SMALL_DATA_SECTION ();
else if (((TREE_CODE (decl) == VAR_DECL
&& TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
&& DECL_INITIAL (decl)
&& (DECL_INITIAL (decl) == error_mark_node
|| TREE_CONSTANT (DECL_INITIAL (decl))))
|| (TREE_CODE (decl) != VAR_DECL
&& (TREE_CODE (decl) != STRING_CST
|| !flag_writable_strings)))
&& ! (flag_pic && reloc))
readonly_data_section ();
else
data_section ();
}
}
static void
mips_encode_section_info (decl, first)
tree decl;
int first;
{
if (TARGET_MIPS16)
{
if (first && TREE_CODE (decl) == STRING_CST
&& ! flag_writable_strings
&& (! current_function_decl
|| ! DECL_ONE_ONLY (current_function_decl)))
{
rtx symref;
symref = XEXP (TREE_CST_RTL (decl), 0);
mips16_strings = alloc_EXPR_LIST (0, symref, mips16_strings);
SYMBOL_REF_FLAG (symref) = 1;
mips_string_length += TREE_STRING_LENGTH (decl);
}
}
if (TARGET_EMBEDDED_DATA
&& (TREE_CODE (decl) == VAR_DECL
&& TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl))
&& (!DECL_INITIAL (decl)
|| TREE_CONSTANT (DECL_INITIAL (decl))))
{
SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 0;
}
else if (TARGET_EMBEDDED_PIC)
{
if (TREE_CODE (decl) == VAR_DECL)
SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
else if (TREE_CODE (decl) == FUNCTION_DECL)
SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 0;
else if (TREE_CODE (decl) == STRING_CST
&& ! flag_writable_strings)
SYMBOL_REF_FLAG (XEXP (TREE_CST_RTL (decl), 0)) = 0;
else
SYMBOL_REF_FLAG (XEXP (TREE_CST_RTL (decl), 0)) = 1;
}
else if (TREE_CODE (decl) == VAR_DECL
&& DECL_SECTION_NAME (decl) != NULL_TREE
&& (0 == strcmp (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)),
".sdata")
|| 0 == strcmp (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)),
".sbss")))
{
SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
}
else if (TARGET_GP_OPT && TREE_CODE (decl) == VAR_DECL
&& DECL_SECTION_NAME (decl) == NULL_TREE
&& ! (TARGET_MIPS16 && TREE_PUBLIC (decl)
&& (DECL_COMMON (decl)
|| DECL_ONE_ONLY (decl)
|| DECL_WEAK (decl))))
{
int size = int_size_in_bytes (TREE_TYPE (decl));
if (size > 0 && size <= mips_section_threshold)
SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
}
}
rtx
mips_function_value (valtype, func, mode)
tree valtype;
tree func ATTRIBUTE_UNUSED;
enum machine_mode mode;
{
int reg = GP_RETURN;
enum mode_class mclass;
int unsignedp = 1;
if (valtype)
{
mode = TYPE_MODE (valtype);
unsignedp = TREE_UNSIGNED (valtype);
mode = promote_mode (valtype, mode, &unsignedp, 1);
}
mclass = GET_MODE_CLASS (mode);
if (mclass == MODE_FLOAT && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE)
reg = FP_RETURN;
else if (mclass == MODE_FLOAT && mode == TFmode)
return gen_rtx_PARALLEL
(VOIDmode,
gen_rtvec (2,
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (DImode, FP_RETURN),
GEN_INT (0)),
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (DImode, FP_RETURN + 2),
GEN_INT (GET_MODE_SIZE (mode) / 2))));
else if (mclass == MODE_COMPLEX_FLOAT
&& GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE * 2)
{
enum machine_mode cmode = GET_MODE_INNER (mode);
return gen_rtx_PARALLEL
(VOIDmode,
gen_rtvec (2,
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (cmode, FP_RETURN),
GEN_INT (0)),
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (cmode, FP_RETURN + FP_INC),
GEN_INT (GET_MODE_SIZE (cmode)))));
}
else if (valtype && TREE_CODE (valtype) == RECORD_TYPE
&& mips_abi != ABI_32
&& mips_abi != ABI_O64
&& mips_abi != ABI_EABI)
{
tree field, fields[2];
int i;
for (i = 0, field = TYPE_FIELDS (valtype); field;
field = TREE_CHAIN (field))
{
if (TREE_CODE (field) != FIELD_DECL)
continue;
if (TREE_CODE (TREE_TYPE (field)) != REAL_TYPE || i >= 2)
break;
fields[i++] = field;
}
if (! field)
{
if (i == 1)
{
enum machine_mode field_mode = TYPE_MODE (TREE_TYPE (fields[0]));
return gen_rtx_PARALLEL
(mode,
gen_rtvec (1,
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (field_mode,
FP_RETURN),
const0_rtx)));
}
else if (i == 2)
{
enum machine_mode first_mode
= TYPE_MODE (TREE_TYPE (fields[0]));
enum machine_mode second_mode
= TYPE_MODE (TREE_TYPE (fields[1]));
HOST_WIDE_INT first_offset = int_byte_position (fields[0]);
HOST_WIDE_INT second_offset = int_byte_position (fields[1]);
return gen_rtx_PARALLEL
(mode,
gen_rtvec (2,
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (first_mode,
FP_RETURN),
GEN_INT (first_offset)),
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (second_mode,
FP_RETURN + 2),
GEN_INT (second_offset))));
}
}
}
return gen_rtx_REG (mode, reg);
}
int
function_arg_pass_by_reference (cum, mode, type, named)
const CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int named ATTRIBUTE_UNUSED;
{
int size;
if (mips_abi == ABI_32 || mips_abi == ABI_O64)
return 0;
if (cum && MUST_PASS_IN_STACK (mode, type)
&& mips_abi != ABI_MEABI
&& FUNCTION_ARG (*cum, mode, type, named) != 0)
return 1;
if (mips_abi != ABI_EABI)
return 0;
if (type == NULL_TREE || mode == DImode || mode == DFmode)
return 0;
size = int_size_in_bytes (type);
return size == -1 || size > UNITS_PER_WORD;
}
bool
mips_cannot_change_mode_class (from, to, class)
enum machine_mode from, to;
enum reg_class class;
{
if (GET_MODE_SIZE (from) != GET_MODE_SIZE (to))
{
if (TARGET_BIG_ENDIAN)
return reg_classes_intersect_p (FP_REGS, class);
if (TARGET_FLOAT64)
return reg_classes_intersect_p (HI_AND_FP_REGS, class);
return reg_classes_intersect_p (HI_REG, class);
}
return false;
}
enum reg_class
mips_secondary_reload_class (class, mode, x, in_p)
enum reg_class class;
enum machine_mode mode;
rtx x;
int in_p;
{
enum reg_class gr_regs = TARGET_MIPS16 ? M16_REGS : GR_REGS;
int regno = -1;
int gp_reg_p;
if (GET_CODE (x) == SIGN_EXTEND)
{
int off = 0;
x = XEXP (x, 0);
if (reg_renumber)
regno = true_regnum (x);
else
{
while (GET_CODE (x) == SUBREG)
{
off += subreg_regno_offset (REGNO (SUBREG_REG (x)),
GET_MODE (SUBREG_REG (x)),
SUBREG_BYTE (x),
GET_MODE (x));
x = SUBREG_REG (x);
}
if (GET_CODE (x) == REG)
regno = REGNO (x) + off;
}
if (FP_REG_P (regno))
return (class == GR_REGS ? NO_REGS : GR_REGS);
if (class == FP_REGS)
return (GP_REG_P (regno) ? NO_REGS : GR_REGS);
}
else if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
regno = true_regnum (x);
gp_reg_p = TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno);
if (class == HILO_REG && regno != GP_REG_FIRST + 0)
return ((! in_p
&& gp_reg_p
&& GET_MODE_SIZE (mode) <= GET_MODE_SIZE (SImode))
? NO_REGS : gr_regs);
else if (regno == HILO_REGNUM)
return ((in_p
&& class == gr_regs
&& GET_MODE_SIZE (mode) <= GET_MODE_SIZE (SImode))
? NO_REGS : gr_regs);
if (class == HI_REG || class == LO_REG || class == MD_REGS)
{
if (TARGET_MIPS16 && in_p)
{
return M16_REGS;
}
return gp_reg_p ? NO_REGS : gr_regs;
}
if (MD_REG_P (regno))
{
if (TARGET_MIPS16 && ! in_p)
{
return M16_REGS;
}
return class == gr_regs ? NO_REGS : gr_regs;
}
if (class == ST_REGS)
{
if (in_p)
return FP_REGS;
return GP_REG_P (regno) ? NO_REGS : GR_REGS;
}
if (ST_REG_P (regno))
{
if (! in_p)
return FP_REGS;
return class == GR_REGS ? NO_REGS : GR_REGS;
}
if (class == FP_REGS)
{
if (GET_CODE (x) == MEM)
{
return NO_REGS;
}
else if (CONSTANT_P (x) && GET_MODE_CLASS (mode) == MODE_FLOAT)
{
return NO_REGS;
}
else if (GP_REG_P (regno) || x == CONST0_RTX (mode))
{
return NO_REGS;
}
else if (FP_REG_P (regno))
{
return NO_REGS;
}
else
{
return GR_REGS;
}
}
if (TARGET_MIPS16)
{
if (class != M16_REGS && class != M16_NA_REGS)
{
if (gp_reg_p)
return NO_REGS;
return M16_REGS;
}
if (! gp_reg_p)
{
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == REG
&& GET_CODE (XEXP (x, 1)) == REG
&& (XEXP (x, 0) == stack_pointer_rtx
|| XEXP (x, 1) == stack_pointer_rtx))
return (class == M16_REGS ? M16_NA_REGS : M16_REGS);
if (class == M16_REGS || class == M16_NA_REGS)
return NO_REGS;
return M16_REGS;
}
}
return NO_REGS;
}
int
mips_class_max_nregs (class, mode)
enum reg_class class;
enum machine_mode mode;
{
if (class == FP_REGS)
return FP_INC;
else
return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
}
rtx
mips16_gp_pseudo_reg ()
{
if (cfun->machine->mips16_gp_pseudo_rtx == NULL_RTX)
{
rtx const_gp;
rtx insn, scan;
cfun->machine->mips16_gp_pseudo_rtx = gen_reg_rtx (Pmode);
RTX_UNCHANGING_P (cfun->machine->mips16_gp_pseudo_rtx) = 1;
const_gp = gen_rtx (CONST, Pmode,
gen_rtx (REG, Pmode, GP_REG_FIRST + 28));
start_sequence ();
emit_move_insn (cfun->machine->mips16_gp_pseudo_rtx,
const_gp);
insn = get_insns ();
end_sequence ();
push_topmost_sequence ();
for (scan = get_insns (); scan != NULL_RTX; scan = NEXT_INSN (scan))
if (GET_CODE (scan) == NOTE
&& NOTE_LINE_NUMBER (scan) == NOTE_INSN_FUNCTION_BEG)
break;
if (scan == NULL_RTX)
scan = get_insns ();
insn = emit_insn_after (insn, scan);
pop_topmost_sequence ();
}
return cfun->machine->mips16_gp_pseudo_rtx;
}
rtx
mips16_gp_offset (sym)
rtx sym;
{
tree gp;
if (GET_CODE (sym) != SYMBOL_REF
|| ! SYMBOL_REF_FLAG (sym))
abort ();
gp = get_identifier ("__mips16_gp_value");
return gen_rtx (CONST, Pmode,
gen_rtx (MINUS, Pmode, sym,
gen_rtx (SYMBOL_REF, Pmode,
IDENTIFIER_POINTER (gp))));
}
int
mips16_gp_offset_p (x)
rtx x;
{
if (GET_CODE (x) == CONST)
x = XEXP (x, 0);
if (GET_CODE (x) == PLUS)
{
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& SMALL_INT (XEXP (x, 1)))
return mips16_gp_offset_p (XEXP (x, 0));
if (GET_CODE (XEXP (x, 0)) == CONST_INT
&& SMALL_INT (XEXP (x, 0)))
return mips16_gp_offset_p (XEXP (x, 1));
return 0;
}
return (GET_CODE (x) == MINUS
&& GET_CODE (XEXP (x, 0)) == SYMBOL_REF
&& SYMBOL_REF_FLAG (XEXP (x, 0))
&& GET_CODE (XEXP (x, 1)) == SYMBOL_REF
&& strcmp (XSTR (XEXP (x, 1), 0), "__mips16_gp_value") == 0);
}
static void
mips16_output_gp_offset (file, x)
FILE *file;
rtx x;
{
if (GET_CODE (x) == CONST)
x = XEXP (x, 0);
if (GET_CODE (x) == PLUS)
{
mips16_output_gp_offset (file, XEXP (x, 0));
fputs ("+", file);
mips16_output_gp_offset (file, XEXP (x, 1));
return;
}
if (GET_CODE (x) == MINUS
&& GET_CODE (XEXP (x, 1)) == SYMBOL_REF
&& strcmp (XSTR (XEXP (x, 1), 0), "__mips16_gp_value") == 0)
{
mips16_output_gp_offset (file, XEXP (x, 0));
return;
}
output_addr_const (file, x);
}
int
mips16_constant_after_function_p (x)
tree x;
{
if (TREE_CODE (x) == STRING_CST
&& ! flag_writable_strings
&& current_function_decl != 0
&& ! DECL_DEFER_OUTPUT (current_function_decl)
&& ! (DECL_INLINE (current_function_decl)
&& ((! TREE_PUBLIC (current_function_decl)
&& ! TREE_ADDRESSABLE (current_function_decl)
&& ! flag_keep_inline_functions)
|| DECL_EXTERNAL (current_function_decl))))
{
struct string_constant *n;
n = (struct string_constant *) xmalloc (sizeof *n);
n->label = XSTR (XEXP (TREE_CST_RTL (x), 0), 0);
n->next = string_constants;
string_constants = n;
return 1;
}
return 0;
}
int
mips16_constant (x, mode, addr, addend)
rtx x;
enum machine_mode mode;
int addr;
int addend;
{
while (GET_CODE (x) == CONST)
x = XEXP (x, 0);
switch (GET_CODE (x))
{
default:
return 0;
case PLUS:
return (mips16_constant (XEXP (x, 0), mode, addr, 1)
&& mips16_constant (XEXP (x, 1), mode, addr, 1));
case SYMBOL_REF:
if (addr && GET_MODE_SIZE (mode) != 4 && GET_MODE_SIZE (mode) != 8)
return 0;
if (CONSTANT_POOL_ADDRESS_P (x))
return 1;
if (! addr
&& ! addend
&& SYMBOL_REF_FLAG (x)
&& mode == (enum machine_mode) Pmode)
return 1;
if (SYMBOL_REF_FLAG (x))
{
const char *name = XSTR (x, 0);
return (name[0] == '*'
&& strncmp (name + 1, LOCAL_LABEL_PREFIX,
sizeof LOCAL_LABEL_PREFIX - 1) == 0);
}
return 0;
case LABEL_REF:
if (addr && GET_MODE_SIZE (mode) != 4 && GET_MODE_SIZE (mode) != 8)
return 0;
return 1;
case CONST_INT:
if (addr && ! addend)
return 0;
return INTVAL (x) > - 0x10000 && INTVAL (x) <= 0xffff;
case REG:
return REGNO (x) == GP_REG_FIRST + 28;
}
}
static void
mips16_fp_args (file, fp_code, from_fp_p)
FILE *file;
int fp_code;
int from_fp_p;
{
const char *s;
int gparg, fparg;
unsigned int f;
if (mips_abi != ABI_32 && mips_abi != ABI_O64)
abort ();
if (from_fp_p)
s = "mfc1";
else
s = "mtc1";
gparg = GP_ARG_FIRST;
fparg = FP_ARG_FIRST;
for (f = (unsigned int) fp_code; f != 0; f >>= 2)
{
if ((f & 3) == 1)
{
if ((fparg & 1) != 0)
++fparg;
fprintf (file, "\t%s\t%s,%s\n", s,
reg_names[gparg], reg_names[fparg]);
}
else if ((f & 3) == 2)
{
if (TARGET_64BIT)
fprintf (file, "\td%s\t%s,%s\n", s,
reg_names[gparg], reg_names[fparg]);
else
{
if ((fparg & 1) != 0)
++fparg;
if (TARGET_BIG_ENDIAN)
fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
reg_names[gparg], reg_names[fparg + 1], s,
reg_names[gparg + 1], reg_names[fparg]);
else
fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
reg_names[gparg], reg_names[fparg], s,
reg_names[gparg + 1], reg_names[fparg + 1]);
++gparg;
++fparg;
}
}
else
abort ();
++gparg;
++fparg;
}
}
static void
build_mips16_function_stub (file)
FILE *file;
{
const char *fnname;
char *secname, *stubname;
tree stubid, stubdecl;
int need_comma;
unsigned int f;
fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
secname = (char *) alloca (strlen (fnname) + 20);
sprintf (secname, ".mips16.fn.%s", fnname);
stubname = (char *) alloca (strlen (fnname) + 20);
sprintf (stubname, "__fn_stub_%s", fnname);
stubid = get_identifier (stubname);
stubdecl = build_decl (FUNCTION_DECL, stubid,
build_function_type (void_type_node, NULL_TREE));
DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
fprintf (file, "\t# Stub function for %s (", current_function_name);
need_comma = 0;
for (f = (unsigned int) current_function_args_info.fp_code; f != 0; f >>= 2)
{
fprintf (file, "%s%s",
need_comma ? ", " : "",
(f & 3) == 1 ? "float" : "double");
need_comma = 1;
}
fprintf (file, ")\n");
fprintf (file, "\t.set\tnomips16\n");
function_section (stubdecl);
ASM_OUTPUT_ALIGN (file, floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT));
#ifndef FUNCTION_NAME_ALREADY_DECLARED
fputs ("\t.ent\t", file);
assemble_name (file, stubname);
fputs ("\n", file);
#endif
assemble_name (file, stubname);
fputs (":\n", file);
fprintf (file, "\t.set\tnoreorder\n");
mips16_fp_args (file, current_function_args_info.fp_code, 1);
fprintf (asm_out_file, "\t.set\tnoat\n");
fprintf (asm_out_file, "\tla\t%s,", reg_names[GP_REG_FIRST + 1]);
assemble_name (file, fnname);
fprintf (file, "\n");
fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]);
fprintf (asm_out_file, "\t.set\tat\n");
fprintf (file, "\tnop\n");
fprintf (file, "\t.set\treorder\n");
#ifndef FUNCTION_NAME_ALREADY_DECLARED
fputs ("\t.end\t", file);
assemble_name (file, stubname);
fputs ("\n", file);
#endif
fprintf (file, "\t.set\tmips16\n");
function_section (current_function_decl);
}
struct mips16_stub
{
struct mips16_stub *next;
char *name;
int fpret;
};
static struct mips16_stub *mips16_stubs;
int
build_mips16_call_stub (retval, fnmem, arg_size, fp_code)
rtx retval;
rtx fnmem;
rtx arg_size;
int fp_code;
{
int fpret;
rtx fn;
const char *fnname;
char *secname, *stubname;
struct mips16_stub *l;
tree stubid, stubdecl;
int need_comma;
unsigned int f;
if (! TARGET_MIPS16 || ! mips16_hard_float)
return 0;
fpret = (retval != 0
&& GET_MODE_CLASS (GET_MODE (retval)) == MODE_FLOAT
&& GET_MODE_SIZE (GET_MODE (retval)) <= UNITS_PER_FPVALUE);
if (fp_code == 0 && ! fpret)
return 0;
if (GET_CODE (fnmem) != MEM)
abort ();
fn = XEXP (fnmem, 0);
if (GET_CODE (fn) == SYMBOL_REF
&& strncmp (XSTR (fn, 0), "__mips16_", 9) == 0)
return 0;
if (mips_abi != ABI_32 && mips_abi != ABI_O64)
abort ();
if (fpret && GET_MODE (retval) != SFmode && GET_MODE (retval) != DFmode)
abort ();
if (GET_CODE (fn) != SYMBOL_REF)
{
char buf[30];
tree id;
rtx stub_fn, stub_mem, insn;
sprintf (buf, "__mips16_call_stub_%s%d",
(fpret
? (GET_MODE (retval) == SFmode ? "sf_" : "df_")
: ""),
fp_code);
id = get_identifier (buf);
stub_fn = gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (id));
stub_mem = gen_rtx (MEM, Pmode, stub_fn);
emit_move_insn (gen_rtx (REG, Pmode, 2), fn);
if (retval == NULL_RTX)
insn = gen_call_internal0 (stub_mem, arg_size,
gen_rtx (REG, SImode,
GP_REG_FIRST + 31));
else
insn = gen_call_value_internal0 (retval, stub_mem, arg_size,
gen_rtx (REG, SImode,
GP_REG_FIRST + 31));
insn = emit_call_insn (insn);
if (GET_CODE (insn) != CALL_INSN)
abort ();
CALL_INSN_FUNCTION_USAGE (insn) =
gen_rtx (EXPR_LIST, VOIDmode,
gen_rtx (USE, VOIDmode, gen_rtx (REG, Pmode, 2)),
CALL_INSN_FUNCTION_USAGE (insn));
if (fpret)
CALL_INSN_FUNCTION_USAGE (insn) =
gen_rtx (EXPR_LIST, VOIDmode,
gen_rtx (USE, VOIDmode, gen_rtx (REG, word_mode, 18)),
CALL_INSN_FUNCTION_USAGE (insn));
return 1;
}
fnname = XSTR (fn, 0);
for (l = mips16_stubs; l != NULL; l = l->next)
if (strcmp (l->name, fnname) == 0)
break;
if (l == NULL)
{
secname = (char *) alloca (strlen (fnname) + 40);
sprintf (secname, ".mips16.call.%s%s",
fpret ? "fp." : "",
fnname);
stubname = (char *) alloca (strlen (fnname) + 20);
sprintf (stubname, "__call_stub_%s%s",
fpret ? "fp_" : "",
fnname);
stubid = get_identifier (stubname);
stubdecl = build_decl (FUNCTION_DECL, stubid,
build_function_type (void_type_node, NULL_TREE));
DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
fprintf (asm_out_file, "\t# Stub function to call %s%s (",
(fpret
? (GET_MODE (retval) == SFmode ? "float " : "double ")
: ""),
fnname);
need_comma = 0;
for (f = (unsigned int) fp_code; f != 0; f >>= 2)
{
fprintf (asm_out_file, "%s%s",
need_comma ? ", " : "",
(f & 3) == 1 ? "float" : "double");
need_comma = 1;
}
fprintf (asm_out_file, ")\n");
fprintf (asm_out_file, "\t.set\tnomips16\n");
assemble_start_function (stubdecl, stubname);
#ifndef FUNCTION_NAME_ALREADY_DECLARED
fputs ("\t.ent\t", asm_out_file);
assemble_name (asm_out_file, stubname);
fputs ("\n", asm_out_file);
assemble_name (asm_out_file, stubname);
fputs (":\n", asm_out_file);
#endif
fprintf (asm_out_file, "\t.set\tnoreorder\n");
mips16_fp_args (asm_out_file, fp_code, 0);
if (! fpret)
{
fprintf (asm_out_file, "\t.set\tnoat\n");
fprintf (asm_out_file, "\tla\t%s,%s\n", reg_names[GP_REG_FIRST + 1],
fnname);
fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]);
fprintf (asm_out_file, "\t.set\tat\n");
fprintf (asm_out_file, "\tnop\n");
}
else
{
fprintf (asm_out_file, "\tmove\t%s,%s\n",
reg_names[GP_REG_FIRST + 18], reg_names[GP_REG_FIRST + 31]);
fprintf (asm_out_file, "\tjal\t%s\n", fnname);
fprintf (asm_out_file, "\tnop\n");
if (GET_MODE (retval) == SFmode)
fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
reg_names[GP_REG_FIRST + 2], reg_names[FP_REG_FIRST + 0]);
else
{
if (TARGET_BIG_ENDIAN)
{
fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
reg_names[GP_REG_FIRST + 2],
reg_names[FP_REG_FIRST + 1]);
fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
reg_names[GP_REG_FIRST + 3],
reg_names[FP_REG_FIRST + 0]);
}
else
{
fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
reg_names[GP_REG_FIRST + 2],
reg_names[FP_REG_FIRST + 0]);
fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
reg_names[GP_REG_FIRST + 3],
reg_names[FP_REG_FIRST + 1]);
}
}
fprintf (asm_out_file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 18]);
fprintf (asm_out_file, "\tnop\n");
}
fprintf (asm_out_file, "\t.set\treorder\n");
#ifdef ASM_DECLARE_FUNCTION_SIZE
ASM_DECLARE_FUNCTION_SIZE (asm_out_file, stubname, stubdecl);
#endif
#ifndef FUNCTION_NAME_ALREADY_DECLARED
fputs ("\t.end\t", asm_out_file);
assemble_name (asm_out_file, stubname);
fputs ("\n", asm_out_file);
#endif
fprintf (asm_out_file, "\t.set\tmips16\n");
l = (struct mips16_stub *) xmalloc (sizeof *l);
l->name = xstrdup (fnname);
l->fpret = fpret;
l->next = mips16_stubs;
mips16_stubs = l;
}
if (fpret && ! l->fpret)
error ("can not handle inconsistent calls to `%s'", fnname);
if (l->fpret)
{
rtx insn;
if (retval == NULL_RTX)
insn = gen_call_internal0 (fnmem, arg_size,
gen_rtx (REG, SImode,
GP_REG_FIRST + 31));
else
insn = gen_call_value_internal0 (retval, fnmem, arg_size,
gen_rtx (REG, SImode,
GP_REG_FIRST + 31));
insn = emit_call_insn (insn);
if (GET_CODE (insn) != CALL_INSN)
abort ();
CALL_INSN_FUNCTION_USAGE (insn) =
gen_rtx (EXPR_LIST, VOIDmode,
gen_rtx (USE, VOIDmode, gen_rtx (REG, word_mode, 18)),
CALL_INSN_FUNCTION_USAGE (insn));
return 1;
}
return 0;
}
static void
mips16_optimize_gp (first)
rtx first;
{
rtx gpcopy, slot, insn;
gpcopy = NULL_RTX;
slot = NULL_RTX;
for (insn = first; insn != NULL_RTX; insn = next_active_insn (insn))
{
rtx set;
if (! INSN_P (insn))
continue;
set = PATTERN (insn);
if (GET_CODE (set) != SET)
continue;
if (gpcopy == NULL_RTX
&& GET_CODE (SET_SRC (set)) == CONST
&& GET_CODE (XEXP (SET_SRC (set), 0)) == REG
&& REGNO (XEXP (SET_SRC (set), 0)) == GP_REG_FIRST + 28
&& GET_CODE (SET_DEST (set)) == REG
&& GET_MODE (SET_DEST (set)) == (unsigned) Pmode)
gpcopy = SET_DEST (set);
else if (slot == NULL_RTX
&& gpcopy != NULL_RTX
&& GET_CODE (SET_DEST (set)) == MEM
&& GET_CODE (SET_SRC (set)) == REG
&& REGNO (SET_SRC (set)) == REGNO (gpcopy)
&& GET_MODE (SET_DEST (set)) == (unsigned) Pmode)
{
rtx base, offset;
offset = const0_rtx;
base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
if (GET_CODE (base) == REG
&& (REGNO (base) == STACK_POINTER_REGNUM
|| REGNO (base) == FRAME_POINTER_REGNUM))
slot = SET_DEST (set);
}
else if (gpcopy != NULL_RTX
&& (GET_CODE (SET_DEST (set)) == REG
|| GET_CODE (SET_DEST (set)) == SUBREG)
&& reg_overlap_mentioned_p (SET_DEST (set), gpcopy)
&& (GET_CODE (SET_DEST (set)) != REG
|| REGNO (SET_DEST (set)) != REGNO (gpcopy)
|| GET_MODE (SET_DEST (set)) != (unsigned) Pmode
|| ((GET_CODE (SET_SRC (set)) != CONST
|| GET_CODE (XEXP (SET_SRC (set), 0)) != REG
|| (REGNO (XEXP (SET_SRC (set), 0))
!= GP_REG_FIRST + 28))
&& ! rtx_equal_p (SET_SRC (set), slot))))
break;
else if (slot != NULL_RTX
&& GET_CODE (SET_DEST (set)) == MEM
&& rtx_equal_p (SET_DEST (set), slot)
&& (GET_CODE (SET_SRC (set)) != REG
|| REGNO (SET_SRC (set)) != REGNO (gpcopy)))
break;
}
if (insn != NULL_RTX || gpcopy == NULL_RTX || slot == NULL_RTX)
{
rtx next;
if (Pmode != SImode)
return;
for (insn = first; insn != NULL_RTX; insn = next)
{
rtx set1, set2;
next = insn;
do
{
next = NEXT_INSN (next);
}
while (next != NULL_RTX
&& (GET_CODE (next) == NOTE
|| (GET_CODE (next) == INSN
&& (GET_CODE (PATTERN (next)) == USE
|| GET_CODE (PATTERN (next)) == CLOBBER))));
if (next == NULL_RTX)
break;
if (! INSN_P (insn))
continue;
if (! INSN_P (next))
continue;
set1 = PATTERN (insn);
if (GET_CODE (set1) != SET)
continue;
set2 = PATTERN (next);
if (GET_CODE (set2) != SET)
continue;
if (GET_CODE (SET_DEST (set1)) == REG
&& GET_CODE (SET_SRC (set1)) == CONST
&& GET_CODE (XEXP (SET_SRC (set1), 0)) == REG
&& REGNO (XEXP (SET_SRC (set1), 0)) == GP_REG_FIRST + 28
&& rtx_equal_p (SET_DEST (set1), SET_DEST (set2))
&& GET_CODE (SET_SRC (set2)) == PLUS
&& rtx_equal_p (SET_DEST (set1), XEXP (SET_SRC (set2), 0))
&& mips16_gp_offset_p (XEXP (SET_SRC (set2), 1))
&& GET_CODE (XEXP (XEXP (SET_SRC (set2), 1), 0)) == MINUS)
{
rtx sym;
sym = XEXP (XEXP (XEXP (SET_SRC (set2), 1), 0), 0);
if (GET_CODE (sym) != SYMBOL_REF)
abort ();
emit_insn_after (gen_rtx (SET, VOIDmode, SET_DEST (set1),
force_const_mem (Pmode, sym)),
next);
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
PUT_CODE (next, NOTE);
NOTE_LINE_NUMBER (next) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (next) = 0;
}
}
return;
}
for (insn = first; insn != NULL_RTX; insn = next_active_insn (insn))
{
rtx set;
if (! INSN_P (insn))
continue;
set = PATTERN (insn);
if (GET_CODE (set) != SET
|| GET_MODE (SET_DEST (set)) != (unsigned) Pmode)
continue;
if (GET_CODE (SET_DEST (set)) == MEM
&& rtx_equal_p (SET_DEST (set), slot)
&& GET_CODE (SET_SRC (set)) == REG
&& REGNO (SET_SRC (set)) == REGNO (gpcopy))
{
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
}
else if (GET_CODE (SET_DEST (set)) == REG
&& REGNO (SET_DEST (set)) == REGNO (gpcopy)
&& GET_CODE (SET_SRC (set)) == MEM
&& rtx_equal_p (SET_SRC (set), slot))
{
emit_insn_after (gen_rtx (SET, Pmode, SET_DEST (set),
gen_rtx (CONST, Pmode,
gen_rtx (REG, Pmode,
GP_REG_FIRST + 28))),
insn);
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
}
}
}
struct constant
{
struct constant *next;
rtx value;
rtx label;
enum machine_mode mode;
};
static rtx
add_constant (pconstants, val, mode)
struct constant **pconstants;
rtx val;
enum machine_mode mode;
{
struct constant *c;
for (c = *pconstants; c != NULL; c = c->next)
if (mode == c->mode && rtx_equal_p (val, c->value))
return c->label;
c = (struct constant *) xmalloc (sizeof *c);
c->value = val;
c->mode = mode;
c->label = gen_label_rtx ();
c->next = *pconstants;
*pconstants = c;
return c->label;
}
static void
dump_constants (constants, insn)
struct constant *constants;
rtx insn;
{
struct constant *c;
int align;
c = constants;
align = 0;
while (c != NULL)
{
rtx r;
struct constant *next;
switch (GET_MODE_SIZE (c->mode))
{
case 1:
align = 0;
break;
case 2:
if (align < 1)
insn = emit_insn_after (gen_align_2 (), insn);
align = 1;
break;
case 4:
if (align < 2)
insn = emit_insn_after (gen_align_4 (), insn);
align = 2;
break;
default:
if (align < 3)
insn = emit_insn_after (gen_align_8 (), insn);
align = 3;
break;
}
insn = emit_label_after (c->label, insn);
switch (c->mode)
{
case QImode:
r = gen_consttable_qi (c->value);
break;
case HImode:
r = gen_consttable_hi (c->value);
break;
case SImode:
r = gen_consttable_si (c->value);
break;
case SFmode:
r = gen_consttable_sf (c->value);
break;
case DImode:
r = gen_consttable_di (c->value);
break;
case DFmode:
r = gen_consttable_df (c->value);
break;
default:
abort ();
}
insn = emit_insn_after (r, insn);
next = c->next;
free (c);
c = next;
}
emit_barrier_after (insn);
}
static rtx
mips_find_symbol (addr)
rtx addr;
{
if (GET_CODE (addr) == MEM)
addr = XEXP (addr, 0);
while (GET_CODE (addr) == CONST)
addr = XEXP (addr, 0);
if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
return addr;
if (GET_CODE (addr) == PLUS)
{
rtx l1, l2;
l1 = mips_find_symbol (XEXP (addr, 0));
l2 = mips_find_symbol (XEXP (addr, 1));
if (l1 != NULL_RTX && l2 == NULL_RTX)
return l1;
else if (l1 == NULL_RTX && l2 != NULL_RTX)
return l2;
}
return NULL_RTX;
}
void
machine_dependent_reorg (first)
rtx first;
{
int insns_len, max_internal_pool_size, pool_size, addr, first_constant_ref;
rtx insn;
struct constant *constants;
if (! TARGET_MIPS16)
return;
if (optimize)
mips16_optimize_gp (first);
insns_len = 0;
for (insn = first; insn; insn = NEXT_INSN (insn))
{
insns_len += get_attr_length (insn);
if (GET_CODE (insn) == JUMP_INSN)
{
rtx body;
body = PATTERN (insn);
if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC)
insns_len += (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC)
* GET_MODE_SIZE (GET_MODE (body)));
insns_len += GET_MODE_SIZE (GET_MODE (body)) - 1;
}
}
cfun->machine->insns_len = insns_len;
pool_size = get_pool_size ();
if (insns_len + pool_size + mips_string_length < 0x8000)
return;
max_internal_pool_size = 0;
for (insn = first; insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) == SET)
{
rtx src;
src = mips_find_symbol (SET_SRC (PATTERN (insn)));
if (src == NULL_RTX)
continue;
if (CONSTANT_POOL_ADDRESS_P (src))
max_internal_pool_size += GET_MODE_SIZE (get_pool_mode (src));
else if (SYMBOL_REF_FLAG (src))
max_internal_pool_size += GET_MODE_SIZE (Pmode);
}
}
constants = NULL;
addr = 0;
first_constant_ref = -1;
for (insn = first; insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) == SET)
{
rtx val, src;
enum machine_mode mode = VOIDmode;
val = NULL_RTX;
src = mips_find_symbol (SET_SRC (PATTERN (insn)));
if (src != NULL_RTX && CONSTANT_POOL_ADDRESS_P (src))
{
if (((insns_len - addr)
+ max_internal_pool_size
+ get_pool_offset (src))
>= 0x8000)
{
val = get_pool_constant (src);
mode = get_pool_mode (src);
}
max_internal_pool_size -= GET_MODE_SIZE (get_pool_mode (src));
}
else if (src != NULL_RTX && SYMBOL_REF_FLAG (src))
{
if (((insns_len - addr)
+ max_internal_pool_size
+ pool_size
+ mips_string_length)
>= 0x8000)
{
val = src;
mode = Pmode;
}
max_internal_pool_size -= Pmode;
}
if (val != NULL_RTX)
{
rtx lab, newsrc;
lab = add_constant (&constants, val, mode);
newsrc = gen_rtx (MEM, mode,
gen_rtx (LABEL_REF, VOIDmode, lab));
RTX_UNCHANGING_P (newsrc) = 1;
PATTERN (insn) = gen_rtx (SET, VOIDmode,
SET_DEST (PATTERN (insn)),
newsrc);
INSN_CODE (insn) = -1;
if (first_constant_ref < 0)
first_constant_ref = addr;
}
}
addr += get_attr_length (insn);
if (GET_CODE (insn) == JUMP_INSN)
{
rtx body;
body = PATTERN (insn);
if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC)
addr += (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC)
* GET_MODE_SIZE (GET_MODE (body)));
addr += GET_MODE_SIZE (GET_MODE (body)) - 1;
}
if (GET_CODE (insn) == BARRIER)
{
if (constants != NULL)
dump_constants (constants, insn);
constants = NULL;
first_constant_ref = -1;
}
if (constants != NULL
&& (NEXT_INSN (insn) == NULL
|| (first_constant_ref >= 0
&& (((addr - first_constant_ref)
+ 2
+ 2
+ pool_size)
>= 0x8000))))
{
rtx label, jump, barrier;
label = gen_label_rtx ();
jump = emit_jump_insn_after (gen_jump (label), insn);
JUMP_LABEL (jump) = label;
LABEL_NUSES (label) = 1;
barrier = emit_barrier_after (jump);
emit_label_after (label, barrier);
first_constant_ref = -1;
}
}
}
int
extend_operator (x, mode)
rtx x;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
enum rtx_code code = GET_CODE (x);
return code == SIGN_EXTEND || code == ZERO_EXTEND;
}
int
highpart_shift_operator (x, mode)
rtx x;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
enum rtx_code code = GET_CODE (x);
return (code == LSHIFTRT
|| code == ASHIFTRT
|| code == ROTATERT
|| code == ROTATE);
}
int
mips_register_move_cost (mode, to, from)
enum machine_mode mode ATTRIBUTE_UNUSED;
enum reg_class to, from;
{
if (from == M16_REGS && GR_REG_CLASS_P (to))
return 2;
else if (from == M16_NA_REGS && GR_REG_CLASS_P (to))
return 2;
else if (GR_REG_CLASS_P (from))
{
if (to == M16_REGS)
return 2;
else if (to == M16_NA_REGS)
return 2;
else if (GR_REG_CLASS_P (to))
{
if (TARGET_MIPS16)
return 4;
else
return 2;
}
else if (to == FP_REGS)
return 4;
else if (to == HI_REG || to == LO_REG || to == MD_REGS
|| to == HILO_REG)
{
if (TARGET_MIPS16)
return 12;
else
return 6;
}
else if (COP_REG_CLASS_P (to))
{
return 5;
}
}
else if (from == FP_REGS)
{
if (GR_REG_CLASS_P (to))
return 4;
else if (to == FP_REGS)
return 2;
else if (to == ST_REGS)
return 8;
}
else if (from == HI_REG || from == LO_REG || from == MD_REGS
|| from == HILO_REG)
{
if (GR_REG_CLASS_P (to))
{
if (TARGET_MIPS16)
return 12;
else
return 6;
}
}
else if (from == ST_REGS && GR_REG_CLASS_P (to))
return 4;
else if (COP_REG_CLASS_P (from))
{
return 5;
}
return 12;
}
int
mips_adjust_insn_length (insn, length)
rtx insn;
int length;
{
if (simplejump_p (insn)
|| (!TARGET_MIPS16 && (GET_CODE (insn) == JUMP_INSN
|| GET_CODE (insn) == CALL_INSN)))
length += 4;
if (TARGET_MIPS16)
length /= 2;
return length;
}
const char *
mips_output_conditional_branch (insn,
operands,
two_operands_p,
float_p,
inverted_p,
length)
rtx insn;
rtx *operands;
int two_operands_p;
int float_p;
int inverted_p;
int length;
{
static char buffer[200];
enum rtx_code code = GET_CODE (operands[0]);
int need_z_p;
const char *op1 = "%z2";
const char *op2 = (two_operands_p ? ",%z3" : ",%.");
const char *const comp = (float_p ? "%F0" : "%C0");
const char *const inverted_comp = (float_p ? "%W0" : "%N0");
mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
if (!two_operands_p)
{
switch (code)
{
case GTU:
code = NE;
break;
case LEU:
code = EQ;
break;
case GEU:
code = EQ;
op1 = "%.";
break;
case LTU:
code = NE;
op1 = "%.";
break;
default:
break;
}
}
need_z_p = (!float_p && code != EQ && code != NE);
if (need_z_p)
op2 = "";
buffer[0] = '\0';
switch (length)
{
case 4:
case 8:
if (float_p)
sprintf (buffer, "%%*b%s%%?\t%%Z2%%1",
inverted_p ? inverted_comp : comp);
else
sprintf (buffer, "%%*b%s%s%%?\t%s%s,%%1",
inverted_p ? inverted_comp : comp,
need_z_p ? "z" : "",
op1,
op2);
return buffer;
case 12:
case 16:
case 24:
case 28:
{
rtx orig_target;
rtx target = gen_label_rtx ();
orig_target = operands[1];
operands[1] = target;
if (float_p)
sprintf (buffer, "%%*b%s\t%%Z2%%1",
inverted_p ? comp : inverted_comp);
else
sprintf (buffer, "%%*b%s%s\t%s%s,%%1",
inverted_p ? comp : inverted_comp,
need_z_p ? "z" : "",
op1,
op2);
output_asm_insn (buffer, operands);
if (length != 16 && length != 28 && ! mips_branch_likely)
{
rtx insn = final_sequence;
final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file,
optimize, 0, 1);
INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
}
else
output_asm_insn ("%#", 0);
if (length <= 16)
output_asm_insn ("j\t%0", &orig_target);
else
{
if (Pmode == DImode)
output_asm_insn ("%[dla\t%@,%0\n\tjr\t%@%]", &orig_target);
else
output_asm_insn ("%[la\t%@,%0\n\tjr\t%@%]", &orig_target);
}
if (length != 16 && length != 28 && mips_branch_likely)
{
rtx insn = final_sequence;
final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file,
optimize, 0, 1);
INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
}
else
output_asm_insn ("%#", 0);
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
CODE_LABEL_NUMBER (target));
return "";
}
#if 0
case 16:
case 20:
{
const char *const target
= ((mips_branch_likely || length == 20) ? ".+20" : ".+16");
const char *at_register = mips_reg_names[ASSEMBLER_SCRATCH_REGNUM];
const char *gp_register = mips_reg_names[PIC_OFFSET_TABLE_REGNUM];
char *c;
strcpy (buffer, "%(%<%[");
c = strchr (buffer, '\0');
if (float_p)
sprintf (c, "%%*b%s\t%%Z2%s",
inverted_p ? comp : inverted_comp,
target);
else
sprintf (c, "%%*b%s%s\t%s%s,%s",
inverted_p ? comp : inverted_comp,
need_z_p ? "z" : "",
op1,
op2,
target);
c = strchr (buffer, '\0');
sprintf (c,
"\n\tlw\t%s,%%%%got_page(%%1)(%s)\n\tdaddiu\t%s,%s,%%%%got_ofst(%%1)\n\tjr\t%s",
at_register,
gp_register,
at_register,
at_register,
at_register);
if (length == 20)
strcat (buffer, "\n\tnop");
strcat (buffer, "%]%>%)");
return buffer;
}
#endif
default:
abort ();
}
return 0;
}
static bool
mips_strict_matching_cpu_name_p (canonical, given)
const char *canonical, *given;
{
while (*given != 0 && TOLOWER (*given) == TOLOWER (*canonical))
given++, canonical++;
return ((*given == 0 && *canonical == 0)
|| (strcmp (canonical, "000") == 0 && strcasecmp (given, "k") == 0));
}
static bool
mips_matching_cpu_name_p (canonical, given)
const char *canonical, *given;
{
if (mips_strict_matching_cpu_name_p (canonical, given))
return true;
if (TOLOWER (*given) == 'r')
given++;
if (!ISDIGIT (*given))
return false;
if (TOLOWER (canonical[0]) == 'v' && TOLOWER (canonical[1]) == 'r')
canonical += 2;
else if (TOLOWER (canonical[0]) == 'r' && TOLOWER (canonical[1]) == 'm')
canonical += 2;
else if (TOLOWER (canonical[0]) == 'r')
canonical += 1;
return mips_strict_matching_cpu_name_p (canonical, given);
}
static const struct mips_cpu_info *
mips_parse_cpu (option, cpu_string)
const char *option, *cpu_string;
{
const struct mips_cpu_info *p;
const char *s;
for (s = cpu_string; *s != 0; s++)
if (ISUPPER (*s))
{
warning ("the cpu name must be lower case");
break;
}
if (strcasecmp (cpu_string, "from-abi") == 0)
return mips_cpu_info_from_isa (ABI_NEEDS_32BIT_REGS ? 1
: ABI_NEEDS_64BIT_REGS ? 3
: (TARGET_64BIT ? 3 : 1));
if (strcasecmp (cpu_string, "default") == 0)
return 0;
for (p = mips_cpu_info_table; p->name != 0; p++)
if (mips_matching_cpu_name_p (p->name, cpu_string))
return p;
error ("bad value (%s) for %s", cpu_string, option);
return 0;
}
static const struct mips_cpu_info *
mips_cpu_info_from_isa (isa)
int isa;
{
const struct mips_cpu_info *p;
for (p = mips_cpu_info_table; p->name != 0; p++)
if (p->isa == isa)
return p;
return 0;
}
static int
mips_adjust_cost (insn, link, dep, cost)
rtx insn ATTRIBUTE_UNUSED;
rtx link;
rtx dep ATTRIBUTE_UNUSED;
int cost;
{
if (REG_NOTE_KIND (link) != 0)
return 0;
return cost;
}
static void
mips_unique_section (decl, reloc)
tree decl;
int reloc;
{
int len, size, sec;
const char *name, *prefix;
char *string;
static const char *const prefixes[4][2] = {
{ ".text.", ".gnu.linkonce.t." },
{ ".rodata.", ".gnu.linkonce.r." },
{ ".data.", ".gnu.linkonce.d." },
{ ".sdata.", ".gnu.linkonce.s." }
};
name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
name = (* targetm.strip_name_encoding) (name);
size = int_size_in_bytes (TREE_TYPE (decl));
if (TREE_CODE (decl) == FUNCTION_DECL)
sec = 0;
else if (DECL_INITIAL (decl) == 0
|| DECL_INITIAL (decl) == error_mark_node)
sec = 2;
else if ((TARGET_EMBEDDED_PIC || TARGET_MIPS16)
&& TREE_CODE (decl) == STRING_CST
&& !flag_writable_strings)
{
sec = 0;
}
else if (TARGET_EMBEDDED_DATA)
{
if (decl_readonly_section (decl, reloc))
sec = 1;
else if (size > 0 && size <= mips_section_threshold)
sec = 3;
else
sec = 2;
}
else
{
if (size > 0 && size <= mips_section_threshold)
sec = 3;
else if (decl_readonly_section (decl, reloc))
sec = 1;
else
sec = 2;
}
prefix = prefixes[sec][DECL_ONE_ONLY (decl)];
len = strlen (name) + strlen (prefix);
string = alloca (len + 1);
sprintf (string, "%s%s", prefix, name);
DECL_SECTION_NAME (decl) = build_string (len, string);
}
unsigned int
mips_hard_regno_nregs (regno, mode)
int regno;
enum machine_mode mode;
{
if (! FP_REG_P (regno))
return ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD);
else
return ((GET_MODE_SIZE (mode) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG);
}
int
mips_return_in_memory (type)
tree type;
{
if (mips_abi == ABI_32 || mips_abi == ABI_O64)
return (TYPE_MODE (type) == BLKmode);
else
return ((int_size_in_bytes (type) > (2 * UNITS_PER_WORD))
|| (int_size_in_bytes (type) == -1));
}
static int
mips_issue_rate ()
{
switch (mips_tune)
{
case PROCESSOR_R3000: return 1;
case PROCESSOR_R5400: return 2;
case PROCESSOR_R5500: return 2;
default:
return 1;
}
abort ();
}
static int
mips_use_dfa_pipeline_interface ()
{
switch (mips_tune)
{
case PROCESSOR_R5400:
case PROCESSOR_R5500:
case PROCESSOR_SR71000:
return true;
default:
return false;
}
}
const char *
mips_emit_prefetch (operands)
rtx operands[];
{
int write = INTVAL (operands[1]);
int locality = INTVAL (operands[2]);
static const char * const alt[2][4] = {
{
"pref\t4,%a0",
"pref\t0,%a0",
"pref\t0,%a0",
"pref\t6,%a0"
},
{
"pref\t5,%a0",
"pref\t1,%a0",
"pref\t1,%a0",
"pref\t7,%a0"
}
};
return alt[write][locality];
}
#ifdef TARGET_IRIX6
static void
iris6_asm_named_section_1 (name, flags, align)
const char *name;
unsigned int flags;
unsigned int align;
{
unsigned int sh_type, sh_flags, sh_entsize;
sh_flags = 0;
if (!(flags & SECTION_DEBUG))
sh_flags |= 2;
if (flags & SECTION_WRITE)
sh_flags |= 1;
if (flags & SECTION_CODE)
sh_flags |= 4;
if (flags & SECTION_SMALL)
sh_flags |= 0x10000000;
if (strcmp (name, ".debug_frame") == 0)
sh_flags |= 0x08000000;
if (flags & SECTION_DEBUG)
sh_type = 0x7000001e;
else if (flags & SECTION_BSS)
sh_type = 8;
else
sh_type = 1;
if (flags & SECTION_CODE)
sh_entsize = 4;
else
sh_entsize = 0;
fprintf (asm_out_file, "\t.section %s,%#x,%#x,%u,%u\n",
name, sh_type, sh_flags, sh_entsize, align);
}
static void
iris6_asm_named_section (name, flags)
const char *name;
unsigned int flags;
{
if (TARGET_FILE_SWITCHING && (flags & SECTION_CODE))
asm_out_file = asm_out_text_file;
iris6_asm_named_section_1 (name, flags, 0);
}
struct iris_section_align_entry
{
const char *name;
unsigned int log;
unsigned int flags;
};
static htab_t iris_section_align_htab;
static FILE *iris_orig_asm_out_file;
static int
iris_section_align_entry_eq (p1, p2)
const PTR p1;
const PTR p2;
{
const struct iris_section_align_entry *old = p1;
const char *new = p2;
return strcmp (old->name, new) == 0;
}
static hashval_t
iris_section_align_entry_hash (p)
const PTR p;
{
const struct iris_section_align_entry *old = p;
return htab_hash_string (old->name);
}
void
iris6_asm_output_align (file, log)
FILE *file;
unsigned int log;
{
const char *section = current_section_name ();
struct iris_section_align_entry **slot, *entry;
if (! section)
abort ();
slot = (struct iris_section_align_entry **)
htab_find_slot_with_hash (iris_section_align_htab, section,
htab_hash_string (section), INSERT);
entry = *slot;
if (! entry)
{
entry = (struct iris_section_align_entry *)
xmalloc (sizeof (struct iris_section_align_entry));
*slot = entry;
entry->name = section;
entry->log = log;
entry->flags = current_section_flags ();
}
else if (entry->log < log)
entry->log = log;
fprintf (file, "\t.align\t%u\n", log);
}
void
iris6_asm_file_start (stream)
FILE *stream;
{
mips_asm_file_start (stream);
iris_orig_asm_out_file = asm_out_file;
stream = tmpfile ();
asm_out_file = stream;
asm_out_data_file = stream;
if (! TARGET_FILE_SWITCHING)
asm_out_text_file = stream;
iris_section_align_htab = htab_create (31, iris_section_align_entry_hash,
iris_section_align_entry_eq, NULL);
}
static int
iris6_section_align_1 (slot, data)
void **slot;
void *data ATTRIBUTE_UNUSED;
{
const struct iris_section_align_entry *entry
= *(const struct iris_section_align_entry **) slot;
iris6_asm_named_section_1 (entry->name, entry->flags, 1 << entry->log);
return 1;
}
void
iris6_asm_file_end (stream)
FILE *stream;
{
asm_out_file = iris_orig_asm_out_file;
htab_traverse (iris_section_align_htab, iris6_section_align_1, NULL);
copy_file_data (asm_out_file, stream);
mips_asm_file_end (stream);
}
#endif
#include "gt-mips.h"