Index: head/contrib/binutils/gas/config/tc-mips.c =================================================================== --- head/contrib/binutils/gas/config/tc-mips.c (revision 312898) +++ head/contrib/binutils/gas/config/tc-mips.c (revision 312899) @@ -1,15449 +1,15450 @@ /* tc-mips.c -- assemble code for a MIPS chip. Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. Contributed by the OSF and Ralph Campbell. Written by Keith Knowles and Ralph Campbell, working independently. Modified for ECOFF and R4000 support by Ian Lance Taylor of Cygnus Support. This file is part of GAS. GAS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GAS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GAS; see the file COPYING. If not, write to the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "as.h" #include "config.h" #include "subsegs.h" #include "safe-ctype.h" #include "opcode/mips.h" #include "itbl-ops.h" #include "dwarf2dbg.h" #include "dw2gencfi.h" #ifdef DEBUG #define DBG(x) printf x #else #define DBG(x) #endif #ifdef OBJ_MAYBE_ELF /* Clean up namespace so we can include obj-elf.h too. */ static int mips_output_flavor (void); static int mips_output_flavor (void) { return OUTPUT_FLAVOR; } #undef OBJ_PROCESS_STAB #undef OUTPUT_FLAVOR #undef S_GET_ALIGN #undef S_GET_SIZE #undef S_SET_ALIGN #undef S_SET_SIZE #undef obj_frob_file #undef obj_frob_file_after_relocs #undef obj_frob_symbol #undef obj_pop_insert #undef obj_sec_sym_ok_for_reloc #undef OBJ_COPY_SYMBOL_ATTRIBUTES #include "obj-elf.h" /* Fix any of them that we actually care about. */ #undef OUTPUT_FLAVOR #define OUTPUT_FLAVOR mips_output_flavor() #endif #if defined (OBJ_ELF) #include "elf/mips.h" #endif #ifndef ECOFF_DEBUGGING #define NO_ECOFF_DEBUGGING #define ECOFF_DEBUGGING 0 #endif int mips_flag_mdebug = -1; /* Control generation of .pdr sections. Off by default on IRIX: the native linker doesn't know about and discards them, but relocations against them remain, leading to rld crashes. */ #ifdef TE_IRIX int mips_flag_pdr = FALSE; #else int mips_flag_pdr = TRUE; #endif /* Control generation of error message for unsupported instructions in Octeon. Octeon does not have floating point, and all the instructions that use floating point registers are not allowed in Elf targets but are allowed in Linux targets by default. */ #ifdef OCTEON_ERROR_ON_UNSUPPORTED static int octeon_error_on_unsupported = 1; #else static int octeon_error_on_unsupported = 0; #endif /* Control generation of Octeon/MIPS unaligned load/store instructions. For ELF target, default to Octeon load/store instructions. For Linux target, default to MIPS load/store instructions. */ #ifdef OCTEON_USE_UNALIGN static int octeon_use_unalign = 1; #else static int octeon_use_unalign = 0; #endif #include "ecoff.h" #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) static char *mips_regmask_frag; #endif #define ZERO 0 #define AT 1 #define TREG 24 #define PIC_CALL_REG 25 #define KT0 26 #define KT1 27 #define GP 28 #define SP 29 #define FP 30 #define RA 31 #define ILLEGAL_REG (32) /* Allow override of standard little-endian ECOFF format. */ #ifndef ECOFF_LITTLE_FORMAT #define ECOFF_LITTLE_FORMAT "ecoff-littlemips" #endif extern int target_big_endian; /* The name of the readonly data section. */ #define RDATA_SECTION_NAME (OUTPUT_FLAVOR == bfd_target_ecoff_flavour \ ? ".rdata" \ : OUTPUT_FLAVOR == bfd_target_coff_flavour \ ? ".rdata" \ : OUTPUT_FLAVOR == bfd_target_elf_flavour \ ? ".rodata" \ : (abort (), "")) /* Information about an instruction, including its format, operands and fixups. */ struct mips_cl_insn { /* The opcode's entry in mips_opcodes or mips16_opcodes. */ const struct mips_opcode *insn_mo; /* True if this is a mips16 instruction and if we want the extended form of INSN_MO. */ bfd_boolean use_extend; /* The 16-bit extension instruction to use when USE_EXTEND is true. */ unsigned short extend; /* The 16-bit or 32-bit bitstring of the instruction itself. This is a copy of INSN_MO->match with the operands filled in. */ unsigned long insn_opcode; /* The frag that contains the instruction. */ struct frag *frag; /* The offset into FRAG of the first instruction byte. */ long where; /* The relocs associated with the instruction, if any. */ fixS *fixp[3]; /* True if this entry cannot be moved from its current position. */ unsigned int fixed_p : 1; /* True if this instruction occurred in a .set noreorder block. */ unsigned int noreorder_p : 1; /* True for mips16 instructions that jump to an absolute address. */ unsigned int mips16_absolute_jump_p : 1; }; /* The ABI to use. */ enum mips_abi_level { NO_ABI = 0, O32_ABI, O64_ABI, N32_ABI, N64_ABI, EABI_ABI }; /* MIPS ABI we are using for this output file. */ static enum mips_abi_level mips_abi = NO_ABI; /* Whether or not we have code that can call pic code. */ int mips_abicalls = FALSE; /* Whether or not we have code which can be put into a shared library. */ static bfd_boolean mips_in_shared = TRUE; /* This is the set of options which may be modified by the .set pseudo-op. We use a struct so that .set push and .set pop are more reliable. */ struct mips_set_options { /* MIPS ISA (Instruction Set Architecture) level. This is set to -1 if it has not been initialized. Changed by `.set mipsN', and the -mipsN command line option, and the default CPU. */ int isa; /* Enabled Application Specific Extensions (ASEs). These are set to -1 if they have not been initialized. Changed by `.set ', by command line options, and based on the default architecture. */ int ase_mips3d; int ase_mdmx; int ase_smartmips; int ase_dsp; int ase_dspr2; int ase_mt; /* Whether we are assembling for the mips16 processor. 0 if we are not, 1 if we are, and -1 if the value has not been initialized. Changed by `.set mips16' and `.set nomips16', and the -mips16 and -nomips16 command line options, and the default CPU. */ int mips16; /* Non-zero if we should not reorder instructions. Changed by `.set reorder' and `.set noreorder'. */ int noreorder; /* Non-zero if we should not permit the $at ($1) register to be used in instructions. Changed by `.set at' and `.set noat'. */ int noat; /* Non-zero if we should warn when a macro instruction expands into more than one machine instruction. Changed by `.set nomacro' and `.set macro'. */ int warn_about_macros; /* Non-zero if we should not move instructions. Changed by `.set move', `.set volatile', `.set nomove', and `.set novolatile'. */ int nomove; /* Non-zero if we should not optimize branches by moving the target of the branch into the delay slot. Actually, we don't perform this optimization anyhow. Changed by `.set bopt' and `.set nobopt'. */ int nobopt; /* Non-zero if we should not autoextend mips16 instructions. Changed by `.set autoextend' and `.set noautoextend'. */ int noautoextend; /* Restrict general purpose registers and floating point registers to 32 bit. This is initially determined when -mgp32 or -mfp32 is passed but can changed if the assembler code uses .set mipsN. */ int gp32; int fp32; /* MIPS architecture (CPU) type. Changed by .set arch=FOO, the -march command line option, and the default CPU. */ int arch; /* True if ".set sym32" is in effect. */ bfd_boolean sym32; }; /* True if -mgp32 was passed. */ static int file_mips_gp32 = -1; /* True if -mfp32 was passed. */ static int file_mips_fp32 = -1; /* This is the struct we use to hold the current set of options. Note that we must set the isa field to ISA_UNKNOWN and the ASE fields to -1 to indicate that they have not been initialized. */ static struct mips_set_options mips_opts = { ISA_UNKNOWN, -1, -1, 0, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, CPU_UNKNOWN, FALSE }; /* These variables are filled in with the masks of registers used. The object format code reads them and puts them in the appropriate place. */ unsigned long mips_gprmask; unsigned long mips_cprmask[4]; /* MIPS ISA we are using for this output file. */ static int file_mips_isa = ISA_UNKNOWN; /* True if -mips16 was passed or implied by arguments passed on the command line (e.g., by -march). */ static int file_ase_mips16; #define ISA_SUPPORTS_MIPS16E (mips_opts.isa == ISA_MIPS32 \ || mips_opts.isa == ISA_MIPS32R2 \ || mips_opts.isa == ISA_MIPS64 \ || mips_opts.isa == ISA_MIPS64R2) /* True if -mips3d was passed or implied by arguments passed on the command line (e.g., by -march). */ static int file_ase_mips3d; /* True if -mdmx was passed or implied by arguments passed on the command line (e.g., by -march). */ static int file_ase_mdmx; /* True if -msmartmips was passed or implied by arguments passed on the command line (e.g., by -march). */ static int file_ase_smartmips; #define ISA_SUPPORTS_SMARTMIPS (mips_opts.isa == ISA_MIPS32 \ || mips_opts.isa == ISA_MIPS32R2) /* True if -mdsp was passed or implied by arguments passed on the command line (e.g., by -march). */ static int file_ase_dsp; #define ISA_SUPPORTS_DSP_ASE (mips_opts.isa == ISA_MIPS32R2 \ || mips_opts.isa == ISA_MIPS64R2) #define ISA_SUPPORTS_DSP64_ASE (mips_opts.isa == ISA_MIPS64R2) /* True if -mdspr2 was passed or implied by arguments passed on the command line (e.g., by -march). */ static int file_ase_dspr2; #define ISA_SUPPORTS_DSPR2_ASE (mips_opts.isa == ISA_MIPS32R2 \ || mips_opts.isa == ISA_MIPS64R2) /* True if -mmt was passed or implied by arguments passed on the command line (e.g., by -march). */ static int file_ase_mt; #define ISA_SUPPORTS_MT_ASE (mips_opts.isa == ISA_MIPS32R2 \ || mips_opts.isa == ISA_MIPS64R2) /* The argument of the -march= flag. The architecture we are assembling. */ static int file_mips_arch = CPU_UNKNOWN; static const char *mips_arch_string; /* The argument of the -mtune= flag. The architecture for which we are optimizing. */ static int mips_tune = CPU_UNKNOWN; static const char *mips_tune_string; /* True when generating 32-bit code for a 64-bit processor. */ static int mips_32bitmode = 0; /* True if the given ABI requires 32-bit registers. */ #define ABI_NEEDS_32BIT_REGS(ABI) ((ABI) == O32_ABI) /* Likewise 64-bit registers. */ #define ABI_NEEDS_64BIT_REGS(ABI) \ ((ABI) == N32_ABI \ || (ABI) == N64_ABI \ || (ABI) == O64_ABI) /* Return true if ISA supports 64 bit wide gp registers. */ #define ISA_HAS_64BIT_REGS(ISA) \ ((ISA) == ISA_MIPS3 \ || (ISA) == ISA_MIPS4 \ || (ISA) == ISA_MIPS5 \ || (ISA) == ISA_MIPS64 \ || (ISA) == ISA_MIPS64R2) /* Return true if ISA supports 64 bit wide float registers. */ #define ISA_HAS_64BIT_FPRS(ISA) \ ((ISA) == ISA_MIPS3 \ || (ISA) == ISA_MIPS4 \ || (ISA) == ISA_MIPS5 \ || (ISA) == ISA_MIPS32R2 \ || (ISA) == ISA_MIPS64 \ || (ISA) == ISA_MIPS64R2) /* Return true if ISA supports 64-bit right rotate (dror et al.) instructions. */ #define ISA_HAS_DROR(ISA) \ ((ISA) == ISA_MIPS64R2) /* Return true if ISA supports 32-bit right rotate (ror et al.) instructions. */ #define ISA_HAS_ROR(ISA) \ ((ISA) == ISA_MIPS32R2 \ || (ISA) == ISA_MIPS64R2 \ || mips_opts.ase_smartmips) /* Return true if ISA supports ins instructions. */ #define ISA_HAS_INS(ISA) \ ((ISA) == ISA_MIPS32R2 \ || (ISA) == ISA_MIPS64R2) /* Return true if ISA supports single-precision floats in odd registers. */ #define ISA_HAS_ODD_SINGLE_FPR(ISA) \ ((ISA) == ISA_MIPS32 \ || (ISA) == ISA_MIPS32R2 \ || (ISA) == ISA_MIPS64 \ || (ISA) == ISA_MIPS64R2) /* Return true if ISA supports move to/from high part of a 64-bit floating-point register. */ #define ISA_HAS_MXHC1(ISA) \ ((ISA) == ISA_MIPS32R2 \ || (ISA) == ISA_MIPS64R2) #define HAVE_32BIT_GPRS \ (mips_opts.gp32 || !ISA_HAS_64BIT_REGS (mips_opts.isa)) #define HAVE_32BIT_FPRS \ (mips_opts.fp32 || !ISA_HAS_64BIT_FPRS (mips_opts.isa)) #define HAVE_64BIT_GPRS (!HAVE_32BIT_GPRS) #define HAVE_64BIT_FPRS (!HAVE_32BIT_FPRS) #define HAVE_NEWABI (mips_abi == N32_ABI || mips_abi == N64_ABI) #define HAVE_64BIT_OBJECTS (mips_abi == N64_ABI) /* True if relocations are stored in-place. */ #define HAVE_IN_PLACE_ADDENDS (!HAVE_NEWABI) /* The ABI-derived address size. */ #define HAVE_64BIT_ADDRESSES \ (HAVE_64BIT_GPRS && (mips_abi == EABI_ABI || mips_abi == N64_ABI)) #define HAVE_32BIT_ADDRESSES (!HAVE_64BIT_ADDRESSES) /* The size of symbolic constants (i.e., expressions of the form "SYMBOL" or "SYMBOL + OFFSET"). */ #define HAVE_32BIT_SYMBOLS \ (HAVE_32BIT_ADDRESSES || !HAVE_64BIT_OBJECTS || mips_opts.sym32) #define HAVE_64BIT_SYMBOLS (!HAVE_32BIT_SYMBOLS) /* Addresses are loaded in different ways, depending on the address size in use. The n32 ABI Documentation also mandates the use of additions with overflow checking, but existing implementations don't follow it. */ #define ADDRESS_ADD_INSN \ (HAVE_32BIT_ADDRESSES ? "addu" : "daddu") #define ADDRESS_ADDI_INSN \ (HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu") #define ADDRESS_LOAD_INSN \ (HAVE_32BIT_ADDRESSES ? "lw" : "ld") #define ADDRESS_STORE_INSN \ (HAVE_32BIT_ADDRESSES ? "sw" : "sd") /* Return true if the given CPU supports the MIPS16 ASE. */ #define CPU_HAS_MIPS16(cpu) \ (strncmp (TARGET_CPU, "mips16", sizeof ("mips16") - 1) == 0 \ || strncmp (TARGET_CANONICAL, "mips-lsi-elf", sizeof ("mips-lsi-elf") - 1) == 0) /* True if CPU has a dror instruction. */ #define CPU_HAS_DROR(CPU) ((CPU) == CPU_VR5400 || (CPU) == CPU_VR5500) /* True if CPU has a ror instruction. */ #define CPU_HAS_ROR(CPU) CPU_HAS_DROR (CPU) /* True if mflo and mfhi can be immediately followed by instructions which write to the HI and LO registers. According to MIPS specifications, MIPS ISAs I, II, and III need (at least) two instructions between the reads of HI/LO and instructions which write them, and later ISAs do not. Contradicting the MIPS specifications, some MIPS IV processor user manuals (e.g. the UM for the NEC Vr5000) document needing the instructions between HI/LO reads and writes, as well. Therefore, we declare only MIPS32, MIPS64 and later ISAs to have the interlocks, plus any specific earlier-ISA CPUs for which CPU documentation declares that the instructions are really interlocked. */ #define hilo_interlocks \ (mips_opts.isa == ISA_MIPS32 \ || mips_opts.isa == ISA_MIPS32R2 \ || mips_opts.isa == ISA_MIPS64 \ || mips_opts.isa == ISA_MIPS64R2 \ || mips_opts.arch == CPU_R4010 \ || mips_opts.arch == CPU_R10000 \ || mips_opts.arch == CPU_R12000 \ || mips_opts.arch == CPU_RM7000 \ || mips_opts.arch == CPU_VR5500 \ ) /* Whether the processor uses hardware interlocks to protect reads from the GPRs after they are loaded from memory, and thus does not require nops to be inserted. This applies to instructions marked INSN_LOAD_MEMORY_DELAY. These nops are only required at MIPS ISA level I. */ #define gpr_interlocks \ (mips_opts.isa != ISA_MIPS1 \ || mips_opts.arch == CPU_R3900) /* Whether the processor uses hardware interlocks to avoid delays required by coprocessor instructions, and thus does not require nops to be inserted. This applies to instructions marked INSN_LOAD_COPROC_DELAY, INSN_COPROC_MOVE_DELAY, and to delays between instructions marked INSN_WRITE_COND_CODE and ones marked INSN_READ_COND_CODE. These nops are only required at MIPS ISA levels I, II, and III. */ /* Itbl support may require additional care here. */ #define cop_interlocks \ ((mips_opts.isa != ISA_MIPS1 \ && mips_opts.isa != ISA_MIPS2 \ && mips_opts.isa != ISA_MIPS3) \ || mips_opts.arch == CPU_R4300 \ ) /* Whether the processor uses hardware interlocks to protect reads from coprocessor registers after they are loaded from memory, and thus does not require nops to be inserted. This applies to instructions marked INSN_COPROC_MEMORY_DELAY. These nops are only requires at MIPS ISA level I. */ #define cop_mem_interlocks (mips_opts.isa != ISA_MIPS1) /* Is this a mfhi or mflo instruction? */ #define MF_HILO_INSN(PINFO) \ ((PINFO & INSN_READ_HI) || (PINFO & INSN_READ_LO)) /* MIPS PIC level. */ enum mips_pic_level mips_pic; /* 1 if we should generate 32 bit offsets from the $gp register in SVR4_PIC mode. Currently has no meaning in other modes. */ static int mips_big_got = 0; /* 1 if trap instructions should used for overflow rather than break instructions. */ static int mips_trap = 0; /* 1 if double width floating point constants should not be constructed by assembling two single width halves into two single width floating point registers which just happen to alias the double width destination register. On some architectures this aliasing can be disabled by a bit in the status register, and the setting of this bit cannot be determined automatically at assemble time. */ static int mips_disable_float_construction; /* Non-zero if any .set noreorder directives were used. */ static int mips_any_noreorder; /* Non-zero if nops should be inserted when the register referenced in an mfhi/mflo instruction is read in the next two instructions. */ static int mips_7000_hilo_fix; /* The size of objects in the small data section. */ static unsigned int g_switch_value = 8; /* Whether the -G option was used. */ static int g_switch_seen = 0; #define N_RMASK 0xc4 #define N_VFP 0xd4 /* If we can determine in advance that GP optimization won't be possible, we can skip the relaxation stuff that tries to produce GP-relative references. This makes delay slot optimization work better. This function can only provide a guess, but it seems to work for gcc output. It needs to guess right for gcc, otherwise gcc will put what it thinks is a GP-relative instruction in a branch delay slot. I don't know if a fix is needed for the SVR4_PIC mode. I've only fixed it for the non-PIC mode. KR 95/04/07 */ static int nopic_need_relax (symbolS *, int); /* handle of the OPCODE hash table */ static struct hash_control *op_hash = NULL; /* The opcode hash table we use for the mips16. */ static struct hash_control *mips16_op_hash = NULL; /* This array holds the chars that always start a comment. If the pre-processor is disabled, these aren't very useful */ const char comment_chars[] = "#"; /* This array holds the chars that only start a comment at the beginning of a line. If the line seems to have the form '# 123 filename' .line and .file directives will appear in the pre-processed output */ /* Note that input_file.c hand checks for '#' at the beginning of the first line of the input file. This is because the compiler outputs #NO_APP at the beginning of its output. */ /* Also note that C style comments are always supported. */ const char line_comment_chars[] = "#"; /* This array holds machine specific line separator characters. */ const char line_separator_chars[] = ";"; /* Chars that can be used to separate mant from exp in floating point nums */ const char EXP_CHARS[] = "eE"; /* Chars that mean this number is a floating point constant */ /* As in 0f12.456 */ /* or 0d1.2345e12 */ const char FLT_CHARS[] = "rRsSfFdDxXpP"; /* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be changed in read.c . Ideally it shouldn't have to know about it at all, but nothing is ideal around here. */ static char *insn_error; static int auto_align = 1; /* When outputting SVR4 PIC code, the assembler needs to know the offset in the stack frame from which to restore the $gp register. This is set by the .cprestore pseudo-op, and saved in this variable. */ static offsetT mips_cprestore_offset = -1; /* Similar for NewABI PIC code, where $gp is callee-saved. NewABI has some more optimizations, it can use a register value instead of a memory-saved offset and even an other register than $gp as global pointer. */ static offsetT mips_cpreturn_offset = -1; static int mips_cpreturn_register = -1; static int mips_gp_register = GP; static int mips_gprel_offset = 0; /* Whether mips_cprestore_offset has been set in the current function (or whether it has already been warned about, if not). */ static int mips_cprestore_valid = 0; /* This is the register which holds the stack frame, as set by the .frame pseudo-op. This is needed to implement .cprestore. */ static int mips_frame_reg = SP; /* Whether mips_frame_reg has been set in the current function (or whether it has already been warned about, if not). */ static int mips_frame_reg_valid = 0; /* To output NOP instructions correctly, we need to keep information about the previous two instructions. */ /* Whether we are optimizing. The default value of 2 means to remove unneeded NOPs and swap branch instructions when possible. A value of 1 means to not swap branches. A value of 0 means to always insert NOPs. */ static int mips_optimize = 2; /* Debugging level. -g sets this to 2. -gN sets this to N. -g0 is equivalent to seeing no -g option at all. */ static int mips_debug = 0; /* The maximum number of NOPs needed to avoid the VR4130 mflo/mfhi errata. */ #define MAX_VR4130_NOPS 4 /* The maximum number of NOPs needed to fill delay slots. */ #define MAX_DELAY_NOPS 2 /* The maximum number of NOPs needed for any purpose. */ #define MAX_NOPS 4 /* A list of previous instructions, with index 0 being the most recent. We need to look back MAX_NOPS instructions when filling delay slots or working around processor errata. We need to look back one instruction further if we're thinking about using history[0] to fill a branch delay slot. */ static struct mips_cl_insn history[1 + MAX_NOPS]; /* Nop instructions used by emit_nop. */ static struct mips_cl_insn nop_insn, mips16_nop_insn; /* The appropriate nop for the current mode. */ #define NOP_INSN (mips_opts.mips16 ? &mips16_nop_insn : &nop_insn) /* If this is set, it points to a frag holding nop instructions which were inserted before the start of a noreorder section. If those nops turn out to be unnecessary, the size of the frag can be decreased. */ static fragS *prev_nop_frag; /* The number of nop instructions we created in prev_nop_frag. */ static int prev_nop_frag_holds; /* The number of nop instructions that we know we need in prev_nop_frag. */ static int prev_nop_frag_required; /* The number of instructions we've seen since prev_nop_frag. */ static int prev_nop_frag_since; /* For ECOFF and ELF, relocations against symbols are done in two parts, with a HI relocation and a LO relocation. Each relocation has only 16 bits of space to store an addend. This means that in order for the linker to handle carries correctly, it must be able to locate both the HI and the LO relocation. This means that the relocations must appear in order in the relocation table. In order to implement this, we keep track of each unmatched HI relocation. We then sort them so that they immediately precede the corresponding LO relocation. */ struct mips_hi_fixup { /* Next HI fixup. */ struct mips_hi_fixup *next; /* This fixup. */ fixS *fixp; /* The section this fixup is in. */ segT seg; }; /* The list of unmatched HI relocs. */ static struct mips_hi_fixup *mips_hi_fixup_list; /* The frag containing the last explicit relocation operator. Null if explicit relocations have not been used. */ static fragS *prev_reloc_op_frag; /* Map normal MIPS register numbers to mips16 register numbers. */ #define X ILLEGAL_REG static const int mips32_to_16_reg_map[] = { X, X, 2, 3, 4, 5, 6, 7, X, X, X, X, X, X, X, X, 0, 1, X, X, X, X, X, X, X, X, X, X, X, X, X, X }; #undef X /* Map mips16 register numbers to normal MIPS register numbers. */ static const unsigned int mips16_to_32_reg_map[] = { 16, 17, 2, 3, 4, 5, 6, 7 }; /* Classifies the kind of instructions we're interested in when implementing -mfix-vr4120. */ enum fix_vr4120_class { FIX_VR4120_MACC, FIX_VR4120_DMACC, FIX_VR4120_MULT, FIX_VR4120_DMULT, FIX_VR4120_DIV, FIX_VR4120_MTHILO, NUM_FIX_VR4120_CLASSES }; /* Given two FIX_VR4120_* values X and Y, bit Y of element X is set if there must be at least one other instruction between an instruction of type X and an instruction of type Y. */ static unsigned int vr4120_conflicts[NUM_FIX_VR4120_CLASSES]; /* True if -mfix-vr4120 is in force. */ static int mips_fix_vr4120; /* ...likewise -mfix-vr4130. */ static int mips_fix_vr4130; /* We don't relax branches by default, since this causes us to expand `la .l2 - .l1' if there's a branch between .l1 and .l2, because we fail to compute the offset before expanding the macro to the most efficient expansion. */ static int mips_relax_branch; /* The expansion of many macros depends on the type of symbol that they refer to. For example, when generating position-dependent code, a macro that refers to a symbol may have two different expansions, one which uses GP-relative addresses and one which uses absolute addresses. When generating SVR4-style PIC, a macro may have different expansions for local and global symbols. We handle these situations by generating both sequences and putting them in variant frags. In position-dependent code, the first sequence will be the GP-relative one and the second sequence will be the absolute one. In SVR4 PIC, the first sequence will be for global symbols and the second will be for local symbols. The frag's "subtype" is RELAX_ENCODE (FIRST, SECOND), where FIRST and SECOND are the lengths of the two sequences in bytes. These fields can be extracted using RELAX_FIRST() and RELAX_SECOND(). In addition, the subtype has the following flags: RELAX_USE_SECOND Set if it has been decided that we should use the second sequence instead of the first. RELAX_SECOND_LONGER Set in the first variant frag if the macro's second implementation is longer than its first. This refers to the macro as a whole, not an individual relaxation. RELAX_NOMACRO Set in the first variant frag if the macro appeared in a .set nomacro block and if one alternative requires a warning but the other does not. RELAX_DELAY_SLOT Like RELAX_NOMACRO, but indicates that the macro appears in a branch delay slot. The frag's "opcode" points to the first fixup for relaxable code. Relaxable macros are generated using a sequence such as: relax_start (SYMBOL); ... generate first expansion ... relax_switch (); ... generate second expansion ... relax_end (); The code and fixups for the unwanted alternative are discarded by md_convert_frag. */ #define RELAX_ENCODE(FIRST, SECOND) (((FIRST) << 8) | (SECOND)) #define RELAX_FIRST(X) (((X) >> 8) & 0xff) #define RELAX_SECOND(X) ((X) & 0xff) #define RELAX_USE_SECOND 0x10000 #define RELAX_SECOND_LONGER 0x20000 #define RELAX_NOMACRO 0x40000 #define RELAX_DELAY_SLOT 0x80000 /* Branch without likely bit. If label is out of range, we turn: beq reg1, reg2, label delay slot into bne reg1, reg2, 0f nop j label 0: delay slot with the following opcode replacements: beq <-> bne blez <-> bgtz bltz <-> bgez bc1f <-> bc1t bltzal <-> bgezal (with jal label instead of j label) Even though keeping the delay slot instruction in the delay slot of the branch would be more efficient, it would be very tricky to do correctly, because we'd have to introduce a variable frag *after* the delay slot instruction, and expand that instead. Let's do it the easy way for now, even if the branch-not-taken case now costs one additional instruction. Out-of-range branches are not supposed to be common, anyway. Branch likely. If label is out of range, we turn: beql reg1, reg2, label delay slot (annulled if branch not taken) into beql reg1, reg2, 1f nop beql $0, $0, 2f nop 1: j[al] label delay slot (executed only if branch taken) 2: It would be possible to generate a shorter sequence by losing the likely bit, generating something like: bne reg1, reg2, 0f nop j[al] label delay slot (executed only if branch taken) 0: beql -> bne bnel -> beq blezl -> bgtz bgtzl -> blez bltzl -> bgez bgezl -> bltz bc1fl -> bc1t bc1tl -> bc1f bltzall -> bgezal (with jal label instead of j label) bgezall -> bltzal (ditto) but it's not clear that it would actually improve performance. */ #define RELAX_BRANCH_ENCODE(uncond, likely, link, toofar) \ ((relax_substateT) \ (0xc0000000 \ | ((toofar) ? 1 : 0) \ | ((link) ? 2 : 0) \ | ((likely) ? 4 : 0) \ | ((uncond) ? 8 : 0))) #define RELAX_BRANCH_P(i) (((i) & 0xf0000000) == 0xc0000000) #define RELAX_BRANCH_UNCOND(i) (((i) & 8) != 0) #define RELAX_BRANCH_LIKELY(i) (((i) & 4) != 0) #define RELAX_BRANCH_LINK(i) (((i) & 2) != 0) #define RELAX_BRANCH_TOOFAR(i) (((i) & 1) != 0) /* For mips16 code, we use an entirely different form of relaxation. mips16 supports two versions of most instructions which take immediate values: a small one which takes some small value, and a larger one which takes a 16 bit value. Since branches also follow this pattern, relaxing these values is required. We can assemble both mips16 and normal MIPS code in a single object. Therefore, we need to support this type of relaxation at the same time that we support the relaxation described above. We use the high bit of the subtype field to distinguish these cases. The information we store for this type of relaxation is the argument code found in the opcode file for this relocation, whether the user explicitly requested a small or extended form, and whether the relocation is in a jump or jal delay slot. That tells us the size of the value, and how it should be stored. We also store whether the fragment is considered to be extended or not. We also store whether this is known to be a branch to a different section, whether we have tried to relax this frag yet, and whether we have ever extended a PC relative fragment because of a shift count. */ #define RELAX_MIPS16_ENCODE(type, small, ext, dslot, jal_dslot) \ (0x80000000 \ | ((type) & 0xff) \ | ((small) ? 0x100 : 0) \ | ((ext) ? 0x200 : 0) \ | ((dslot) ? 0x400 : 0) \ | ((jal_dslot) ? 0x800 : 0)) #define RELAX_MIPS16_P(i) (((i) & 0xc0000000) == 0x80000000) #define RELAX_MIPS16_TYPE(i) ((i) & 0xff) #define RELAX_MIPS16_USER_SMALL(i) (((i) & 0x100) != 0) #define RELAX_MIPS16_USER_EXT(i) (((i) & 0x200) != 0) #define RELAX_MIPS16_DSLOT(i) (((i) & 0x400) != 0) #define RELAX_MIPS16_JAL_DSLOT(i) (((i) & 0x800) != 0) #define RELAX_MIPS16_EXTENDED(i) (((i) & 0x1000) != 0) #define RELAX_MIPS16_MARK_EXTENDED(i) ((i) | 0x1000) #define RELAX_MIPS16_CLEAR_EXTENDED(i) ((i) &~ 0x1000) #define RELAX_MIPS16_LONG_BRANCH(i) (((i) & 0x2000) != 0) #define RELAX_MIPS16_MARK_LONG_BRANCH(i) ((i) | 0x2000) #define RELAX_MIPS16_CLEAR_LONG_BRANCH(i) ((i) &~ 0x2000) /* Is the given value a sign-extended 32-bit value? */ #define IS_SEXT_32BIT_NUM(x) \ (((x) &~ (offsetT) 0x7fffffff) == 0 \ || (((x) &~ (offsetT) 0x7fffffff) == ~ (offsetT) 0x7fffffff)) /* Is the given value a sign-extended 16-bit value? */ #define IS_SEXT_16BIT_NUM(x) \ (((x) &~ (offsetT) 0x7fff) == 0 \ || (((x) &~ (offsetT) 0x7fff) == ~ (offsetT) 0x7fff)) /* Is the given value a zero-extended 32-bit value? Or a negated one? */ #define IS_ZEXT_32BIT_NUM(x) \ (((x) &~ (offsetT) 0xffffffff) == 0 \ || (((x) &~ (offsetT) 0xffffffff) == ~ (offsetT) 0xffffffff)) /* Replace bits MASK << SHIFT of STRUCT with the equivalent bits in VALUE << SHIFT. VALUE is evaluated exactly once. */ #define INSERT_BITS(STRUCT, VALUE, MASK, SHIFT) \ (STRUCT) = (((STRUCT) & ~((MASK) << (SHIFT))) \ | (((VALUE) & (MASK)) << (SHIFT))) /* Extract bits MASK << SHIFT from STRUCT and shift them right SHIFT places. */ #define EXTRACT_BITS(STRUCT, MASK, SHIFT) \ (((STRUCT) >> (SHIFT)) & (MASK)) /* Change INSN's opcode so that the operand given by FIELD has value VALUE. INSN is a mips_cl_insn structure and VALUE is evaluated exactly once. include/opcode/mips.h specifies operand fields using the macros OP_MASK_ and OP_SH_. The MIPS16 equivalents start with "MIPS16OP" instead of "OP". */ #define INSERT_OPERAND(FIELD, INSN, VALUE) \ INSERT_BITS ((INSN).insn_opcode, VALUE, OP_MASK_##FIELD, OP_SH_##FIELD) #define MIPS16_INSERT_OPERAND(FIELD, INSN, VALUE) \ INSERT_BITS ((INSN).insn_opcode, VALUE, \ MIPS16OP_MASK_##FIELD, MIPS16OP_SH_##FIELD) /* Extract the operand given by FIELD from mips_cl_insn INSN. */ #define EXTRACT_OPERAND(FIELD, INSN) \ EXTRACT_BITS ((INSN).insn_opcode, OP_MASK_##FIELD, OP_SH_##FIELD) #define MIPS16_EXTRACT_OPERAND(FIELD, INSN) \ EXTRACT_BITS ((INSN).insn_opcode, \ MIPS16OP_MASK_##FIELD, \ MIPS16OP_SH_##FIELD) /* Global variables used when generating relaxable macros. See the comment above RELAX_ENCODE for more details about how relaxation is used. */ static struct { /* 0 if we're not emitting a relaxable macro. 1 if we're emitting the first of the two relaxation alternatives. 2 if we're emitting the second alternative. */ int sequence; /* The first relaxable fixup in the current frag. (In other words, the first fixup that refers to relaxable code.) */ fixS *first_fixup; /* sizes[0] says how many bytes of the first alternative are stored in the current frag. Likewise sizes[1] for the second alternative. */ unsigned int sizes[2]; /* The symbol on which the choice of sequence depends. */ symbolS *symbol; } mips_relax; /* Global variables used to decide whether a macro needs a warning. */ static struct { /* True if the macro is in a branch delay slot. */ bfd_boolean delay_slot_p; /* For relaxable macros, sizes[0] is the length of the first alternative in bytes and sizes[1] is the length of the second alternative. For non-relaxable macros, both elements give the length of the macro in bytes. */ unsigned int sizes[2]; /* The first variant frag for this macro. */ fragS *first_frag; } mips_macro_warning; /* Prototypes for static functions. */ #define internalError() \ as_fatal (_("internal Error, line %d, %s"), __LINE__, __FILE__) enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG }; static void append_insn (struct mips_cl_insn *ip, expressionS *p, bfd_reloc_code_real_type *r); static void mips_no_prev_insn (void); static void mips16_macro_build (expressionS *, const char *, const char *, va_list); static void load_register (int, expressionS *, int); static void macro_start (void); static void macro_end (void); static void macro (struct mips_cl_insn * ip); static void mips16_macro (struct mips_cl_insn * ip); #ifdef LOSING_COMPILER static void macro2 (struct mips_cl_insn * ip); #endif static void mips_ip (char *str, struct mips_cl_insn * ip); static void mips16_ip (char *str, struct mips_cl_insn * ip); static void mips16_immed (char *, unsigned int, int, offsetT, bfd_boolean, bfd_boolean, bfd_boolean, unsigned long *, bfd_boolean *, unsigned short *); static size_t my_getSmallExpression (expressionS *, bfd_reloc_code_real_type *, char *); static void my_getExpression (expressionS *, char *); static void s_align (int); static void s_change_sec (int); static void s_change_section (int); static void s_cons (int); static void s_float_cons (int); static void s_mips_globl (int); static void s_option (int); static void s_mipsset (int); static void s_abicalls (int); static void s_cpload (int); static void s_cpsetup (int); static void s_cplocal (int); static void s_cprestore (int); static void s_cpreturn (int); static void s_dtprelword (int); static void s_dtpreldword (int); static void s_gpvalue (int); static void s_gpword (int); static void s_gpdword (int); static void s_cpadd (int); static void s_insn (int); static void md_obj_begin (void); static void md_obj_end (void); static void s_mips_ent (int); static void s_mips_end (int); static void s_mips_frame (int); static void s_mips_mask (int reg_type); static void s_mips_stab (int); static void s_mips_weakext (int); static void s_mips_file (int); static void s_mips_loc (int); static bfd_boolean pic_need_relax (symbolS *, asection *); static int relaxed_branch_length (fragS *, asection *, int); static int validate_mips_insn (const struct mips_opcode *); /* Table and functions used to map between CPU/ISA names, and ISA levels, and CPU numbers. */ struct mips_cpu_info { const char *name; /* CPU or ISA name. */ int flags; /* ASEs available, or ISA flag. */ int isa; /* ISA level. */ int cpu; /* CPU number (default CPU if ISA). */ }; #define MIPS_CPU_IS_ISA 0x0001 /* Is this an ISA? (If 0, a CPU.) */ #define MIPS_CPU_ASE_SMARTMIPS 0x0002 /* CPU implements SmartMIPS ASE */ #define MIPS_CPU_ASE_DSP 0x0004 /* CPU implements DSP ASE */ #define MIPS_CPU_ASE_MT 0x0008 /* CPU implements MT ASE */ #define MIPS_CPU_ASE_MIPS3D 0x0010 /* CPU implements MIPS-3D ASE */ #define MIPS_CPU_ASE_MDMX 0x0020 /* CPU implements MDMX ASE */ #define MIPS_CPU_ASE_DSPR2 0x0040 /* CPU implements DSP R2 ASE */ static const struct mips_cpu_info *mips_parse_cpu (const char *, const char *); static const struct mips_cpu_info *mips_cpu_info_from_isa (int); static const struct mips_cpu_info *mips_cpu_info_from_arch (int); /* Pseudo-op table. The following pseudo-ops from the Kane and Heinrich MIPS book should be defined here, but are currently unsupported: .alias, .galive, .gjaldef, .gjrlive, .livereg, .noalias. The following pseudo-ops from the Kane and Heinrich MIPS book are specific to the type of debugging information being generated, and should be defined by the object format: .aent, .begin, .bend, .bgnb, .end, .endb, .ent, .fmask, .frame, .loc, .mask, .verstamp, .vreg. The following pseudo-ops from the Kane and Heinrich MIPS book are not MIPS CPU specific, but are also not specific to the object file format. This file is probably the best place to define them, but they are not currently supported: .asm0, .endr, .lab, .struct. */ static const pseudo_typeS mips_pseudo_table[] = { /* MIPS specific pseudo-ops. */ {"option", s_option, 0}, {"set", s_mipsset, 0}, {"rdata", s_change_sec, 'r'}, {"sdata", s_change_sec, 's'}, {"livereg", s_ignore, 0}, {"abicalls", s_abicalls, 0}, {"cpload", s_cpload, 0}, {"cpsetup", s_cpsetup, 0}, {"cplocal", s_cplocal, 0}, {"cprestore", s_cprestore, 0}, {"cpreturn", s_cpreturn, 0}, {"dtprelword", s_dtprelword, 0}, {"dtpreldword", s_dtpreldword, 0}, {"gpvalue", s_gpvalue, 0}, {"gpword", s_gpword, 0}, {"gpdword", s_gpdword, 0}, {"cpadd", s_cpadd, 0}, {"insn", s_insn, 0}, /* Relatively generic pseudo-ops that happen to be used on MIPS chips. */ {"asciiz", stringer, 1}, {"bss", s_change_sec, 'b'}, {"err", s_err, 0}, {"half", s_cons, 1}, {"dword", s_cons, 3}, {"weakext", s_mips_weakext, 0}, {"origin", s_org, 0}, {"repeat", s_rept, 0}, /* These pseudo-ops are defined in read.c, but must be overridden here for one reason or another. */ {"align", s_align, 0}, {"byte", s_cons, 0}, {"data", s_change_sec, 'd'}, {"double", s_float_cons, 'd'}, {"float", s_float_cons, 'f'}, {"globl", s_mips_globl, 0}, {"global", s_mips_globl, 0}, {"hword", s_cons, 1}, {"int", s_cons, 2}, {"long", s_cons, 2}, {"octa", s_cons, 4}, {"quad", s_cons, 3}, {"section", s_change_section, 0}, {"short", s_cons, 1}, {"single", s_float_cons, 'f'}, {"stabn", s_mips_stab, 'n'}, {"text", s_change_sec, 't'}, {"word", s_cons, 2}, { "extern", ecoff_directive_extern, 0}, { NULL, NULL, 0 }, }; static const pseudo_typeS mips_nonecoff_pseudo_table[] = { /* These pseudo-ops should be defined by the object file format. However, a.out doesn't support them, so we have versions here. */ {"aent", s_mips_ent, 1}, {"bgnb", s_ignore, 0}, {"end", s_mips_end, 0}, {"endb", s_ignore, 0}, {"ent", s_mips_ent, 0}, {"file", s_mips_file, 0}, {"fmask", s_mips_mask, 'F'}, {"frame", s_mips_frame, 0}, {"loc", s_mips_loc, 0}, {"mask", s_mips_mask, 'R'}, {"verstamp", s_ignore, 0}, { NULL, NULL, 0 }, }; extern void pop_insert (const pseudo_typeS *); void mips_pop_insert (void) { pop_insert (mips_pseudo_table); if (! ECOFF_DEBUGGING) pop_insert (mips_nonecoff_pseudo_table); } /* Symbols labelling the current insn. */ struct insn_label_list { struct insn_label_list *next; symbolS *label; }; static struct insn_label_list *free_insn_labels; #define label_list tc_segment_info_data static void mips_clear_insn_labels (void); static inline void mips_clear_insn_labels (void) { register struct insn_label_list **pl; segment_info_type *si; if (now_seg) { for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next) ; si = seg_info (now_seg); *pl = si->label_list; si->label_list = NULL; } } static char *expr_end; /* Expressions which appear in instructions. These are set by mips_ip. */ static expressionS imm_expr; static expressionS imm2_expr; static expressionS offset_expr; /* Relocs associated with imm_expr and offset_expr. */ static bfd_reloc_code_real_type imm_reloc[3] = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED}; static bfd_reloc_code_real_type offset_reloc[3] = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED}; /* These are set by mips16_ip if an explicit extension is used. */ static bfd_boolean mips16_small, mips16_ext; #ifdef OBJ_ELF /* The pdr segment for per procedure frame/regmask info. Not used for ECOFF debugging. */ static segT pdr_seg; #endif /* The default target format to use. */ const char * mips_target_format (void) { switch (OUTPUT_FLAVOR) { case bfd_target_ecoff_flavour: return target_big_endian ? "ecoff-bigmips" : ECOFF_LITTLE_FORMAT; case bfd_target_coff_flavour: return "pe-mips"; case bfd_target_elf_flavour: #ifdef TE_VXWORKS if (!HAVE_64BIT_OBJECTS && !HAVE_NEWABI) return (target_big_endian ? "elf32-bigmips-vxworks" : "elf32-littlemips-vxworks"); #endif #ifdef TE_TMIPS /* This is traditional mips. */ return (target_big_endian ? (HAVE_64BIT_OBJECTS ? "elf64-tradbigmips" : (HAVE_NEWABI ? "elf32-ntradbigmips" : "elf32-tradbigmips")) : (HAVE_64BIT_OBJECTS ? "elf64-tradlittlemips" : (HAVE_NEWABI ? "elf32-ntradlittlemips" : "elf32-tradlittlemips"))); #else return (target_big_endian ? (HAVE_64BIT_OBJECTS ? "elf64-bigmips" : (HAVE_NEWABI ? "elf32-nbigmips" : "elf32-bigmips")) : (HAVE_64BIT_OBJECTS ? "elf64-littlemips" : (HAVE_NEWABI ? "elf32-nlittlemips" : "elf32-littlemips"))); #endif default: abort (); return NULL; } } /* Return the length of instruction INSN. */ static inline unsigned int insn_length (const struct mips_cl_insn *insn) { if (!mips_opts.mips16) return 4; return insn->mips16_absolute_jump_p || insn->use_extend ? 4 : 2; } /* Initialise INSN from opcode entry MO. Leave its position unspecified. */ static void create_insn (struct mips_cl_insn *insn, const struct mips_opcode *mo) { size_t i; insn->insn_mo = mo; insn->use_extend = FALSE; insn->extend = 0; insn->insn_opcode = mo->match; insn->frag = NULL; insn->where = 0; for (i = 0; i < ARRAY_SIZE (insn->fixp); i++) insn->fixp[i] = NULL; insn->fixed_p = (mips_opts.noreorder > 0); insn->noreorder_p = (mips_opts.noreorder > 0); insn->mips16_absolute_jump_p = 0; } /* Install INSN at the location specified by its "frag" and "where" fields. */ static void install_insn (const struct mips_cl_insn *insn) { char *f = insn->frag->fr_literal + insn->where; if (!mips_opts.mips16) md_number_to_chars (f, insn->insn_opcode, 4); else if (insn->mips16_absolute_jump_p) { md_number_to_chars (f, insn->insn_opcode >> 16, 2); md_number_to_chars (f + 2, insn->insn_opcode & 0xffff, 2); } else { if (insn->use_extend) { md_number_to_chars (f, 0xf000 | insn->extend, 2); f += 2; } md_number_to_chars (f, insn->insn_opcode, 2); } } /* Move INSN to offset WHERE in FRAG. Adjust the fixups accordingly and install the opcode in the new location. */ static void move_insn (struct mips_cl_insn *insn, fragS *frag, long where) { size_t i; insn->frag = frag; insn->where = where; for (i = 0; i < ARRAY_SIZE (insn->fixp); i++) if (insn->fixp[i] != NULL) { insn->fixp[i]->fx_frag = frag; insn->fixp[i]->fx_where = where; } install_insn (insn); } /* Add INSN to the end of the output. */ static void add_fixed_insn (struct mips_cl_insn *insn) { char *f = frag_more (insn_length (insn)); move_insn (insn, frag_now, f - frag_now->fr_literal); } /* Start a variant frag and move INSN to the start of the variant part, marking it as fixed. The other arguments are as for frag_var. */ static void add_relaxed_insn (struct mips_cl_insn *insn, int max_chars, int var, relax_substateT subtype, symbolS *symbol, offsetT offset) { frag_grow (max_chars); move_insn (insn, frag_now, frag_more (0) - frag_now->fr_literal); insn->fixed_p = 1; frag_var (rs_machine_dependent, max_chars, var, subtype, symbol, offset, NULL); } /* Insert N copies of INSN into the history buffer, starting at position FIRST. Neither FIRST nor N need to be clipped. */ static void insert_into_history (unsigned int first, unsigned int n, const struct mips_cl_insn *insn) { if (mips_relax.sequence != 2) { unsigned int i; for (i = ARRAY_SIZE (history); i-- > first;) if (i >= first + n) history[i] = history[i - n]; else history[i] = *insn; } } /* Emit a nop instruction, recording it in the history buffer. */ static void emit_nop (void) { add_fixed_insn (NOP_INSN); insert_into_history (0, 1, NOP_INSN); } /* Initialize vr4120_conflicts. There is a bit of duplication here: the idea is to make it obvious at a glance that each errata is included. */ static void init_vr4120_conflicts (void) { #define CONFLICT(FIRST, SECOND) \ vr4120_conflicts[FIX_VR4120_##FIRST] |= 1 << FIX_VR4120_##SECOND /* Errata 21 - [D]DIV[U] after [D]MACC */ CONFLICT (MACC, DIV); CONFLICT (DMACC, DIV); /* Errata 23 - Continuous DMULT[U]/DMACC instructions. */ CONFLICT (DMULT, DMULT); CONFLICT (DMULT, DMACC); CONFLICT (DMACC, DMULT); CONFLICT (DMACC, DMACC); /* Errata 24 - MT{LO,HI} after [D]MACC */ CONFLICT (MACC, MTHILO); CONFLICT (DMACC, MTHILO); /* VR4181A errata MD(1): "If a MULT, MULTU, DMULT or DMULTU instruction is executed immediately after a MACC or DMACC instruction, the result of [either instruction] is incorrect." */ CONFLICT (MACC, MULT); CONFLICT (MACC, DMULT); CONFLICT (DMACC, MULT); CONFLICT (DMACC, DMULT); /* VR4181A errata MD(4): "If a MACC or DMACC instruction is executed immediately after a DMULT, DMULTU, DIV, DIVU, DDIV or DDIVU instruction, the result of the MACC or DMACC instruction is incorrect.". */ CONFLICT (DMULT, MACC); CONFLICT (DMULT, DMACC); CONFLICT (DIV, MACC); CONFLICT (DIV, DMACC); #undef CONFLICT } struct regname { const char *name; unsigned int num; }; #define RTYPE_MASK 0x1ff00 #define RTYPE_NUM 0x00100 #define RTYPE_FPU 0x00200 #define RTYPE_FCC 0x00400 #define RTYPE_VEC 0x00800 #define RTYPE_GP 0x01000 #define RTYPE_CP0 0x02000 #define RTYPE_PC 0x04000 #define RTYPE_ACC 0x08000 #define RTYPE_CCC 0x10000 #define RNUM_MASK 0x000ff #define RWARN 0x80000 #define GENERIC_REGISTER_NUMBERS \ {"$0", RTYPE_NUM | 0}, \ {"$1", RTYPE_NUM | 1}, \ {"$2", RTYPE_NUM | 2}, \ {"$3", RTYPE_NUM | 3}, \ {"$4", RTYPE_NUM | 4}, \ {"$5", RTYPE_NUM | 5}, \ {"$6", RTYPE_NUM | 6}, \ {"$7", RTYPE_NUM | 7}, \ {"$8", RTYPE_NUM | 8}, \ {"$9", RTYPE_NUM | 9}, \ {"$10", RTYPE_NUM | 10}, \ {"$11", RTYPE_NUM | 11}, \ {"$12", RTYPE_NUM | 12}, \ {"$13", RTYPE_NUM | 13}, \ {"$14", RTYPE_NUM | 14}, \ {"$15", RTYPE_NUM | 15}, \ {"$16", RTYPE_NUM | 16}, \ {"$17", RTYPE_NUM | 17}, \ {"$18", RTYPE_NUM | 18}, \ {"$19", RTYPE_NUM | 19}, \ {"$20", RTYPE_NUM | 20}, \ {"$21", RTYPE_NUM | 21}, \ {"$22", RTYPE_NUM | 22}, \ {"$23", RTYPE_NUM | 23}, \ {"$24", RTYPE_NUM | 24}, \ {"$25", RTYPE_NUM | 25}, \ {"$26", RTYPE_NUM | 26}, \ {"$27", RTYPE_NUM | 27}, \ {"$28", RTYPE_NUM | 28}, \ {"$29", RTYPE_NUM | 29}, \ {"$30", RTYPE_NUM | 30}, \ {"$31", RTYPE_NUM | 31} #define FPU_REGISTER_NAMES \ {"$f0", RTYPE_FPU | 0}, \ {"$f1", RTYPE_FPU | 1}, \ {"$f2", RTYPE_FPU | 2}, \ {"$f3", RTYPE_FPU | 3}, \ {"$f4", RTYPE_FPU | 4}, \ {"$f5", RTYPE_FPU | 5}, \ {"$f6", RTYPE_FPU | 6}, \ {"$f7", RTYPE_FPU | 7}, \ {"$f8", RTYPE_FPU | 8}, \ {"$f9", RTYPE_FPU | 9}, \ {"$f10", RTYPE_FPU | 10}, \ {"$f11", RTYPE_FPU | 11}, \ {"$f12", RTYPE_FPU | 12}, \ {"$f13", RTYPE_FPU | 13}, \ {"$f14", RTYPE_FPU | 14}, \ {"$f15", RTYPE_FPU | 15}, \ {"$f16", RTYPE_FPU | 16}, \ {"$f17", RTYPE_FPU | 17}, \ {"$f18", RTYPE_FPU | 18}, \ {"$f19", RTYPE_FPU | 19}, \ {"$f20", RTYPE_FPU | 20}, \ {"$f21", RTYPE_FPU | 21}, \ {"$f22", RTYPE_FPU | 22}, \ {"$f23", RTYPE_FPU | 23}, \ {"$f24", RTYPE_FPU | 24}, \ {"$f25", RTYPE_FPU | 25}, \ {"$f26", RTYPE_FPU | 26}, \ {"$f27", RTYPE_FPU | 27}, \ {"$f28", RTYPE_FPU | 28}, \ {"$f29", RTYPE_FPU | 29}, \ {"$f30", RTYPE_FPU | 30}, \ {"$f31", RTYPE_FPU | 31} #define FPU_CONDITION_CODE_NAMES \ {"$fcc0", RTYPE_FCC | 0}, \ {"$fcc1", RTYPE_FCC | 1}, \ {"$fcc2", RTYPE_FCC | 2}, \ {"$fcc3", RTYPE_FCC | 3}, \ {"$fcc4", RTYPE_FCC | 4}, \ {"$fcc5", RTYPE_FCC | 5}, \ {"$fcc6", RTYPE_FCC | 6}, \ {"$fcc7", RTYPE_FCC | 7} #define COPROC_CONDITION_CODE_NAMES \ {"$cc0", RTYPE_FCC | RTYPE_CCC | 0}, \ {"$cc1", RTYPE_FCC | RTYPE_CCC | 1}, \ {"$cc2", RTYPE_FCC | RTYPE_CCC | 2}, \ {"$cc3", RTYPE_FCC | RTYPE_CCC | 3}, \ {"$cc4", RTYPE_FCC | RTYPE_CCC | 4}, \ {"$cc5", RTYPE_FCC | RTYPE_CCC | 5}, \ {"$cc6", RTYPE_FCC | RTYPE_CCC | 6}, \ {"$cc7", RTYPE_FCC | RTYPE_CCC | 7} #define N32N64_SYMBOLIC_REGISTER_NAMES \ {"$a4", RTYPE_GP | 8}, \ {"$a5", RTYPE_GP | 9}, \ {"$a6", RTYPE_GP | 10}, \ {"$a7", RTYPE_GP | 11}, \ {"$ta0", RTYPE_GP | 8}, /* alias for $a4 */ \ {"$ta1", RTYPE_GP | 9}, /* alias for $a5 */ \ {"$ta2", RTYPE_GP | 10}, /* alias for $a6 */ \ {"$ta3", RTYPE_GP | 11}, /* alias for $a7 */ \ {"$t0", RTYPE_GP | 12}, \ {"$t1", RTYPE_GP | 13}, \ {"$t2", RTYPE_GP | 14}, \ {"$t3", RTYPE_GP | 15} #define O32_SYMBOLIC_REGISTER_NAMES \ {"$t0", RTYPE_GP | 8}, \ {"$t1", RTYPE_GP | 9}, \ {"$t2", RTYPE_GP | 10}, \ {"$t3", RTYPE_GP | 11}, \ {"$t4", RTYPE_GP | 12}, \ {"$t5", RTYPE_GP | 13}, \ {"$t6", RTYPE_GP | 14}, \ {"$t7", RTYPE_GP | 15}, \ {"$ta0", RTYPE_GP | 12}, /* alias for $t4 */ \ {"$ta1", RTYPE_GP | 13}, /* alias for $t5 */ \ {"$ta2", RTYPE_GP | 14}, /* alias for $t6 */ \ {"$ta3", RTYPE_GP | 15} /* alias for $t7 */ /* Remaining symbolic register names */ #define SYMBOLIC_REGISTER_NAMES \ {"$zero", RTYPE_GP | 0}, \ {"$at", RTYPE_GP | 1}, \ {"$AT", RTYPE_GP | 1}, \ {"$v0", RTYPE_GP | 2}, \ {"$v1", RTYPE_GP | 3}, \ {"$a0", RTYPE_GP | 4}, \ {"$a1", RTYPE_GP | 5}, \ {"$a2", RTYPE_GP | 6}, \ {"$a3", RTYPE_GP | 7}, \ {"$s0", RTYPE_GP | 16}, \ {"$s1", RTYPE_GP | 17}, \ {"$s2", RTYPE_GP | 18}, \ {"$s3", RTYPE_GP | 19}, \ {"$s4", RTYPE_GP | 20}, \ {"$s5", RTYPE_GP | 21}, \ {"$s6", RTYPE_GP | 22}, \ {"$s7", RTYPE_GP | 23}, \ {"$t8", RTYPE_GP | 24}, \ {"$t9", RTYPE_GP | 25}, \ {"$k0", RTYPE_GP | 26}, \ {"$kt0", RTYPE_GP | 26}, \ {"$k1", RTYPE_GP | 27}, \ {"$kt1", RTYPE_GP | 27}, \ {"$gp", RTYPE_GP | 28}, \ {"$sp", RTYPE_GP | 29}, \ {"$s8", RTYPE_GP | 30}, \ {"$fp", RTYPE_GP | 30}, \ {"$ra", RTYPE_GP | 31} #define MIPS16_SPECIAL_REGISTER_NAMES \ {"$pc", RTYPE_PC | 0} #define MDMX_VECTOR_REGISTER_NAMES \ /* {"$v0", RTYPE_VEC | 0}, clash with REG 2 above */ \ /* {"$v1", RTYPE_VEC | 1}, clash with REG 3 above */ \ {"$v2", RTYPE_VEC | 2}, \ {"$v3", RTYPE_VEC | 3}, \ {"$v4", RTYPE_VEC | 4}, \ {"$v5", RTYPE_VEC | 5}, \ {"$v6", RTYPE_VEC | 6}, \ {"$v7", RTYPE_VEC | 7}, \ {"$v8", RTYPE_VEC | 8}, \ {"$v9", RTYPE_VEC | 9}, \ {"$v10", RTYPE_VEC | 10}, \ {"$v11", RTYPE_VEC | 11}, \ {"$v12", RTYPE_VEC | 12}, \ {"$v13", RTYPE_VEC | 13}, \ {"$v14", RTYPE_VEC | 14}, \ {"$v15", RTYPE_VEC | 15}, \ {"$v16", RTYPE_VEC | 16}, \ {"$v17", RTYPE_VEC | 17}, \ {"$v18", RTYPE_VEC | 18}, \ {"$v19", RTYPE_VEC | 19}, \ {"$v20", RTYPE_VEC | 20}, \ {"$v21", RTYPE_VEC | 21}, \ {"$v22", RTYPE_VEC | 22}, \ {"$v23", RTYPE_VEC | 23}, \ {"$v24", RTYPE_VEC | 24}, \ {"$v25", RTYPE_VEC | 25}, \ {"$v26", RTYPE_VEC | 26}, \ {"$v27", RTYPE_VEC | 27}, \ {"$v28", RTYPE_VEC | 28}, \ {"$v29", RTYPE_VEC | 29}, \ {"$v30", RTYPE_VEC | 30}, \ {"$v31", RTYPE_VEC | 31} #define MIPS_DSP_ACCUMULATOR_NAMES \ {"$ac0", RTYPE_ACC | 0}, \ {"$ac1", RTYPE_ACC | 1}, \ {"$ac2", RTYPE_ACC | 2}, \ {"$ac3", RTYPE_ACC | 3} static const struct regname reg_names[] = { GENERIC_REGISTER_NUMBERS, FPU_REGISTER_NAMES, FPU_CONDITION_CODE_NAMES, COPROC_CONDITION_CODE_NAMES, /* The $txx registers depends on the abi, these will be added later into the symbol table from one of the tables below once mips_abi is set after parsing of arguments from the command line. */ SYMBOLIC_REGISTER_NAMES, MIPS16_SPECIAL_REGISTER_NAMES, MDMX_VECTOR_REGISTER_NAMES, MIPS_DSP_ACCUMULATOR_NAMES, {0, 0} }; static const struct regname reg_names_o32[] = { O32_SYMBOLIC_REGISTER_NAMES, {0, 0} }; static const struct regname reg_names_n32n64[] = { N32N64_SYMBOLIC_REGISTER_NAMES, {0, 0} }; static int reg_lookup (char **s, unsigned int types, unsigned int *regnop) { symbolS *symbolP; char *e; char save_c; int reg = -1; /* Find end of name. */ e = *s; if (is_name_beginner (*e)) ++e; while (is_part_of_name (*e)) ++e; /* Terminate name. */ save_c = *e; *e = '\0'; /* Look for a register symbol. */ if ((symbolP = symbol_find (*s)) && S_GET_SEGMENT (symbolP) == reg_section) { int r = S_GET_VALUE (symbolP); if (r & types) reg = r & RNUM_MASK; else if ((types & RTYPE_VEC) && (r & ~1) == (RTYPE_GP | 2)) /* Convert GP reg $v0/1 to MDMX reg $v0/1! */ reg = (r & RNUM_MASK) - 2; } /* Else see if this is a register defined in an itbl entry. */ else if ((types & RTYPE_GP) && itbl_have_entries) { char *n = *s; unsigned long r; if (*n == '$') ++n; if (itbl_get_reg_val (n, &r)) reg = r & RNUM_MASK; } /* Advance to next token if a register was recognised. */ if (reg >= 0) *s = e; else if (types & RWARN) as_warn ("Unrecognized register name `%s'", *s); *e = save_c; if (regnop) *regnop = reg; return reg >= 0; } /* This function is called once, at assembler startup time. It should set up all the tables, etc. that the MD part of the assembler will need. */ void md_begin (void) { const char *retval = NULL; int i = 0; int broken = 0; if (mips_pic != NO_PIC) { if (g_switch_seen && g_switch_value != 0) as_bad (_("-G may not be used in position-independent code")); g_switch_value = 0; } if (! bfd_set_arch_mach (stdoutput, bfd_arch_mips, file_mips_arch)) as_warn (_("Could not set architecture and machine")); op_hash = hash_new (); for (i = 0; i < NUMOPCODES;) { const char *name = mips_opcodes[i].name; retval = hash_insert (op_hash, name, (void *) &mips_opcodes[i]); if (retval != NULL) { fprintf (stderr, _("internal error: can't hash `%s': %s\n"), mips_opcodes[i].name, retval); /* Probably a memory allocation problem? Give up now. */ as_fatal (_("Broken assembler. No assembly attempted.")); } do { if (mips_opcodes[i].pinfo != INSN_MACRO) { if (!validate_mips_insn (&mips_opcodes[i])) broken = 1; if (nop_insn.insn_mo == NULL && strcmp (name, "nop") == 0) { create_insn (&nop_insn, mips_opcodes + i); nop_insn.fixed_p = 1; } } ++i; } while ((i < NUMOPCODES) && !strcmp (mips_opcodes[i].name, name)); } mips16_op_hash = hash_new (); i = 0; while (i < bfd_mips16_num_opcodes) { const char *name = mips16_opcodes[i].name; retval = hash_insert (mips16_op_hash, name, (void *) &mips16_opcodes[i]); if (retval != NULL) as_fatal (_("internal: can't hash `%s': %s"), mips16_opcodes[i].name, retval); do { if (mips16_opcodes[i].pinfo != INSN_MACRO && ((mips16_opcodes[i].match & mips16_opcodes[i].mask) != mips16_opcodes[i].match)) { fprintf (stderr, _("internal error: bad mips16 opcode: %s %s\n"), mips16_opcodes[i].name, mips16_opcodes[i].args); broken = 1; } if (mips16_nop_insn.insn_mo == NULL && strcmp (name, "nop") == 0) { create_insn (&mips16_nop_insn, mips16_opcodes + i); mips16_nop_insn.fixed_p = 1; } ++i; } while (i < bfd_mips16_num_opcodes && strcmp (mips16_opcodes[i].name, name) == 0); } if (broken) as_fatal (_("Broken assembler. No assembly attempted.")); /* We add all the general register names to the symbol table. This helps us detect invalid uses of them. */ for (i = 0; reg_names[i].name; i++) symbol_table_insert (symbol_new (reg_names[i].name, reg_section, reg_names[i].num, // & RNUM_MASK, &zero_address_frag)); if (HAVE_NEWABI) for (i = 0; reg_names_n32n64[i].name; i++) symbol_table_insert (symbol_new (reg_names_n32n64[i].name, reg_section, reg_names_n32n64[i].num, // & RNUM_MASK, &zero_address_frag)); else for (i = 0; reg_names_o32[i].name; i++) symbol_table_insert (symbol_new (reg_names_o32[i].name, reg_section, reg_names_o32[i].num, // & RNUM_MASK, &zero_address_frag)); mips_no_prev_insn (); mips_gprmask = 0; mips_cprmask[0] = 0; mips_cprmask[1] = 0; mips_cprmask[2] = 0; mips_cprmask[3] = 0; /* set the default alignment for the text section (2**2) */ record_alignment (text_section, 2); bfd_set_gp_size (stdoutput, g_switch_value); #ifdef OBJ_ELF if (IS_ELF) { /* On a native system other than VxWorks, sections must be aligned to 16 byte boundaries. When configured for an embedded ELF target, we don't bother. */ if (strcmp (TARGET_OS, "elf") != 0 && strcmp (TARGET_OS, "vxworks") != 0) { (void) bfd_set_section_alignment (stdoutput, text_section, 4); (void) bfd_set_section_alignment (stdoutput, data_section, 4); (void) bfd_set_section_alignment (stdoutput, bss_section, 4); } /* Create a .reginfo section for register masks and a .mdebug section for debugging information. */ { segT seg; subsegT subseg; flagword flags; segT sec; seg = now_seg; subseg = now_subseg; /* The ABI says this section should be loaded so that the running program can access it. However, we don't load it if we are configured for an embedded target */ flags = SEC_READONLY | SEC_DATA; if (strcmp (TARGET_OS, "elf") != 0) flags |= SEC_ALLOC | SEC_LOAD; if (mips_abi != N64_ABI) { sec = subseg_new (".reginfo", (subsegT) 0); bfd_set_section_flags (stdoutput, sec, flags); bfd_set_section_alignment (stdoutput, sec, HAVE_NEWABI ? 3 : 2); mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo)); } else { /* The 64-bit ABI uses a .MIPS.options section rather than .reginfo section. */ sec = subseg_new (".MIPS.options", (subsegT) 0); bfd_set_section_flags (stdoutput, sec, flags); bfd_set_section_alignment (stdoutput, sec, 3); /* Set up the option header. */ { Elf_Internal_Options opthdr; char *f; opthdr.kind = ODK_REGINFO; opthdr.size = (sizeof (Elf_External_Options) + sizeof (Elf64_External_RegInfo)); opthdr.section = 0; opthdr.info = 0; f = frag_more (sizeof (Elf_External_Options)); bfd_mips_elf_swap_options_out (stdoutput, &opthdr, (Elf_External_Options *) f); mips_regmask_frag = frag_more (sizeof (Elf64_External_RegInfo)); } } if (ECOFF_DEBUGGING) { sec = subseg_new (".mdebug", (subsegT) 0); (void) bfd_set_section_flags (stdoutput, sec, SEC_HAS_CONTENTS | SEC_READONLY); (void) bfd_set_section_alignment (stdoutput, sec, 2); } else if (mips_flag_pdr) { pdr_seg = subseg_new (".pdr", (subsegT) 0); (void) bfd_set_section_flags (stdoutput, pdr_seg, SEC_READONLY | SEC_RELOC | SEC_DEBUGGING); (void) bfd_set_section_alignment (stdoutput, pdr_seg, 2); } subseg_set (seg, subseg); } } #endif /* OBJ_ELF */ if (! ECOFF_DEBUGGING) md_obj_begin (); if (mips_fix_vr4120) init_vr4120_conflicts (); } void md_mips_end (void) { if (! ECOFF_DEBUGGING) md_obj_end (); } void md_assemble (char *str) { struct mips_cl_insn insn; bfd_reloc_code_real_type unused_reloc[3] = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED}; imm_expr.X_op = O_absent; imm2_expr.X_op = O_absent; offset_expr.X_op = O_absent; imm_reloc[0] = BFD_RELOC_UNUSED; imm_reloc[1] = BFD_RELOC_UNUSED; imm_reloc[2] = BFD_RELOC_UNUSED; offset_reloc[0] = BFD_RELOC_UNUSED; offset_reloc[1] = BFD_RELOC_UNUSED; offset_reloc[2] = BFD_RELOC_UNUSED; if (mips_opts.mips16) mips16_ip (str, &insn); else { mips_ip (str, &insn); DBG ((_("returned from mips_ip(%s) insn_opcode = 0x%x\n"), str, insn.insn_opcode)); } if (insn_error) { as_bad ("%s `%s'", insn_error, str); return; } if (insn.insn_mo->pinfo == INSN_MACRO) { macro_start (); if (mips_opts.mips16) mips16_macro (&insn); else macro (&insn); macro_end (); } else { if (imm_expr.X_op != O_absent) append_insn (&insn, &imm_expr, imm_reloc); else if (offset_expr.X_op != O_absent) append_insn (&insn, &offset_expr, offset_reloc); else append_insn (&insn, NULL, unused_reloc); } } /* Return true if the given relocation might need a matching %lo(). This is only "might" because SVR4 R_MIPS_GOT16 relocations only need a matching %lo() when applied to local symbols. */ static inline bfd_boolean reloc_needs_lo_p (bfd_reloc_code_real_type reloc) { return (HAVE_IN_PLACE_ADDENDS && (reloc == BFD_RELOC_HI16_S || reloc == BFD_RELOC_MIPS16_HI16_S /* VxWorks R_MIPS_GOT16 relocs never need a matching %lo(); all GOT16 relocations evaluate to "G". */ || (reloc == BFD_RELOC_MIPS_GOT16 && mips_pic != VXWORKS_PIC))); } /* Return true if the given fixup is followed by a matching R_MIPS_LO16 relocation. */ static inline bfd_boolean fixup_has_matching_lo_p (fixS *fixp) { return (fixp->fx_next != NULL && (fixp->fx_next->fx_r_type == BFD_RELOC_LO16 || fixp->fx_next->fx_r_type == BFD_RELOC_MIPS16_LO16) && fixp->fx_addsy == fixp->fx_next->fx_addsy && fixp->fx_offset == fixp->fx_next->fx_offset); } /* See whether instruction IP reads register REG. CLASS is the type of register. */ static int insn_uses_reg (const struct mips_cl_insn *ip, unsigned int reg, enum mips_regclass class) { if (class == MIPS16_REG) { assert (mips_opts.mips16); reg = mips16_to_32_reg_map[reg]; class = MIPS_GR_REG; } /* Don't report on general register ZERO, since it never changes. */ if (class == MIPS_GR_REG && reg == ZERO) return 0; if (class == MIPS_FP_REG) { assert (! mips_opts.mips16); /* If we are called with either $f0 or $f1, we must check $f0. This is not optimal, because it will introduce an unnecessary NOP between "lwc1 $f0" and "swc1 $f1". To fix this we would need to distinguish reading both $f0 and $f1 or just one of them. Note that we don't have to check the other way, because there is no instruction that sets both $f0 and $f1 and requires a delay. */ if ((ip->insn_mo->pinfo & INSN_READ_FPR_S) && ((EXTRACT_OPERAND (FS, *ip) & ~(unsigned) 1) == (reg &~ (unsigned) 1))) return 1; if ((ip->insn_mo->pinfo & INSN_READ_FPR_T) && ((EXTRACT_OPERAND (FT, *ip) & ~(unsigned) 1) == (reg &~ (unsigned) 1))) return 1; } else if (! mips_opts.mips16) { if ((ip->insn_mo->pinfo & INSN_READ_GPR_S) && EXTRACT_OPERAND (RS, *ip) == reg) return 1; if ((ip->insn_mo->pinfo & INSN_READ_GPR_T) && EXTRACT_OPERAND (RT, *ip) == reg) return 1; } else { if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_X) && mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)] == reg) return 1; if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Y) && mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)] == reg) return 1; if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Z) && (mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip)] == reg)) return 1; if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_T) && reg == TREG) return 1; if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_SP) && reg == SP) return 1; if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_31) && reg == RA) return 1; if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_GPR_X) && MIPS16_EXTRACT_OPERAND (REGR32, *ip) == reg) return 1; } return 0; } /* This function returns true if modifying a register requires a delay. */ static int reg_needs_delay (unsigned int reg) { unsigned long prev_pinfo; prev_pinfo = history[0].insn_mo->pinfo; if (! mips_opts.noreorder && (((prev_pinfo & INSN_LOAD_MEMORY_DELAY) && ! gpr_interlocks) || ((prev_pinfo & INSN_LOAD_COPROC_DELAY) && ! cop_interlocks))) { /* A load from a coprocessor or from memory. All load delays delay the use of general register rt for one instruction. */ /* Itbl support may require additional care here. */ know (prev_pinfo & INSN_WRITE_GPR_T); if (reg == EXTRACT_OPERAND (RT, history[0])) return 1; } return 0; } /* Move all labels in insn_labels to the current insertion point. */ static void mips_move_labels (void) { segment_info_type *si = seg_info (now_seg); struct insn_label_list *l; valueT val; for (l = si->label_list; l != NULL; l = l->next) { assert (S_GET_SEGMENT (l->label) == now_seg); symbol_set_frag (l->label, frag_now); val = (valueT) frag_now_fix (); /* mips16 text labels are stored as odd. */ if (mips_opts.mips16) ++val; S_SET_VALUE (l->label, val); } } static bfd_boolean s_is_linkonce (symbolS *sym, segT from_seg) { bfd_boolean linkonce = FALSE; segT symseg = S_GET_SEGMENT (sym); if (symseg != from_seg && !S_IS_LOCAL (sym)) { if ((bfd_get_section_flags (stdoutput, symseg) & SEC_LINK_ONCE)) linkonce = TRUE; #ifdef OBJ_ELF /* The GNU toolchain uses an extension for ELF: a section beginning with the magic string .gnu.linkonce is a linkonce section. */ if (strncmp (segment_name (symseg), ".gnu.linkonce", sizeof ".gnu.linkonce" - 1) == 0) linkonce = TRUE; #endif } return linkonce; } /* Mark instruction labels in mips16 mode. This permits the linker to handle them specially, such as generating jalx instructions when needed. We also make them odd for the duration of the assembly, in order to generate the right sort of code. We will make them even in the adjust_symtab routine, while leaving them marked. This is convenient for the debugger and the disassembler. The linker knows to make them odd again. */ static void mips16_mark_labels (void) { segment_info_type *si = seg_info (now_seg); struct insn_label_list *l; if (!mips_opts.mips16) return; for (l = si->label_list; l != NULL; l = l->next) { symbolS *label = l->label; #if defined(OBJ_ELF) || defined(OBJ_MAYBE_ELF) if (IS_ELF) S_SET_OTHER (label, STO_MIPS16); #endif if ((S_GET_VALUE (label) & 1) == 0 /* Don't adjust the address if the label is global or weak, or in a link-once section, since we'll be emitting symbol reloc references to it which will be patched up by the linker, and the final value of the symbol may or may not be MIPS16. */ && ! S_IS_WEAK (label) && ! S_IS_EXTERNAL (label) && ! s_is_linkonce (label, now_seg)) S_SET_VALUE (label, S_GET_VALUE (label) | 1); } } /* End the current frag. Make it a variant frag and record the relaxation info. */ static void relax_close_frag (void) { mips_macro_warning.first_frag = frag_now; frag_var (rs_machine_dependent, 0, 0, RELAX_ENCODE (mips_relax.sizes[0], mips_relax.sizes[1]), mips_relax.symbol, 0, (char *) mips_relax.first_fixup); memset (&mips_relax.sizes, 0, sizeof (mips_relax.sizes)); mips_relax.first_fixup = 0; } /* Start a new relaxation sequence whose expansion depends on SYMBOL. See the comment above RELAX_ENCODE for more details. */ static void relax_start (symbolS *symbol) { assert (mips_relax.sequence == 0); mips_relax.sequence = 1; mips_relax.symbol = symbol; } /* Start generating the second version of a relaxable sequence. See the comment above RELAX_ENCODE for more details. */ static void relax_switch (void) { assert (mips_relax.sequence == 1); mips_relax.sequence = 2; } /* End the current relaxable sequence. */ static void relax_end (void) { assert (mips_relax.sequence == 2); relax_close_frag (); mips_relax.sequence = 0; } /* Classify an instruction according to the FIX_VR4120_* enumeration. Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected by VR4120 errata. */ static unsigned int classify_vr4120_insn (const char *name) { if (strncmp (name, "macc", 4) == 0) return FIX_VR4120_MACC; if (strncmp (name, "dmacc", 5) == 0) return FIX_VR4120_DMACC; if (strncmp (name, "mult", 4) == 0) return FIX_VR4120_MULT; if (strncmp (name, "dmult", 5) == 0) return FIX_VR4120_DMULT; if (strstr (name, "div")) return FIX_VR4120_DIV; if (strcmp (name, "mtlo") == 0 || strcmp (name, "mthi") == 0) return FIX_VR4120_MTHILO; return NUM_FIX_VR4120_CLASSES; } /* Return the number of instructions that must separate INSN1 and INSN2, where INSN1 is the earlier instruction. Return the worst-case value for any INSN2 if INSN2 is null. */ static unsigned int insns_between (const struct mips_cl_insn *insn1, const struct mips_cl_insn *insn2) { unsigned long pinfo1, pinfo2; /* This function needs to know which pinfo flags are set for INSN2 and which registers INSN2 uses. The former is stored in PINFO2 and the latter is tested via INSN2_USES_REG. If INSN2 is null, PINFO2 will have every flag set and INSN2_USES_REG will always return true. */ pinfo1 = insn1->insn_mo->pinfo; pinfo2 = insn2 ? insn2->insn_mo->pinfo : ~0U; #define INSN2_USES_REG(REG, CLASS) \ (insn2 == NULL || insn_uses_reg (insn2, REG, CLASS)) /* For most targets, write-after-read dependencies on the HI and LO registers must be separated by at least two instructions. */ if (!hilo_interlocks) { if ((pinfo1 & INSN_READ_LO) && (pinfo2 & INSN_WRITE_LO)) return 2; if ((pinfo1 & INSN_READ_HI) && (pinfo2 & INSN_WRITE_HI)) return 2; } /* If we're working around r7000 errata, there must be two instructions between an mfhi or mflo and any instruction that uses the result. */ if (mips_7000_hilo_fix && MF_HILO_INSN (pinfo1) && INSN2_USES_REG (EXTRACT_OPERAND (RD, *insn1), MIPS_GR_REG)) return 2; /* If working around VR4120 errata, check for combinations that need a single intervening instruction. */ if (mips_fix_vr4120) { unsigned int class1, class2; class1 = classify_vr4120_insn (insn1->insn_mo->name); if (class1 != NUM_FIX_VR4120_CLASSES && vr4120_conflicts[class1] != 0) { if (insn2 == NULL) return 1; class2 = classify_vr4120_insn (insn2->insn_mo->name); if (vr4120_conflicts[class1] & (1 << class2)) return 1; } } if (!mips_opts.mips16) { /* Check for GPR or coprocessor load delays. All such delays are on the RT register. */ /* Itbl support may require additional care here. */ if ((!gpr_interlocks && (pinfo1 & INSN_LOAD_MEMORY_DELAY)) || (!cop_interlocks && (pinfo1 & INSN_LOAD_COPROC_DELAY))) { know (pinfo1 & INSN_WRITE_GPR_T); if (INSN2_USES_REG (EXTRACT_OPERAND (RT, *insn1), MIPS_GR_REG)) return 1; } /* Check for generic coprocessor hazards. This case is not handled very well. There is no special knowledge of CP0 handling, and the coprocessors other than the floating point unit are not distinguished at all. */ /* Itbl support may require additional care here. FIXME! Need to modify this to include knowledge about user specified delays! */ else if ((!cop_interlocks && (pinfo1 & INSN_COPROC_MOVE_DELAY)) || (!cop_mem_interlocks && (pinfo1 & INSN_COPROC_MEMORY_DELAY))) { /* Handle cases where INSN1 writes to a known general coprocessor register. There must be a one instruction delay before INSN2 if INSN2 reads that register, otherwise no delay is needed. */ if (pinfo1 & INSN_WRITE_FPR_T) { if (INSN2_USES_REG (EXTRACT_OPERAND (FT, *insn1), MIPS_FP_REG)) return 1; } else if (pinfo1 & INSN_WRITE_FPR_S) { if (INSN2_USES_REG (EXTRACT_OPERAND (FS, *insn1), MIPS_FP_REG)) return 1; } else { /* Read-after-write dependencies on the control registers require a two-instruction gap. */ if ((pinfo1 & INSN_WRITE_COND_CODE) && (pinfo2 & INSN_READ_COND_CODE)) return 2; /* We don't know exactly what INSN1 does. If INSN2 is also a coprocessor instruction, assume there must be a one instruction gap. */ if (pinfo2 & INSN_COP) return 1; } } /* Check for read-after-write dependencies on the coprocessor control registers in cases where INSN1 does not need a general coprocessor delay. This means that INSN1 is a floating point comparison instruction. */ /* Itbl support may require additional care here. */ else if (!cop_interlocks && (pinfo1 & INSN_WRITE_COND_CODE) && (pinfo2 & INSN_READ_COND_CODE)) return 1; } #undef INSN2_USES_REG return 0; } /* Return the number of nops that would be needed to work around the VR4130 mflo/mfhi errata if instruction INSN immediately followed the MAX_VR4130_NOPS instructions described by HISTORY. */ static int nops_for_vr4130 (const struct mips_cl_insn *history, const struct mips_cl_insn *insn) { int i, j, reg; /* Check if the instruction writes to HI or LO. MTHI and MTLO are not affected by the errata. */ if (insn != 0 && ((insn->insn_mo->pinfo & (INSN_WRITE_HI | INSN_WRITE_LO)) == 0 || strcmp (insn->insn_mo->name, "mtlo") == 0 || strcmp (insn->insn_mo->name, "mthi") == 0)) return 0; /* Search for the first MFLO or MFHI. */ for (i = 0; i < MAX_VR4130_NOPS; i++) if (!history[i].noreorder_p && MF_HILO_INSN (history[i].insn_mo->pinfo)) { /* Extract the destination register. */ if (mips_opts.mips16) reg = mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, history[i])]; else reg = EXTRACT_OPERAND (RD, history[i]); /* No nops are needed if INSN reads that register. */ if (insn != NULL && insn_uses_reg (insn, reg, MIPS_GR_REG)) return 0; /* ...or if any of the intervening instructions do. */ for (j = 0; j < i; j++) if (insn_uses_reg (&history[j], reg, MIPS_GR_REG)) return 0; return MAX_VR4130_NOPS - i; } return 0; } /* Return the number of nops that would be needed if instruction INSN immediately followed the MAX_NOPS instructions given by HISTORY, where HISTORY[0] is the most recent instruction. If INSN is null, return the worse-case number of nops for any instruction. */ static int nops_for_insn (const struct mips_cl_insn *history, const struct mips_cl_insn *insn) { int i, nops, tmp_nops; nops = 0; for (i = 0; i < MAX_DELAY_NOPS; i++) if (!history[i].noreorder_p) { tmp_nops = insns_between (history + i, insn) - i; if (tmp_nops > nops) nops = tmp_nops; } if (mips_fix_vr4130) { tmp_nops = nops_for_vr4130 (history, insn); if (tmp_nops > nops) nops = tmp_nops; } return nops; } /* The variable arguments provide NUM_INSNS extra instructions that might be added to HISTORY. Return the largest number of nops that would be needed after the extended sequence. */ static int nops_for_sequence (int num_insns, const struct mips_cl_insn *history, ...) { va_list args; struct mips_cl_insn buffer[MAX_NOPS]; struct mips_cl_insn *cursor; int nops; va_start (args, history); cursor = buffer + num_insns; memcpy (cursor, history, (MAX_NOPS - num_insns) * sizeof (*cursor)); while (cursor > buffer) *--cursor = *va_arg (args, const struct mips_cl_insn *); nops = nops_for_insn (buffer, NULL); va_end (args); return nops; } /* Like nops_for_insn, but if INSN is a branch, take into account the worst-case delay for the branch target. */ static int nops_for_insn_or_target (const struct mips_cl_insn *history, const struct mips_cl_insn *insn) { int nops, tmp_nops; nops = nops_for_insn (history, insn); if (insn->insn_mo->pinfo & (INSN_UNCOND_BRANCH_DELAY | INSN_COND_BRANCH_DELAY | INSN_COND_BRANCH_LIKELY)) { tmp_nops = nops_for_sequence (2, history, insn, NOP_INSN); if (tmp_nops > nops) nops = tmp_nops; } else if (mips_opts.mips16 && (insn->insn_mo->pinfo & MIPS16_INSN_BRANCH)) { tmp_nops = nops_for_sequence (1, history, insn); if (tmp_nops > nops) nops = tmp_nops; } return nops; } /* Output an instruction. IP is the instruction information. ADDRESS_EXPR is an operand of the instruction to be used with RELOC_TYPE. */ static void append_insn (struct mips_cl_insn *ip, expressionS *address_expr, bfd_reloc_code_real_type *reloc_type) { unsigned long prev_pinfo, pinfo; relax_stateT prev_insn_frag_type = 0; bfd_boolean relaxed_branch = FALSE; segment_info_type *si = seg_info (now_seg); /* Mark instruction labels in mips16 mode. */ mips16_mark_labels (); prev_pinfo = history[0].insn_mo->pinfo; pinfo = ip->insn_mo->pinfo; if (mips_relax.sequence != 2 && !mips_opts.noreorder) { /* There are a lot of optimizations we could do that we don't. In particular, we do not, in general, reorder instructions. If you use gcc with optimization, it will reorder instructions and generally do much more optimization then we do here; repeating all that work in the assembler would only benefit hand written assembly code, and does not seem worth it. */ int nops = (mips_optimize == 0 ? nops_for_insn (history, NULL) : nops_for_insn_or_target (history, ip)); if (nops > 0) { fragS *old_frag; unsigned long old_frag_offset; int i; old_frag = frag_now; old_frag_offset = frag_now_fix (); for (i = 0; i < nops; i++) emit_nop (); if (listing) { listing_prev_line (); /* We may be at the start of a variant frag. In case we are, make sure there is enough space for the frag after the frags created by listing_prev_line. The argument to frag_grow here must be at least as large as the argument to all other calls to frag_grow in this file. We don't have to worry about being in the middle of a variant frag, because the variants insert all needed nop instructions themselves. */ frag_grow (40); } mips_move_labels (); #ifndef NO_ECOFF_DEBUGGING if (ECOFF_DEBUGGING) ecoff_fix_loc (old_frag, old_frag_offset); #endif } } else if (mips_relax.sequence != 2 && prev_nop_frag != NULL) { /* Work out how many nops in prev_nop_frag are needed by IP. */ int nops = nops_for_insn_or_target (history, ip); assert (nops <= prev_nop_frag_holds); /* Enforce NOPS as a minimum. */ if (nops > prev_nop_frag_required) prev_nop_frag_required = nops; if (prev_nop_frag_holds == prev_nop_frag_required) { /* Settle for the current number of nops. Update the history accordingly (for the benefit of any future .set reorder code). */ prev_nop_frag = NULL; insert_into_history (prev_nop_frag_since, prev_nop_frag_holds, NOP_INSN); } else { /* Allow this instruction to replace one of the nops that was tentatively added to prev_nop_frag. */ prev_nop_frag->fr_fix -= mips_opts.mips16 ? 2 : 4; prev_nop_frag_holds--; prev_nop_frag_since++; } } #ifdef OBJ_ELF /* The value passed to dwarf2_emit_insn is the distance between the beginning of the current instruction and the address that should be recorded in the debug tables. For MIPS16 debug info we want to use ISA-encoded addresses, so we pass -1 for an address higher by one than the current. */ dwarf2_emit_insn (mips_opts.mips16 ? -1 : 0); #endif /* Record the frag type before frag_var. */ if (history[0].frag) prev_insn_frag_type = history[0].frag->fr_type; if (address_expr && *reloc_type == BFD_RELOC_16_PCREL_S2 && (pinfo & INSN_UNCOND_BRANCH_DELAY || pinfo & INSN_COND_BRANCH_DELAY || pinfo & INSN_COND_BRANCH_LIKELY) && mips_relax_branch /* Don't try branch relaxation within .set nomacro, or within .set noat if we use $at for PIC computations. If it turns out that the branch was out-of-range, we'll get an error. */ && !mips_opts.warn_about_macros && !(mips_opts.noat && mips_pic != NO_PIC) && !mips_opts.mips16) { relaxed_branch = TRUE; add_relaxed_insn (ip, (relaxed_branch_length (NULL, NULL, (pinfo & INSN_UNCOND_BRANCH_DELAY) ? -1 : (pinfo & INSN_COND_BRANCH_LIKELY) ? 1 : 0)), 4, RELAX_BRANCH_ENCODE (pinfo & INSN_UNCOND_BRANCH_DELAY, pinfo & INSN_COND_BRANCH_LIKELY, pinfo & INSN_WRITE_GPR_31, 0), address_expr->X_add_symbol, address_expr->X_add_number); *reloc_type = BFD_RELOC_UNUSED; } else if (*reloc_type > BFD_RELOC_UNUSED) { /* We need to set up a variant frag. */ assert (mips_opts.mips16 && address_expr != NULL); add_relaxed_insn (ip, 4, 0, RELAX_MIPS16_ENCODE (*reloc_type - BFD_RELOC_UNUSED, mips16_small, mips16_ext, prev_pinfo & INSN_UNCOND_BRANCH_DELAY, history[0].mips16_absolute_jump_p), make_expr_symbol (address_expr), 0); } else if (mips_opts.mips16 && ! ip->use_extend && *reloc_type != BFD_RELOC_MIPS16_JMP) { if ((pinfo & INSN_UNCOND_BRANCH_DELAY) == 0) /* Make sure there is enough room to swap this instruction with a following jump instruction. */ frag_grow (6); add_fixed_insn (ip); } else { if (mips_opts.mips16 && mips_opts.noreorder && (prev_pinfo & INSN_UNCOND_BRANCH_DELAY) != 0) as_warn (_("extended instruction in delay slot")); if (mips_relax.sequence) { /* If we've reached the end of this frag, turn it into a variant frag and record the information for the instructions we've written so far. */ if (frag_room () < 4) relax_close_frag (); mips_relax.sizes[mips_relax.sequence - 1] += 4; } if (mips_relax.sequence != 2) mips_macro_warning.sizes[0] += 4; if (mips_relax.sequence != 1) mips_macro_warning.sizes[1] += 4; if (mips_opts.mips16) { ip->fixed_p = 1; ip->mips16_absolute_jump_p = (*reloc_type == BFD_RELOC_MIPS16_JMP); } add_fixed_insn (ip); } if (address_expr != NULL && *reloc_type <= BFD_RELOC_UNUSED) { if (address_expr->X_op == O_constant) { unsigned int tmp; switch (*reloc_type) { case BFD_RELOC_32: ip->insn_opcode |= address_expr->X_add_number; break; case BFD_RELOC_MIPS_HIGHEST: tmp = (address_expr->X_add_number + 0x800080008000ull) >> 48; ip->insn_opcode |= tmp & 0xffff; break; case BFD_RELOC_MIPS_HIGHER: tmp = (address_expr->X_add_number + 0x80008000ull) >> 32; ip->insn_opcode |= tmp & 0xffff; break; case BFD_RELOC_HI16_S: tmp = (address_expr->X_add_number + 0x8000) >> 16; ip->insn_opcode |= tmp & 0xffff; break; case BFD_RELOC_HI16: ip->insn_opcode |= (address_expr->X_add_number >> 16) & 0xffff; break; case BFD_RELOC_UNUSED: case BFD_RELOC_LO16: case BFD_RELOC_MIPS_GOT_DISP: ip->insn_opcode |= address_expr->X_add_number & 0xffff; break; case BFD_RELOC_MIPS_JMP: if ((address_expr->X_add_number & 3) != 0) as_bad (_("jump to misaligned address (0x%lx)"), (unsigned long) address_expr->X_add_number); ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0x3ffffff; break; case BFD_RELOC_MIPS16_JMP: if ((address_expr->X_add_number & 3) != 0) as_bad (_("jump to misaligned address (0x%lx)"), (unsigned long) address_expr->X_add_number); ip->insn_opcode |= (((address_expr->X_add_number & 0x7c0000) << 3) | ((address_expr->X_add_number & 0xf800000) >> 7) | ((address_expr->X_add_number & 0x3fffc) >> 2)); break; case BFD_RELOC_16_PCREL_S2: if ((address_expr->X_add_number & 3) != 0) as_bad (_("branch to misaligned address (0x%lx)"), (unsigned long) address_expr->X_add_number); if (mips_relax_branch) goto need_reloc; if ((address_expr->X_add_number + 0x20000) & ~0x3ffff) as_bad (_("branch address range overflow (0x%lx)"), (unsigned long) address_expr->X_add_number); ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0xffff; break; default: internalError (); } } else if (*reloc_type < BFD_RELOC_UNUSED) need_reloc: { reloc_howto_type *howto; int i; /* In a compound relocation, it is the final (outermost) operator that determines the relocated field. */ for (i = 1; i < 3; i++) if (reloc_type[i] == BFD_RELOC_UNUSED) break; howto = bfd_reloc_type_lookup (stdoutput, reloc_type[i - 1]); ip->fixp[0] = fix_new_exp (ip->frag, ip->where, bfd_get_reloc_size (howto), address_expr, reloc_type[0] == BFD_RELOC_16_PCREL_S2, reloc_type[0]); /* Tag symbols that have a R_MIPS16_26 relocation against them. */ if (reloc_type[0] == BFD_RELOC_MIPS16_JMP && ip->fixp[0]->fx_addsy) *symbol_get_tc (ip->fixp[0]->fx_addsy) = 1; /* These relocations can have an addend that won't fit in 4 octets for 64bit assembly. */ if (HAVE_64BIT_GPRS && ! howto->partial_inplace && (reloc_type[0] == BFD_RELOC_16 || reloc_type[0] == BFD_RELOC_32 || reloc_type[0] == BFD_RELOC_MIPS_JMP || reloc_type[0] == BFD_RELOC_HI16_S || reloc_type[0] == BFD_RELOC_LO16 || reloc_type[0] == BFD_RELOC_GPREL16 || reloc_type[0] == BFD_RELOC_MIPS_LITERAL || reloc_type[0] == BFD_RELOC_GPREL32 || reloc_type[0] == BFD_RELOC_64 || reloc_type[0] == BFD_RELOC_CTOR || reloc_type[0] == BFD_RELOC_MIPS_SUB || reloc_type[0] == BFD_RELOC_MIPS_HIGHEST || reloc_type[0] == BFD_RELOC_MIPS_HIGHER || reloc_type[0] == BFD_RELOC_MIPS_SCN_DISP || reloc_type[0] == BFD_RELOC_MIPS_REL16 || reloc_type[0] == BFD_RELOC_MIPS_RELGOT || reloc_type[0] == BFD_RELOC_MIPS16_GPREL || reloc_type[0] == BFD_RELOC_MIPS16_HI16_S || reloc_type[0] == BFD_RELOC_MIPS16_LO16)) ip->fixp[0]->fx_no_overflow = 1; if (mips_relax.sequence) { if (mips_relax.first_fixup == 0) mips_relax.first_fixup = ip->fixp[0]; } else if (reloc_needs_lo_p (*reloc_type)) { struct mips_hi_fixup *hi_fixup; /* Reuse the last entry if it already has a matching %lo. */ hi_fixup = mips_hi_fixup_list; if (hi_fixup == 0 || !fixup_has_matching_lo_p (hi_fixup->fixp)) { hi_fixup = ((struct mips_hi_fixup *) xmalloc (sizeof (struct mips_hi_fixup))); hi_fixup->next = mips_hi_fixup_list; mips_hi_fixup_list = hi_fixup; } hi_fixup->fixp = ip->fixp[0]; hi_fixup->seg = now_seg; } /* Add fixups for the second and third relocations, if given. Note that the ABI allows the second relocation to be against RSS_UNDEF, RSS_GP, RSS_GP0 or RSS_LOC. At the moment we only use RSS_UNDEF, but we could add support for the others if it ever becomes necessary. */ for (i = 1; i < 3; i++) if (reloc_type[i] != BFD_RELOC_UNUSED) { ip->fixp[i] = fix_new (ip->frag, ip->where, ip->fixp[0]->fx_size, NULL, 0, FALSE, reloc_type[i]); /* Use fx_tcbit to mark compound relocs. */ ip->fixp[0]->fx_tcbit = 1; ip->fixp[i]->fx_tcbit = 1; } } } install_insn (ip); /* Update the register mask information. */ if (! mips_opts.mips16) { if (pinfo & INSN_WRITE_GPR_D) mips_gprmask |= 1 << EXTRACT_OPERAND (RD, *ip); if ((pinfo & (INSN_WRITE_GPR_T | INSN_READ_GPR_T)) != 0) mips_gprmask |= 1 << EXTRACT_OPERAND (RT, *ip); if (pinfo & INSN_READ_GPR_S) mips_gprmask |= 1 << EXTRACT_OPERAND (RS, *ip); if (pinfo & INSN_WRITE_GPR_31) mips_gprmask |= 1 << RA; if (pinfo & INSN_WRITE_FPR_D) mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FD, *ip); if ((pinfo & (INSN_WRITE_FPR_S | INSN_READ_FPR_S)) != 0) mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FS, *ip); if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0) mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FT, *ip); if ((pinfo & INSN_READ_FPR_R) != 0) mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FR, *ip); if (pinfo & INSN_COP) { /* We don't keep enough information to sort these cases out. The itbl support does keep this information however, although we currently don't support itbl fprmats as part of the cop instruction. May want to add this support in the future. */ } /* Never set the bit for $0, which is always zero. */ mips_gprmask &= ~1 << 0; } else { if (pinfo & (MIPS16_INSN_WRITE_X | MIPS16_INSN_READ_X)) mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RX, *ip); if (pinfo & (MIPS16_INSN_WRITE_Y | MIPS16_INSN_READ_Y)) mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RY, *ip); if (pinfo & MIPS16_INSN_WRITE_Z) mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RZ, *ip); if (pinfo & (MIPS16_INSN_WRITE_T | MIPS16_INSN_READ_T)) mips_gprmask |= 1 << TREG; if (pinfo & (MIPS16_INSN_WRITE_SP | MIPS16_INSN_READ_SP)) mips_gprmask |= 1 << SP; if (pinfo & (MIPS16_INSN_WRITE_31 | MIPS16_INSN_READ_31)) mips_gprmask |= 1 << RA; if (pinfo & MIPS16_INSN_WRITE_GPR_Y) mips_gprmask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode); if (pinfo & MIPS16_INSN_READ_Z) mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip); if (pinfo & MIPS16_INSN_READ_GPR_X) mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (REGR32, *ip); } if (mips_relax.sequence != 2 && !mips_opts.noreorder) { /* Filling the branch delay slot is more complex. We try to switch the branch with the previous instruction, which we can do if the previous instruction does not set up a condition that the branch tests and if the branch is not itself the target of any branch. */ if ((pinfo & INSN_UNCOND_BRANCH_DELAY) || (pinfo & INSN_COND_BRANCH_DELAY)) { if (mips_optimize < 2 /* If we have seen .set volatile or .set nomove, don't optimize. */ || mips_opts.nomove != 0 /* We can't swap if the previous instruction's position is fixed. */ || history[0].fixed_p /* If the previous previous insn was in a .set noreorder, we can't swap. Actually, the MIPS assembler will swap in this situation. However, gcc configured -with-gnu-as will generate code like .set noreorder lw $4,XXX .set reorder INSN bne $4,$0,foo in which we can not swap the bne and INSN. If gcc is not configured -with-gnu-as, it does not output the .set pseudo-ops. */ || history[1].noreorder_p /* If the branch is itself the target of a branch, we can not swap. We cheat on this; all we check for is whether there is a label on this instruction. If there are any branches to anything other than a label, users must use .set noreorder. */ || si->label_list != NULL /* If the previous instruction is in a variant frag other than this branch's one, we cannot do the swap. This does not apply to the mips16, which uses variant frags for different purposes. */ || (! mips_opts.mips16 && prev_insn_frag_type == rs_machine_dependent) /* Check for conflicts between the branch and the instructions before the candidate delay slot. */ || nops_for_insn (history + 1, ip) > 0 /* Check for conflicts between the swapped sequence and the target of the branch. */ || nops_for_sequence (2, history + 1, ip, history) > 0 /* We do not swap with a trap instruction, since it complicates trap handlers to have the trap instruction be in a delay slot. */ || (prev_pinfo & INSN_TRAP) /* If the branch reads a register that the previous instruction sets, we can not swap. */ || (! mips_opts.mips16 && (prev_pinfo & INSN_WRITE_GPR_T) && insn_uses_reg (ip, EXTRACT_OPERAND (RT, history[0]), MIPS_GR_REG)) || (! mips_opts.mips16 && (prev_pinfo & INSN_WRITE_GPR_D) && insn_uses_reg (ip, EXTRACT_OPERAND (RD, history[0]), MIPS_GR_REG)) || (mips_opts.mips16 && (((prev_pinfo & MIPS16_INSN_WRITE_X) && (insn_uses_reg (ip, MIPS16_EXTRACT_OPERAND (RX, history[0]), MIPS16_REG))) || ((prev_pinfo & MIPS16_INSN_WRITE_Y) && (insn_uses_reg (ip, MIPS16_EXTRACT_OPERAND (RY, history[0]), MIPS16_REG))) || ((prev_pinfo & MIPS16_INSN_WRITE_Z) && (insn_uses_reg (ip, MIPS16_EXTRACT_OPERAND (RZ, history[0]), MIPS16_REG))) || ((prev_pinfo & MIPS16_INSN_WRITE_T) && insn_uses_reg (ip, TREG, MIPS_GR_REG)) || ((prev_pinfo & MIPS16_INSN_WRITE_31) && insn_uses_reg (ip, RA, MIPS_GR_REG)) || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y) && insn_uses_reg (ip, MIPS16OP_EXTRACT_REG32R (history[0].insn_opcode), MIPS_GR_REG)))) /* If the branch writes a register that the previous instruction sets, we can not swap (we know that branches write only to RD or to $31). */ || (! mips_opts.mips16 && (prev_pinfo & INSN_WRITE_GPR_T) && (((pinfo & INSN_WRITE_GPR_D) && (EXTRACT_OPERAND (RT, history[0]) == EXTRACT_OPERAND (RD, *ip))) || ((pinfo & INSN_WRITE_GPR_31) && EXTRACT_OPERAND (RT, history[0]) == RA))) || (! mips_opts.mips16 && (prev_pinfo & INSN_WRITE_GPR_D) && (((pinfo & INSN_WRITE_GPR_D) && (EXTRACT_OPERAND (RD, history[0]) == EXTRACT_OPERAND (RD, *ip))) || ((pinfo & INSN_WRITE_GPR_31) && EXTRACT_OPERAND (RD, history[0]) == RA))) || (mips_opts.mips16 && (pinfo & MIPS16_INSN_WRITE_31) && ((prev_pinfo & MIPS16_INSN_WRITE_31) || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y) && (MIPS16OP_EXTRACT_REG32R (history[0].insn_opcode) == RA)))) /* If the branch writes a register that the previous instruction reads, we can not swap (we know that branches only write to RD or to $31). */ || (! mips_opts.mips16 && (pinfo & INSN_WRITE_GPR_D) && insn_uses_reg (&history[0], EXTRACT_OPERAND (RD, *ip), MIPS_GR_REG)) || (! mips_opts.mips16 && (pinfo & INSN_WRITE_GPR_31) && insn_uses_reg (&history[0], RA, MIPS_GR_REG)) || (mips_opts.mips16 && (pinfo & MIPS16_INSN_WRITE_31) && insn_uses_reg (&history[0], RA, MIPS_GR_REG)) /* If one instruction sets a condition code and the other one uses a condition code, we can not swap. */ || ((pinfo & INSN_READ_COND_CODE) && (prev_pinfo & INSN_WRITE_COND_CODE)) || ((pinfo & INSN_WRITE_COND_CODE) && (prev_pinfo & INSN_READ_COND_CODE)) /* If the previous instruction uses the PC, we can not swap. */ || (mips_opts.mips16 && (prev_pinfo & MIPS16_INSN_READ_PC)) /* If the previous instruction had a fixup in mips16 mode, we can not swap. This normally means that the previous instruction was a 4 byte branch anyhow. */ || (mips_opts.mips16 && history[0].fixp[0]) /* If the previous instruction is a sync, sync.l, or sync.p, we can not swap. */ || (prev_pinfo & INSN_SYNC)) { if (mips_opts.mips16 && (pinfo & INSN_UNCOND_BRANCH_DELAY) && (pinfo & (MIPS16_INSN_READ_X | MIPS16_INSN_READ_31)) && ISA_SUPPORTS_MIPS16E) { /* Convert MIPS16 jr/jalr into a "compact" jump. */ ip->insn_opcode |= 0x0080; install_insn (ip); insert_into_history (0, 1, ip); } else { /* We could do even better for unconditional branches to portions of this object file; we could pick up the instruction at the destination, put it in the delay slot, and bump the destination address. */ insert_into_history (0, 1, ip); emit_nop (); } if (mips_relax.sequence) mips_relax.sizes[mips_relax.sequence - 1] += 4; } else { /* It looks like we can actually do the swap. */ struct mips_cl_insn delay = history[0]; if (mips_opts.mips16) { know (delay.frag == ip->frag); move_insn (ip, delay.frag, delay.where); move_insn (&delay, ip->frag, ip->where + insn_length (ip)); } else if (relaxed_branch) { /* Add the delay slot instruction to the end of the current frag and shrink the fixed part of the original frag. If the branch occupies the tail of the latter, move it backwards to cover the gap. */ delay.frag->fr_fix -= 4; if (delay.frag == ip->frag) move_insn (ip, ip->frag, ip->where - 4); add_fixed_insn (&delay); } else { move_insn (&delay, ip->frag, ip->where); move_insn (ip, history[0].frag, history[0].where); } history[0] = *ip; delay.fixed_p = 1; insert_into_history (0, 1, &delay); } /* If that was an unconditional branch, forget the previous insn information. */ if (pinfo & INSN_UNCOND_BRANCH_DELAY) mips_no_prev_insn (); } else if (pinfo & INSN_COND_BRANCH_LIKELY) { /* We don't yet optimize a branch likely. What we should do is look at the target, copy the instruction found there into the delay slot, and increment the branch to jump to the next instruction. */ insert_into_history (0, 1, ip); emit_nop (); } else insert_into_history (0, 1, ip); } else insert_into_history (0, 1, ip); /* We just output an insn, so the next one doesn't have a label. */ mips_clear_insn_labels (); } /* Forget that there was any previous instruction or label. */ static void mips_no_prev_insn (void) { prev_nop_frag = NULL; insert_into_history (0, ARRAY_SIZE (history), NOP_INSN); mips_clear_insn_labels (); } /* This function must be called before we emit something other than instructions. It is like mips_no_prev_insn except that it inserts any NOPS that might be needed by previous instructions. */ void mips_emit_delays (void) { if (! mips_opts.noreorder) { int nops = nops_for_insn (history, NULL); if (nops > 0) { while (nops-- > 0) add_fixed_insn (NOP_INSN); mips_move_labels (); } } mips_no_prev_insn (); } /* Start a (possibly nested) noreorder block. */ static void start_noreorder (void) { if (mips_opts.noreorder == 0) { unsigned int i; int nops; /* None of the instructions before the .set noreorder can be moved. */ for (i = 0; i < ARRAY_SIZE (history); i++) history[i].fixed_p = 1; /* Insert any nops that might be needed between the .set noreorder block and the previous instructions. We will later remove any nops that turn out not to be needed. */ nops = nops_for_insn (history, NULL); if (nops > 0) { if (mips_optimize != 0) { /* Record the frag which holds the nop instructions, so that we can remove them if we don't need them. */ frag_grow (mips_opts.mips16 ? nops * 2 : nops * 4); prev_nop_frag = frag_now; prev_nop_frag_holds = nops; prev_nop_frag_required = 0; prev_nop_frag_since = 0; } for (; nops > 0; --nops) add_fixed_insn (NOP_INSN); /* Move on to a new frag, so that it is safe to simply decrease the size of prev_nop_frag. */ frag_wane (frag_now); frag_new (0); mips_move_labels (); } mips16_mark_labels (); mips_clear_insn_labels (); } mips_opts.noreorder++; mips_any_noreorder = 1; } /* End a nested noreorder block. */ static void end_noreorder (void) { mips_opts.noreorder--; if (mips_opts.noreorder == 0 && prev_nop_frag != NULL) { /* Commit to inserting prev_nop_frag_required nops and go back to handling nop insertion the .set reorder way. */ prev_nop_frag->fr_fix -= ((prev_nop_frag_holds - prev_nop_frag_required) * (mips_opts.mips16 ? 2 : 4)); insert_into_history (prev_nop_frag_since, prev_nop_frag_required, NOP_INSN); prev_nop_frag = NULL; } } /* Set up global variables for the start of a new macro. */ static void macro_start (void) { memset (&mips_macro_warning.sizes, 0, sizeof (mips_macro_warning.sizes)); mips_macro_warning.delay_slot_p = (mips_opts.noreorder && (history[0].insn_mo->pinfo & (INSN_UNCOND_BRANCH_DELAY | INSN_COND_BRANCH_DELAY | INSN_COND_BRANCH_LIKELY)) != 0); } /* Given that a macro is longer than 4 bytes, return the appropriate warning for it. Return null if no warning is needed. SUBTYPE is a bitmask of RELAX_DELAY_SLOT and RELAX_NOMACRO. */ static const char * macro_warning (relax_substateT subtype) { if (subtype & RELAX_DELAY_SLOT) return _("Macro instruction expanded into multiple instructions" " in a branch delay slot"); else if (subtype & RELAX_NOMACRO) return _("Macro instruction expanded into multiple instructions"); else return 0; } /* Finish up a macro. Emit warnings as appropriate. */ static void macro_end (void) { if (mips_macro_warning.sizes[0] > 4 || mips_macro_warning.sizes[1] > 4) { relax_substateT subtype; /* Set up the relaxation warning flags. */ subtype = 0; if (mips_macro_warning.sizes[1] > mips_macro_warning.sizes[0]) subtype |= RELAX_SECOND_LONGER; if (mips_opts.warn_about_macros) subtype |= RELAX_NOMACRO; if (mips_macro_warning.delay_slot_p) subtype |= RELAX_DELAY_SLOT; if (mips_macro_warning.sizes[0] > 4 && mips_macro_warning.sizes[1] > 4) { /* Either the macro has a single implementation or both implementations are longer than 4 bytes. Emit the warning now. */ const char *msg = macro_warning (subtype); if (msg != 0) as_warn (msg); } else { /* One implementation might need a warning but the other definitely doesn't. */ mips_macro_warning.first_frag->fr_subtype |= subtype; } } } /* Read a macro's relocation codes from *ARGS and store them in *R. The first argument in *ARGS will be either the code for a single relocation or -1 followed by the three codes that make up a composite relocation. */ static void macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r) { int i, next; next = va_arg (*args, int); if (next >= 0) r[0] = (bfd_reloc_code_real_type) next; else for (i = 0; i < 3; i++) r[i] = (bfd_reloc_code_real_type) va_arg (*args, int); } /* Build an instruction created by a macro expansion. This is passed a pointer to the count of instructions created so far, an expression, the name of the instruction to build, an operand format string, and corresponding arguments. */ static void macro_build (expressionS *ep, const char *name, const char *fmt, ...) { const struct mips_opcode *mo; struct mips_cl_insn insn; bfd_reloc_code_real_type r[3]; va_list args; va_start (args, fmt); if (mips_opts.mips16) { mips16_macro_build (ep, name, fmt, args); va_end (args); return; } r[0] = BFD_RELOC_UNUSED; r[1] = BFD_RELOC_UNUSED; r[2] = BFD_RELOC_UNUSED; mo = (struct mips_opcode *) hash_find (op_hash, name); assert (mo); assert (strcmp (name, mo->name) == 0); while (1) { /* Search until we get a match for NAME. It is assumed here that macros will never generate MDMX, MIPS-3D, or MT instructions. */ if (strcmp (fmt, mo->args) == 0 && mo->pinfo != INSN_MACRO && OPCODE_IS_MEMBER (mo, (mips_opts.isa | (mips_opts.mips16 ? INSN_MIPS16 : 0) | (mips_opts.ase_dsp ? INSN_DSP : 0) | ((mips_opts.ase_dsp && ISA_SUPPORTS_DSP64_ASE) ? INSN_DSP64 : 0) | (mips_opts.ase_dspr2 ? INSN_DSPR2 : 0) | (mips_opts.ase_smartmips ? INSN_SMARTMIPS : 0)), mips_opts.arch) && (mips_opts.arch != CPU_R4650 || (mo->pinfo & FP_D) == 0)) break; ++mo; assert (mo->name); assert (strcmp (name, mo->name) == 0); } create_insn (&insn, mo); for (;;) { switch (*fmt++) { case '\0': break; case ',': case '(': case ')': continue; case '+': switch (*fmt++) { case 'A': case 'E': INSERT_OPERAND (SHAMT, insn, va_arg (args, int)); continue; case 'B': case 'F': /* Note that in the macro case, these arguments are already in MSB form. (When handling the instruction in the non-macro case, these arguments are sizes from which MSB values must be calculated.) */ INSERT_OPERAND (INSMSB, insn, va_arg (args, int)); continue; case 'C': case 'G': case 'H': /* Note that in the macro case, these arguments are already in MSBD form. (When handling the instruction in the non-macro case, these arguments are sizes from which MSBD values must be calculated.) */ INSERT_OPERAND (EXTMSBD, insn, va_arg (args, int)); continue; default: internalError (); } continue; case '2': INSERT_OPERAND (BP, insn, va_arg (args, int)); continue; case 't': case 'w': case 'E': INSERT_OPERAND (RT, insn, va_arg (args, int)); continue; case 'c': INSERT_OPERAND (CODE, insn, va_arg (args, int)); continue; case 'T': case 'W': INSERT_OPERAND (FT, insn, va_arg (args, int)); continue; case 'd': case 'G': case 'K': INSERT_OPERAND (RD, insn, va_arg (args, int)); continue; case 'U': { int tmp = va_arg (args, int); INSERT_OPERAND (RT, insn, tmp); INSERT_OPERAND (RD, insn, tmp); continue; } case 'V': case 'S': INSERT_OPERAND (FS, insn, va_arg (args, int)); continue; case 'z': continue; case '<': INSERT_OPERAND (SHAMT, insn, va_arg (args, int)); continue; case 'D': INSERT_OPERAND (FD, insn, va_arg (args, int)); continue; case 'B': INSERT_OPERAND (CODE20, insn, va_arg (args, int)); continue; case 'J': INSERT_OPERAND (CODE19, insn, va_arg (args, int)); continue; case 'q': INSERT_OPERAND (CODE2, insn, va_arg (args, int)); continue; case 'b': case 's': case 'r': case 'v': INSERT_OPERAND (RS, insn, va_arg (args, int)); continue; case 'i': case 'j': case 'o': macro_read_relocs (&args, r); assert (*r == BFD_RELOC_GPREL16 || *r == BFD_RELOC_MIPS_LITERAL || *r == BFD_RELOC_MIPS_HIGHER || *r == BFD_RELOC_HI16_S || *r == BFD_RELOC_LO16 || *r == BFD_RELOC_MIPS_GOT16 || *r == BFD_RELOC_MIPS_CALL16 || *r == BFD_RELOC_MIPS_GOT_DISP || *r == BFD_RELOC_MIPS_GOT_PAGE || *r == BFD_RELOC_MIPS_GOT_OFST || *r == BFD_RELOC_MIPS_GOT_LO16 || *r == BFD_RELOC_MIPS_CALL_LO16); continue; case 'u': macro_read_relocs (&args, r); assert (ep != NULL && (ep->X_op == O_constant || (ep->X_op == O_symbol && (*r == BFD_RELOC_MIPS_HIGHEST || *r == BFD_RELOC_HI16_S || *r == BFD_RELOC_HI16 || *r == BFD_RELOC_GPREL16 || *r == BFD_RELOC_MIPS_GOT_HI16 || *r == BFD_RELOC_MIPS_CALL_HI16)))); continue; case 'p': assert (ep != NULL); /* * This allows macro() to pass an immediate expression for * creating short branches without creating a symbol. * * We don't allow branch relaxation for these branches, as * they should only appear in ".set nomacro" anyway. */ if (ep->X_op == O_constant) { if ((ep->X_add_number & 3) != 0) as_bad (_("branch to misaligned address (0x%lx)"), (unsigned long) ep->X_add_number); if ((ep->X_add_number + 0x20000) & ~0x3ffff) as_bad (_("branch address range overflow (0x%lx)"), (unsigned long) ep->X_add_number); insn.insn_opcode |= (ep->X_add_number >> 2) & 0xffff; ep = NULL; } else *r = BFD_RELOC_16_PCREL_S2; continue; case 'a': assert (ep != NULL); *r = BFD_RELOC_MIPS_JMP; continue; case 'C': INSERT_OPERAND (COPZ, insn, va_arg (args, unsigned long)); continue; case 'k': INSERT_OPERAND (CACHE, insn, va_arg (args, unsigned long)); continue; default: internalError (); } break; } va_end (args); assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL); append_insn (&insn, ep, r); } static void mips16_macro_build (expressionS *ep, const char *name, const char *fmt, va_list args) { struct mips_opcode *mo; struct mips_cl_insn insn; bfd_reloc_code_real_type r[3] = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED}; mo = (struct mips_opcode *) hash_find (mips16_op_hash, name); assert (mo); assert (strcmp (name, mo->name) == 0); while (strcmp (fmt, mo->args) != 0 || mo->pinfo == INSN_MACRO) { ++mo; assert (mo->name); assert (strcmp (name, mo->name) == 0); } create_insn (&insn, mo); for (;;) { int c; c = *fmt++; switch (c) { case '\0': break; case ',': case '(': case ')': continue; case 'y': case 'w': MIPS16_INSERT_OPERAND (RY, insn, va_arg (args, int)); continue; case 'x': case 'v': MIPS16_INSERT_OPERAND (RX, insn, va_arg (args, int)); continue; case 'z': MIPS16_INSERT_OPERAND (RZ, insn, va_arg (args, int)); continue; case 'Z': MIPS16_INSERT_OPERAND (MOVE32Z, insn, va_arg (args, int)); continue; case '0': case 'S': case 'P': case 'R': continue; case 'X': MIPS16_INSERT_OPERAND (REGR32, insn, va_arg (args, int)); continue; case 'Y': { int regno; regno = va_arg (args, int); regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3); MIPS16_INSERT_OPERAND (REG32R, insn, regno); } continue; case '<': case '>': case '4': case '5': case 'H': case 'W': case 'D': case 'j': case '8': case 'V': case 'C': case 'U': case 'k': case 'K': case 'p': case 'q': { assert (ep != NULL); if (ep->X_op != O_constant) *r = (int) BFD_RELOC_UNUSED + c; else { mips16_immed (NULL, 0, c, ep->X_add_number, FALSE, FALSE, FALSE, &insn.insn_opcode, &insn.use_extend, &insn.extend); ep = NULL; *r = BFD_RELOC_UNUSED; } } continue; case '6': MIPS16_INSERT_OPERAND (IMM6, insn, va_arg (args, int)); continue; } break; } assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL); append_insn (&insn, ep, r); } /* * Sign-extend 32-bit mode constants that have bit 31 set and all * higher bits unset. */ static void normalize_constant_expr (expressionS *ex) { if (ex->X_op == O_constant && IS_ZEXT_32BIT_NUM (ex->X_add_number)) ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000) - 0x80000000); } /* * Sign-extend 32-bit mode address offsets that have bit 31 set and * all higher bits unset. */ static void normalize_address_expr (expressionS *ex) { if (((ex->X_op == O_constant && HAVE_32BIT_ADDRESSES) || (ex->X_op == O_symbol && HAVE_32BIT_SYMBOLS)) && IS_ZEXT_32BIT_NUM (ex->X_add_number)) ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000) - 0x80000000); } /* * Generate a "jalr" instruction with a relocation hint to the called * function. This occurs in NewABI PIC code. */ static void macro_build_jalr (expressionS *ep) { char *f = NULL; if (HAVE_NEWABI) { frag_grow (8); f = frag_more (0); } macro_build (NULL, "jalr", "d,s", RA, PIC_CALL_REG); if (HAVE_NEWABI) fix_new_exp (frag_now, f - frag_now->fr_literal, 4, ep, FALSE, BFD_RELOC_MIPS_JALR); } /* * Generate a "lui" instruction. */ static void macro_build_lui (expressionS *ep, int regnum) { expressionS high_expr; const struct mips_opcode *mo; struct mips_cl_insn insn; bfd_reloc_code_real_type r[3] = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED}; const char *name = "lui"; const char *fmt = "t,u"; assert (! mips_opts.mips16); high_expr = *ep; if (high_expr.X_op == O_constant) { /* We can compute the instruction now without a relocation entry. */ high_expr.X_add_number = ((high_expr.X_add_number + 0x8000) >> 16) & 0xffff; *r = BFD_RELOC_UNUSED; } else { assert (ep->X_op == O_symbol); /* _gp_disp is a special case, used from s_cpload. __gnu_local_gp is used if mips_no_shared. */ assert (mips_pic == NO_PIC || (! HAVE_NEWABI && strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0) || (! mips_in_shared && strcmp (S_GET_NAME (ep->X_add_symbol), "__gnu_local_gp") == 0)); *r = BFD_RELOC_HI16_S; } mo = hash_find (op_hash, name); assert (strcmp (name, mo->name) == 0); assert (strcmp (fmt, mo->args) == 0); create_insn (&insn, mo); insn.insn_opcode = insn.insn_mo->match; INSERT_OPERAND (RT, insn, regnum); if (*r == BFD_RELOC_UNUSED) { insn.insn_opcode |= high_expr.X_add_number; append_insn (&insn, NULL, r); } else append_insn (&insn, &high_expr, r); } /* Generate a sequence of instructions to do a load or store from a constant offset off of a base register (breg) into/from a target register (treg), using AT if necessary. */ static void macro_build_ldst_constoffset (expressionS *ep, const char *op, int treg, int breg, int dbl) { assert (ep->X_op == O_constant); /* Sign-extending 32-bit constants makes their handling easier. */ if (!dbl) normalize_constant_expr (ep); /* Right now, this routine can only handle signed 32-bit constants. */ if (! IS_SEXT_32BIT_NUM(ep->X_add_number + 0x8000)) as_warn (_("operand overflow")); if (IS_SEXT_16BIT_NUM(ep->X_add_number)) { /* Signed 16-bit offset will fit in the op. Easy! */ macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, breg); } else { /* 32-bit offset, need multiple instructions and AT, like: lui $tempreg,const_hi (BFD_RELOC_HI16_S) addu $tempreg,$tempreg,$breg $treg,const_lo($tempreg) (BFD_RELOC_LO16) to handle the complete offset. */ macro_build_lui (ep, AT); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg); macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, AT); if (mips_opts.noat) as_bad (_("Macro used $at after \".set noat\"")); } } /* set_at() * Generates code to set the $at register to true (one) * if reg is less than the immediate expression. */ static void set_at (int reg, int unsignedp) { if (imm_expr.X_op == O_constant && imm_expr.X_add_number >= -0x8000 && imm_expr.X_add_number < 0x8000) macro_build (&imm_expr, unsignedp ? "sltiu" : "slti", "t,r,j", AT, reg, BFD_RELOC_LO16); else { load_register (AT, &imm_expr, HAVE_64BIT_GPRS); macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT); } } /* Warn if an expression is not a constant. */ static void check_absolute_expr (struct mips_cl_insn *ip, expressionS *ex) { if (ex->X_op == O_big) as_bad (_("unsupported large constant")); else if (ex->X_op != O_constant) as_bad (_("Instruction %s requires absolute expression"), ip->insn_mo->name); if (HAVE_32BIT_GPRS) normalize_constant_expr (ex); } /* Count the leading zeroes by performing a binary chop. This is a bulky bit of source, but performance is a LOT better for the majority of values than a simple loop to count the bits: for (lcnt = 0; (lcnt < 32); lcnt++) if ((v) & (1 << (31 - lcnt))) break; However it is not code size friendly, and the gain will drop a bit on certain cached systems. */ #define COUNT_TOP_ZEROES(v) \ (((v) & ~0xffff) == 0 \ ? ((v) & ~0xff) == 0 \ ? ((v) & ~0xf) == 0 \ ? ((v) & ~0x3) == 0 \ ? ((v) & ~0x1) == 0 \ ? !(v) \ ? 32 \ : 31 \ : 30 \ : ((v) & ~0x7) == 0 \ ? 29 \ : 28 \ : ((v) & ~0x3f) == 0 \ ? ((v) & ~0x1f) == 0 \ ? 27 \ : 26 \ : ((v) & ~0x7f) == 0 \ ? 25 \ : 24 \ : ((v) & ~0xfff) == 0 \ ? ((v) & ~0x3ff) == 0 \ ? ((v) & ~0x1ff) == 0 \ ? 23 \ : 22 \ : ((v) & ~0x7ff) == 0 \ ? 21 \ : 20 \ : ((v) & ~0x3fff) == 0 \ ? ((v) & ~0x1fff) == 0 \ ? 19 \ : 18 \ : ((v) & ~0x7fff) == 0 \ ? 17 \ : 16 \ : ((v) & ~0xffffff) == 0 \ ? ((v) & ~0xfffff) == 0 \ ? ((v) & ~0x3ffff) == 0 \ ? ((v) & ~0x1ffff) == 0 \ ? 15 \ : 14 \ : ((v) & ~0x7ffff) == 0 \ ? 13 \ : 12 \ : ((v) & ~0x3fffff) == 0 \ ? ((v) & ~0x1fffff) == 0 \ ? 11 \ : 10 \ : ((v) & ~0x7fffff) == 0 \ ? 9 \ : 8 \ : ((v) & ~0xfffffff) == 0 \ ? ((v) & ~0x3ffffff) == 0 \ ? ((v) & ~0x1ffffff) == 0 \ ? 7 \ : 6 \ : ((v) & ~0x7ffffff) == 0 \ ? 5 \ : 4 \ : ((v) & ~0x3fffffff) == 0 \ ? ((v) & ~0x1fffffff) == 0 \ ? 3 \ : 2 \ : ((v) & ~0x7fffffff) == 0 \ ? 1 \ : 0) /* load_register() * This routine generates the least number of instructions necessary to load * an absolute expression value into a register. */ static void load_register (int reg, expressionS *ep, int dbl) { int freg; expressionS hi32, lo32; if (ep->X_op != O_big) { assert (ep->X_op == O_constant); /* Sign-extending 32-bit constants makes their handling easier. */ if (!dbl) normalize_constant_expr (ep); if (IS_SEXT_16BIT_NUM (ep->X_add_number)) { /* We can handle 16 bit signed values with an addiu to $zero. No need to ever use daddiu here, since $zero and the result are always correct in 32 bit mode. */ macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16); return; } else if (ep->X_add_number >= 0 && ep->X_add_number < 0x10000) { /* We can handle 16 bit unsigned values with an ori to $zero. */ macro_build (ep, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16); return; } else if ((IS_SEXT_32BIT_NUM (ep->X_add_number))) { /* 32 bit values require an lui. */ macro_build (ep, "lui", "t,u", reg, BFD_RELOC_HI16); if ((ep->X_add_number & 0xffff) != 0) macro_build (ep, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16); return; } } /* The value is larger than 32 bits. */ if (!dbl || HAVE_32BIT_GPRS) { char value[32]; sprintf_vma (value, ep->X_add_number); as_bad (_("Number (0x%s) larger than 32 bits"), value); macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16); return; } if (ep->X_op != O_big) { hi32 = *ep; hi32.X_add_number = (valueT) hi32.X_add_number >> 16; hi32.X_add_number = (valueT) hi32.X_add_number >> 16; hi32.X_add_number &= 0xffffffff; lo32 = *ep; lo32.X_add_number &= 0xffffffff; } else { assert (ep->X_add_number > 2); if (ep->X_add_number == 3) generic_bignum[3] = 0; else if (ep->X_add_number > 4) as_bad (_("Number larger than 64 bits")); lo32.X_op = O_constant; lo32.X_add_number = generic_bignum[0] + (generic_bignum[1] << 16); hi32.X_op = O_constant; hi32.X_add_number = generic_bignum[2] + (generic_bignum[3] << 16); } if (hi32.X_add_number == 0) freg = 0; else { int shift, bit; unsigned long hi, lo; if (hi32.X_add_number == (offsetT) 0xffffffff) { if ((lo32.X_add_number & 0xffff8000) == 0xffff8000) { macro_build (&lo32, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16); return; } if (lo32.X_add_number & 0x80000000) { macro_build (&lo32, "lui", "t,u", reg, BFD_RELOC_HI16); if (lo32.X_add_number & 0xffff) macro_build (&lo32, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16); return; } } /* Check for 16bit shifted constant. We know that hi32 is non-zero, so start the mask on the first bit of the hi32 value. */ shift = 17; do { unsigned long himask, lomask; if (shift < 32) { himask = 0xffff >> (32 - shift); lomask = (0xffff << shift) & 0xffffffff; } else { himask = 0xffff << (shift - 32); lomask = 0; } if ((hi32.X_add_number & ~(offsetT) himask) == 0 && (lo32.X_add_number & ~(offsetT) lomask) == 0) { expressionS tmp; tmp.X_op = O_constant; if (shift < 32) tmp.X_add_number = ((hi32.X_add_number << (32 - shift)) | (lo32.X_add_number >> shift)); else tmp.X_add_number = hi32.X_add_number >> (shift - 32); macro_build (&tmp, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16); macro_build (NULL, (shift >= 32) ? "dsll32" : "dsll", "d,w,<", reg, reg, (shift >= 32) ? shift - 32 : shift); return; } ++shift; } while (shift <= (64 - 16)); /* Find the bit number of the lowest one bit, and store the shifted value in hi/lo. */ hi = (unsigned long) (hi32.X_add_number & 0xffffffff); lo = (unsigned long) (lo32.X_add_number & 0xffffffff); if (lo != 0) { bit = 0; while ((lo & 1) == 0) { lo >>= 1; ++bit; } lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit); hi >>= bit; } else { bit = 32; while ((hi & 1) == 0) { hi >>= 1; ++bit; } lo = hi; hi = 0; } /* Optimize if the shifted value is a (power of 2) - 1. */ if ((hi == 0 && ((lo + 1) & lo) == 0) || (lo == 0xffffffff && ((hi + 1) & hi) == 0)) { shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number); if (shift != 0) { expressionS tmp; /* This instruction will set the register to be all ones. */ tmp.X_op = O_constant; tmp.X_add_number = (offsetT) -1; macro_build (&tmp, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16); if (bit != 0) { bit += shift; macro_build (NULL, (bit >= 32) ? "dsll32" : "dsll", "d,w,<", reg, reg, (bit >= 32) ? bit - 32 : bit); } macro_build (NULL, (shift >= 32) ? "dsrl32" : "dsrl", "d,w,<", reg, reg, (shift >= 32) ? shift - 32 : shift); return; } } /* Sign extend hi32 before calling load_register, because we can generally get better code when we load a sign extended value. */ if ((hi32.X_add_number & 0x80000000) != 0) hi32.X_add_number |= ~(offsetT) 0xffffffff; load_register (reg, &hi32, 0); freg = reg; } if ((lo32.X_add_number & 0xffff0000) == 0) { if (freg != 0) { macro_build (NULL, "dsll32", "d,w,<", reg, freg, 0); freg = reg; } } else { expressionS mid16; if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff)) { macro_build (&lo32, "lui", "t,u", reg, BFD_RELOC_HI16); macro_build (NULL, "dsrl32", "d,w,<", reg, reg, 0); return; } if (freg != 0) { macro_build (NULL, "dsll", "d,w,<", reg, freg, 16); freg = reg; } mid16 = lo32; mid16.X_add_number >>= 16; macro_build (&mid16, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16); macro_build (NULL, "dsll", "d,w,<", reg, reg, 16); freg = reg; } if ((lo32.X_add_number & 0xffff) != 0) macro_build (&lo32, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16); } static inline void load_delay_nop (void) { if (!gpr_interlocks) macro_build (NULL, "nop", ""); } /* Load an address into a register. */ static void load_address (int reg, expressionS *ep, int *used_at) { if (ep->X_op != O_constant && ep->X_op != O_symbol) { as_bad (_("expression too complex")); ep->X_op = O_constant; } if (ep->X_op == O_constant) { load_register (reg, ep, HAVE_64BIT_ADDRESSES); return; } if (mips_pic == NO_PIC) { /* If this is a reference to a GP relative symbol, we want addiu $reg,$gp, (BFD_RELOC_GPREL16) Otherwise we want lui $reg, (BFD_RELOC_HI16_S) addiu $reg,$reg, (BFD_RELOC_LO16) If we have an addend, we always use the latter form. With 64bit address space and a usable $at we want lui $reg, (BFD_RELOC_MIPS_HIGHEST) lui $at, (BFD_RELOC_HI16_S) daddiu $reg, (BFD_RELOC_MIPS_HIGHER) daddiu $at, (BFD_RELOC_LO16) dsll32 $reg,0 daddu $reg,$reg,$at If $at is already in use, we use a path which is suboptimal on superscalar processors. lui $reg, (BFD_RELOC_MIPS_HIGHEST) daddiu $reg, (BFD_RELOC_MIPS_HIGHER) dsll $reg,16 daddiu $reg, (BFD_RELOC_HI16_S) dsll $reg,16 daddiu $reg, (BFD_RELOC_LO16) For GP relative symbols in 64bit address space we can use the same sequence as in 32bit address space. */ if (HAVE_64BIT_SYMBOLS) { if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET && !nopic_need_relax (ep->X_add_symbol, 1)) { relax_start (ep->X_add_symbol); macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, mips_gp_register, BFD_RELOC_GPREL16); relax_switch (); } if (*used_at == 0 && !mips_opts.noat) { macro_build (ep, "lui", "t,u", reg, BFD_RELOC_MIPS_HIGHEST); macro_build (ep, "lui", "t,u", AT, BFD_RELOC_HI16_S); macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_MIPS_HIGHER); macro_build (ep, "daddiu", "t,r,j", AT, AT, BFD_RELOC_LO16); macro_build (NULL, "dsll32", "d,w,<", reg, reg, 0); macro_build (NULL, "daddu", "d,v,t", reg, reg, AT); *used_at = 1; } else { macro_build (ep, "lui", "t,u", reg, BFD_RELOC_MIPS_HIGHEST); macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_MIPS_HIGHER); macro_build (NULL, "dsll", "d,w,<", reg, reg, 16); macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_HI16_S); macro_build (NULL, "dsll", "d,w,<", reg, reg, 16); macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_LO16); } if (mips_relax.sequence) relax_end (); } else { if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET && !nopic_need_relax (ep->X_add_symbol, 1)) { relax_start (ep->X_add_symbol); macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, mips_gp_register, BFD_RELOC_GPREL16); relax_switch (); } macro_build_lui (ep, reg); macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg, BFD_RELOC_LO16); if (mips_relax.sequence) relax_end (); } } else if (!mips_big_got) { expressionS ex; /* If this is a reference to an external symbol, we want lw $reg,($gp) (BFD_RELOC_MIPS_GOT16) Otherwise we want lw $reg,($gp) (BFD_RELOC_MIPS_GOT16) nop addiu $reg,$reg, (BFD_RELOC_LO16) If there is a constant, it must be added in after. If we have NewABI, we want lw $reg,($gp) (BFD_RELOC_MIPS_GOT_DISP) unless we're referencing a global symbol with a non-zero offset, in which case cst must be added separately. */ if (HAVE_NEWABI) { if (ep->X_add_number) { ex.X_add_number = ep->X_add_number; ep->X_add_number = 0; relax_start (ep->X_add_symbol); macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg, BFD_RELOC_MIPS_GOT_DISP, mips_gp_register); if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000) as_bad (_("PIC code offset overflow (max 16 signed bits)")); ex.X_op = O_constant; macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg, BFD_RELOC_LO16); ep->X_add_number = ex.X_add_number; relax_switch (); } macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg, BFD_RELOC_MIPS_GOT_DISP, mips_gp_register); if (mips_relax.sequence) relax_end (); } else { ex.X_add_number = ep->X_add_number; ep->X_add_number = 0; macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg, BFD_RELOC_MIPS_GOT16, mips_gp_register); load_delay_nop (); relax_start (ep->X_add_symbol); relax_switch (); macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg, BFD_RELOC_LO16); relax_end (); if (ex.X_add_number != 0) { if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000) as_bad (_("PIC code offset overflow (max 16 signed bits)")); ex.X_op = O_constant; macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg, BFD_RELOC_LO16); } } } else if (mips_big_got) { expressionS ex; /* This is the large GOT case. If this is a reference to an external symbol, we want lui $reg, (BFD_RELOC_MIPS_GOT_HI16) addu $reg,$reg,$gp lw $reg,($reg) (BFD_RELOC_MIPS_GOT_LO16) Otherwise, for a reference to a local symbol in old ABI, we want lw $reg,($gp) (BFD_RELOC_MIPS_GOT16) nop addiu $reg,$reg, (BFD_RELOC_LO16) If there is a constant, it must be added in after. In the NewABI, for local symbols, with or without offsets, we want: lw $reg,($gp) (BFD_RELOC_MIPS_GOT_PAGE) addiu $reg,$reg, (BFD_RELOC_MIPS_GOT_OFST) */ if (HAVE_NEWABI) { ex.X_add_number = ep->X_add_number; ep->X_add_number = 0; relax_start (ep->X_add_symbol); macro_build (ep, "lui", "t,u", reg, BFD_RELOC_MIPS_GOT_HI16); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", reg, reg, mips_gp_register); macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg, BFD_RELOC_MIPS_GOT_LO16, reg); if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000) as_bad (_("PIC code offset overflow (max 16 signed bits)")); else if (ex.X_add_number) { ex.X_op = O_constant; macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg, BFD_RELOC_LO16); } ep->X_add_number = ex.X_add_number; relax_switch (); macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg, BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register); macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg, BFD_RELOC_MIPS_GOT_OFST); relax_end (); } else { ex.X_add_number = ep->X_add_number; ep->X_add_number = 0; relax_start (ep->X_add_symbol); macro_build (ep, "lui", "t,u", reg, BFD_RELOC_MIPS_GOT_HI16); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", reg, reg, mips_gp_register); macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg, BFD_RELOC_MIPS_GOT_LO16, reg); relax_switch (); if (reg_needs_delay (mips_gp_register)) { /* We need a nop before loading from $gp. This special check is required because the lui which starts the main instruction stream does not refer to $gp, and so will not insert the nop which may be required. */ macro_build (NULL, "nop", ""); } macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg, BFD_RELOC_MIPS_GOT16, mips_gp_register); load_delay_nop (); macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg, BFD_RELOC_LO16); relax_end (); if (ex.X_add_number != 0) { if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000) as_bad (_("PIC code offset overflow (max 16 signed bits)")); ex.X_op = O_constant; macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg, BFD_RELOC_LO16); } } } else abort (); if (mips_opts.noat && *used_at == 1) as_bad (_("Macro used $at after \".set noat\"")); } /* Move the contents of register SOURCE into register DEST. */ static void move_register (int dest, int source) { macro_build (NULL, HAVE_32BIT_GPRS ? "addu" : "daddu", "d,v,t", dest, source, 0); } /* Emit an SVR4 PIC sequence to load address LOCAL into DEST, where LOCAL is the sum of a symbol and a 16-bit or 32-bit displacement. The two alternatives are: Global symbol Local sybmol ------------- ------------ lw DEST,%got(SYMBOL) lw DEST,%got(SYMBOL + OFFSET) ... ... addiu DEST,DEST,OFFSET addiu DEST,DEST,%lo(SYMBOL + OFFSET) load_got_offset emits the first instruction and add_got_offset emits the second for a 16-bit offset or add_got_offset_hilo emits a sequence to add a 32-bit offset using a scratch register. */ static void load_got_offset (int dest, expressionS *local) { expressionS global; global = *local; global.X_add_number = 0; relax_start (local->X_add_symbol); macro_build (&global, ADDRESS_LOAD_INSN, "t,o(b)", dest, BFD_RELOC_MIPS_GOT16, mips_gp_register); relax_switch (); macro_build (local, ADDRESS_LOAD_INSN, "t,o(b)", dest, BFD_RELOC_MIPS_GOT16, mips_gp_register); relax_end (); } static void add_got_offset (int dest, expressionS *local) { expressionS global; global.X_op = O_constant; global.X_op_symbol = NULL; global.X_add_symbol = NULL; global.X_add_number = local->X_add_number; relax_start (local->X_add_symbol); macro_build (&global, ADDRESS_ADDI_INSN, "t,r,j", dest, dest, BFD_RELOC_LO16); relax_switch (); macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", dest, dest, BFD_RELOC_LO16); relax_end (); } static void add_got_offset_hilo (int dest, expressionS *local, int tmp) { expressionS global; int hold_mips_optimize; global.X_op = O_constant; global.X_op_symbol = NULL; global.X_add_symbol = NULL; global.X_add_number = local->X_add_number; relax_start (local->X_add_symbol); load_register (tmp, &global, HAVE_64BIT_ADDRESSES); relax_switch (); /* Set mips_optimize around the lui instruction to avoid inserting an unnecessary nop after the lw. */ hold_mips_optimize = mips_optimize; mips_optimize = 2; macro_build_lui (&global, tmp); mips_optimize = hold_mips_optimize; macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", tmp, tmp, BFD_RELOC_LO16); relax_end (); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp); } /* * Build macros * This routine implements the seemingly endless macro or synthesized * instructions and addressing modes in the mips assembly language. Many * of these macros are simple and are similar to each other. These could * probably be handled by some kind of table or grammar approach instead of * this verbose method. Others are not simple macros but are more like * optimizing code generation. * One interesting optimization is when several store macros appear * consecutively that would load AT with the upper half of the same address. * The ensuing load upper instructions are ommited. This implies some kind * of global optimization. We currently only optimize within a single macro. * For many of the load and store macros if the address is specified as a * constant expression in the first 64k of memory (ie ld $2,0x4000c) we * first load register 'at' with zero and use it as the base register. The * mips assembler simply uses register $zero. Just one tiny optimization * we're missing. */ static void macro (struct mips_cl_insn *ip) { int treg, sreg, dreg, breg; int tempreg; int mask; int used_at = 0; expressionS expr1; const char *s; const char *s2; const char *fmt; int likely = 0; int dbl = 0; int coproc = 0; int lr = 0; int imm = 0; int call = 0; int off; offsetT maxnum; bfd_reloc_code_real_type r; int hold_mips_optimize; assert (! mips_opts.mips16); treg = (ip->insn_opcode >> 16) & 0x1f; dreg = (ip->insn_opcode >> 11) & 0x1f; sreg = breg = (ip->insn_opcode >> 21) & 0x1f; mask = ip->insn_mo->mask; expr1.X_op = O_constant; expr1.X_op_symbol = NULL; expr1.X_add_symbol = NULL; expr1.X_add_number = 1; switch (mask) { case M_DABS: dbl = 1; case M_ABS: /* bgez $a0,.+12 move v0,$a0 sub v0,$zero,$a0 */ start_noreorder (); expr1.X_add_number = 8; macro_build (&expr1, "bgez", "s,p", sreg); if (dreg == sreg) macro_build (NULL, "nop", "", 0); else move_register (dreg, sreg); macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, 0, sreg); end_noreorder (); break; case M_ADD_I: s = "addi"; s2 = "add"; goto do_addi; case M_ADDU_I: s = "addiu"; s2 = "addu"; goto do_addi; case M_DADD_I: dbl = 1; s = "daddi"; s2 = "dadd"; goto do_addi; case M_DADDU_I: dbl = 1; s = "daddiu"; s2 = "daddu"; do_addi: if (imm_expr.X_op == O_constant && imm_expr.X_add_number >= -0x8000 && imm_expr.X_add_number < 0x8000) { macro_build (&imm_expr, s, "t,r,j", treg, sreg, BFD_RELOC_LO16); break; } used_at = 1; load_register (AT, &imm_expr, dbl); macro_build (NULL, s2, "d,v,t", treg, sreg, AT); break; case M_AND_I: s = "andi"; s2 = "and"; goto do_bit; case M_OR_I: s = "ori"; s2 = "or"; goto do_bit; case M_NOR_I: s = ""; s2 = "nor"; goto do_bit; case M_XOR_I: s = "xori"; s2 = "xor"; do_bit: if (imm_expr.X_op == O_constant && imm_expr.X_add_number >= 0 && imm_expr.X_add_number < 0x10000) { if (mask != M_NOR_I) macro_build (&imm_expr, s, "t,r,i", treg, sreg, BFD_RELOC_LO16); else { macro_build (&imm_expr, "ori", "t,r,i", treg, sreg, BFD_RELOC_LO16); macro_build (NULL, "nor", "d,v,t", treg, treg, 0); } break; } used_at = 1; load_register (AT, &imm_expr, HAVE_64BIT_GPRS); macro_build (NULL, s2, "d,v,t", treg, sreg, AT); break; case M_BALIGN: switch (imm_expr.X_add_number) { case 0: macro_build (NULL, "nop", ""); break; case 2: macro_build (NULL, "packrl.ph", "d,s,t", treg, treg, sreg); break; default: macro_build (NULL, "balign", "t,s,2", treg, sreg, (int)imm_expr.X_add_number); break; } break; case M_BEQ_I: s = "beq"; goto beq_i; case M_BEQL_I: s = "beql"; likely = 1; goto beq_i; case M_BNE_I: s = "bne"; goto beq_i; case M_BNEL_I: s = "bnel"; likely = 1; beq_i: if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0) { macro_build (&offset_expr, s, "s,t,p", sreg, 0); break; } used_at = 1; load_register (AT, &imm_expr, HAVE_64BIT_GPRS); macro_build (&offset_expr, s, "s,t,p", sreg, AT); break; case M_BGEL: likely = 1; case M_BGE: if (treg == 0) { macro_build (&offset_expr, likely ? "bgezl" : "bgez", "s,p", sreg); break; } if (sreg == 0) { macro_build (&offset_expr, likely ? "blezl" : "blez", "s,p", treg); break; } used_at = 1; macro_build (NULL, "slt", "d,v,t", AT, sreg, treg); macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0); break; case M_BGTL_I: likely = 1; case M_BGT_I: /* check for > max integer */ maxnum = 0x7fffffff; if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4) { maxnum <<= 16; maxnum |= 0xffff; maxnum <<= 16; maxnum |= 0xffff; } if (imm_expr.X_op == O_constant && imm_expr.X_add_number >= maxnum && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4)) { do_false: /* result is always false */ if (! likely) macro_build (NULL, "nop", "", 0); else macro_build (&offset_expr, "bnel", "s,t,p", 0, 0); break; } if (imm_expr.X_op != O_constant) as_bad (_("Unsupported large constant")); ++imm_expr.X_add_number; /* FALLTHROUGH */ case M_BGE_I: case M_BGEL_I: if (mask == M_BGEL_I) likely = 1; if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0) { macro_build (&offset_expr, likely ? "bgezl" : "bgez", "s,p", sreg); break; } if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1) { macro_build (&offset_expr, likely ? "bgtzl" : "bgtz", "s,p", sreg); break; } maxnum = 0x7fffffff; if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4) { maxnum <<= 16; maxnum |= 0xffff; maxnum <<= 16; maxnum |= 0xffff; } maxnum = - maxnum - 1; if (imm_expr.X_op == O_constant && imm_expr.X_add_number <= maxnum && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4)) { do_true: /* result is always true */ as_warn (_("Branch %s is always true"), ip->insn_mo->name); macro_build (&offset_expr, "b", "p"); break; } used_at = 1; set_at (sreg, 0); macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0); break; case M_BGEUL: likely = 1; case M_BGEU: if (treg == 0) goto do_true; if (sreg == 0) { macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", 0, treg); break; } used_at = 1; macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg); macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0); break; case M_BGTUL_I: likely = 1; case M_BGTU_I: if (sreg == 0 || (HAVE_32BIT_GPRS && imm_expr.X_op == O_constant && imm_expr.X_add_number == (offsetT) 0xffffffff)) goto do_false; if (imm_expr.X_op != O_constant) as_bad (_("Unsupported large constant")); ++imm_expr.X_add_number; /* FALLTHROUGH */ case M_BGEU_I: case M_BGEUL_I: if (mask == M_BGEUL_I) likely = 1; if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0) goto do_true; if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1) { macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", sreg, 0); break; } used_at = 1; set_at (sreg, 1); macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0); break; case M_BGTL: likely = 1; case M_BGT: if (treg == 0) { macro_build (&offset_expr, likely ? "bgtzl" : "bgtz", "s,p", sreg); break; } if (sreg == 0) { macro_build (&offset_expr, likely ? "bltzl" : "bltz", "s,p", treg); break; } used_at = 1; macro_build (NULL, "slt", "d,v,t", AT, treg, sreg); macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0); break; case M_BGTUL: likely = 1; case M_BGTU: if (treg == 0) { macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", sreg, 0); break; } if (sreg == 0) goto do_false; used_at = 1; macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg); macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0); break; case M_BLEL: likely = 1; case M_BLE: if (treg == 0) { macro_build (&offset_expr, likely ? "blezl" : "blez", "s,p", sreg); break; } if (sreg == 0) { macro_build (&offset_expr, likely ? "bgezl" : "bgez", "s,p", treg); break; } used_at = 1; macro_build (NULL, "slt", "d,v,t", AT, treg, sreg); macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0); break; case M_BLEL_I: likely = 1; case M_BLE_I: maxnum = 0x7fffffff; if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4) { maxnum <<= 16; maxnum |= 0xffff; maxnum <<= 16; maxnum |= 0xffff; } if (imm_expr.X_op == O_constant && imm_expr.X_add_number >= maxnum && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4)) goto do_true; if (imm_expr.X_op != O_constant) as_bad (_("Unsupported large constant")); ++imm_expr.X_add_number; /* FALLTHROUGH */ case M_BLT_I: case M_BLTL_I: if (mask == M_BLTL_I) likely = 1; if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0) { macro_build (&offset_expr, likely ? "bltzl" : "bltz", "s,p", sreg); break; } if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1) { macro_build (&offset_expr, likely ? "blezl" : "blez", "s,p", sreg); break; } used_at = 1; set_at (sreg, 0); macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0); break; case M_BLEUL: likely = 1; case M_BLEU: if (treg == 0) { macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", sreg, 0); break; } if (sreg == 0) goto do_true; used_at = 1; macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg); macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0); break; case M_BLEUL_I: likely = 1; case M_BLEU_I: if (sreg == 0 || (HAVE_32BIT_GPRS && imm_expr.X_op == O_constant && imm_expr.X_add_number == (offsetT) 0xffffffff)) goto do_true; if (imm_expr.X_op != O_constant) as_bad (_("Unsupported large constant")); ++imm_expr.X_add_number; /* FALLTHROUGH */ case M_BLTU_I: case M_BLTUL_I: if (mask == M_BLTUL_I) likely = 1; if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0) goto do_false; if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1) { macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", sreg, 0); break; } used_at = 1; set_at (sreg, 1); macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0); break; case M_BLTL: likely = 1; case M_BLT: if (treg == 0) { macro_build (&offset_expr, likely ? "bltzl" : "bltz", "s,p", sreg); break; } if (sreg == 0) { macro_build (&offset_expr, likely ? "bgtzl" : "bgtz", "s,p", treg); break; } used_at = 1; macro_build (NULL, "slt", "d,v,t", AT, sreg, treg); macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0); break; case M_BLTUL: likely = 1; case M_BLTU: if (treg == 0) goto do_false; if (sreg == 0) { macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", 0, treg); break; } used_at = 1; macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg); macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0); break; case M_DEXT: { unsigned long pos; unsigned long size; if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant) { as_bad (_("Unsupported large constant")); pos = size = 1; } else { pos = (unsigned long) imm_expr.X_add_number; size = (unsigned long) imm2_expr.X_add_number; } if (pos > 63) { as_bad (_("Improper position (%lu)"), pos); pos = 1; } if (size == 0 || size > 64 || (pos + size - 1) > 63) { as_bad (_("Improper extract size (%lu, position %lu)"), size, pos); size = 1; } if (size <= 32 && pos < 32) { s = "dext"; fmt = "t,r,+A,+C"; } else if (size <= 32) { s = "dextu"; fmt = "t,r,+E,+H"; } else { s = "dextm"; fmt = "t,r,+A,+G"; } macro_build ((expressionS *) NULL, s, fmt, treg, sreg, pos, size - 1); } break; case M_DINS: { unsigned long pos; unsigned long size; if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant) { as_bad (_("Unsupported large constant")); pos = size = 1; } else { pos = (unsigned long) imm_expr.X_add_number; size = (unsigned long) imm2_expr.X_add_number; } if (pos > 63) { as_bad (_("Improper position (%lu)"), pos); pos = 1; } if (size == 0 || size > 64 || (pos + size - 1) > 63) { as_bad (_("Improper insert size (%lu, position %lu)"), size, pos); size = 1; } if (pos < 32 && (pos + size - 1) < 32) { s = "dins"; fmt = "t,r,+A,+B"; } else if (pos >= 32) { s = "dinsu"; fmt = "t,r,+E,+F"; } else { s = "dinsm"; fmt = "t,r,+A,+F"; } macro_build ((expressionS *) NULL, s, fmt, treg, sreg, pos, pos + size - 1); } break; case M_DDIV_3: dbl = 1; case M_DIV_3: s = "mflo"; goto do_div3; case M_DREM_3: dbl = 1; case M_REM_3: s = "mfhi"; do_div3: if (treg == 0) { as_warn (_("Divide by zero.")); if (mips_trap) macro_build (NULL, "teq", "s,t,q", 0, 0, 7); else macro_build (NULL, "break", "c", 7); break; } start_noreorder (); if (mips_trap) { macro_build (NULL, "teq", "s,t,q", treg, 0, 7); macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg); } else { expr1.X_add_number = 8; macro_build (&expr1, "bne", "s,t,p", treg, 0); macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg); macro_build (NULL, "break", "c", 7); } expr1.X_add_number = -1; used_at = 1; load_register (AT, &expr1, dbl); expr1.X_add_number = mips_trap ? (dbl ? 12 : 8) : (dbl ? 20 : 16); macro_build (&expr1, "bne", "s,t,p", treg, AT); if (dbl) { expr1.X_add_number = 1; load_register (AT, &expr1, dbl); macro_build (NULL, "dsll32", "d,w,<", AT, AT, 31); } else { expr1.X_add_number = 0x80000000; macro_build (&expr1, "lui", "t,u", AT, BFD_RELOC_HI16); } if (mips_trap) { macro_build (NULL, "teq", "s,t,q", sreg, AT, 6); /* We want to close the noreorder block as soon as possible, so that later insns are available for delay slot filling. */ end_noreorder (); } else { expr1.X_add_number = 8; macro_build (&expr1, "bne", "s,t,p", sreg, AT); macro_build (NULL, "nop", "", 0); /* We want to close the noreorder block as soon as possible, so that later insns are available for delay slot filling. */ end_noreorder (); macro_build (NULL, "break", "c", 6); } macro_build (NULL, s, "d", dreg); break; case M_DIV_3I: s = "div"; s2 = "mflo"; goto do_divi; case M_DIVU_3I: s = "divu"; s2 = "mflo"; goto do_divi; case M_REM_3I: s = "div"; s2 = "mfhi"; goto do_divi; case M_REMU_3I: s = "divu"; s2 = "mfhi"; goto do_divi; case M_DDIV_3I: dbl = 1; s = "ddiv"; s2 = "mflo"; goto do_divi; case M_DDIVU_3I: dbl = 1; s = "ddivu"; s2 = "mflo"; goto do_divi; case M_DREM_3I: dbl = 1; s = "ddiv"; s2 = "mfhi"; goto do_divi; case M_DREMU_3I: dbl = 1; s = "ddivu"; s2 = "mfhi"; do_divi: if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0) { as_warn (_("Divide by zero.")); if (mips_trap) macro_build (NULL, "teq", "s,t,q", 0, 0, 7); else macro_build (NULL, "break", "c", 7); break; } if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1) { if (strcmp (s2, "mflo") == 0) move_register (dreg, sreg); else move_register (dreg, 0); break; } if (imm_expr.X_op == O_constant && imm_expr.X_add_number == -1 && s[strlen (s) - 1] != 'u') { if (strcmp (s2, "mflo") == 0) { macro_build (NULL, dbl ? "dneg" : "neg", "d,w", dreg, sreg); } else move_register (dreg, 0); break; } used_at = 1; load_register (AT, &imm_expr, dbl); macro_build (NULL, s, "z,s,t", sreg, AT); macro_build (NULL, s2, "d", dreg); break; case M_DIVU_3: s = "divu"; s2 = "mflo"; goto do_divu3; case M_REMU_3: s = "divu"; s2 = "mfhi"; goto do_divu3; case M_DDIVU_3: s = "ddivu"; s2 = "mflo"; goto do_divu3; case M_DREMU_3: s = "ddivu"; s2 = "mfhi"; do_divu3: start_noreorder (); if (mips_trap) { macro_build (NULL, "teq", "s,t,q", treg, 0, 7); macro_build (NULL, s, "z,s,t", sreg, treg); /* We want to close the noreorder block as soon as possible, so that later insns are available for delay slot filling. */ end_noreorder (); } else { expr1.X_add_number = 8; macro_build (&expr1, "bne", "s,t,p", treg, 0); macro_build (NULL, s, "z,s,t", sreg, treg); /* We want to close the noreorder block as soon as possible, so that later insns are available for delay slot filling. */ end_noreorder (); macro_build (NULL, "break", "c", 7); } macro_build (NULL, s2, "d", dreg); break; case M_DLCA_AB: dbl = 1; case M_LCA_AB: call = 1; goto do_la; case M_DLA_AB: dbl = 1; case M_LA_AB: do_la: /* Load the address of a symbol into a register. If breg is not zero, we then add a base register to it. */ if (dbl && HAVE_32BIT_GPRS) as_warn (_("dla used to load 32-bit register")); if (! dbl && HAVE_64BIT_OBJECTS) as_warn (_("la used to load 64-bit address")); if (offset_expr.X_op == O_constant && offset_expr.X_add_number >= -0x8000 && offset_expr.X_add_number < 0x8000) { macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", treg, sreg, BFD_RELOC_LO16); break; } if (!mips_opts.noat && (treg == breg)) { tempreg = AT; used_at = 1; } else { tempreg = treg; } if (offset_expr.X_op != O_symbol && offset_expr.X_op != O_constant) { as_bad (_("expression too complex")); offset_expr.X_op = O_constant; } if (offset_expr.X_op == O_constant) load_register (tempreg, &offset_expr, HAVE_64BIT_ADDRESSES); else if (mips_pic == NO_PIC) { /* If this is a reference to a GP relative symbol, we want addiu $tempreg,$gp, (BFD_RELOC_GPREL16) Otherwise we want lui $tempreg, (BFD_RELOC_HI16_S) addiu $tempreg,$tempreg, (BFD_RELOC_LO16) If we have a constant, we need two instructions anyhow, so we may as well always use the latter form. With 64bit address space and a usable $at we want lui $tempreg, (BFD_RELOC_MIPS_HIGHEST) lui $at, (BFD_RELOC_HI16_S) daddiu $tempreg, (BFD_RELOC_MIPS_HIGHER) daddiu $at, (BFD_RELOC_LO16) dsll32 $tempreg,0 daddu $tempreg,$tempreg,$at If $at is already in use, we use a path which is suboptimal on superscalar processors. lui $tempreg, (BFD_RELOC_MIPS_HIGHEST) daddiu $tempreg, (BFD_RELOC_MIPS_HIGHER) dsll $tempreg,16 daddiu $tempreg, (BFD_RELOC_HI16_S) dsll $tempreg,16 daddiu $tempreg, (BFD_RELOC_LO16) For GP relative symbols in 64bit address space we can use the same sequence as in 32bit address space. */ if (HAVE_64BIT_SYMBOLS) { if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET && !nopic_need_relax (offset_expr.X_add_symbol, 1)) { relax_start (offset_expr.X_add_symbol); macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg, mips_gp_register, BFD_RELOC_GPREL16); relax_switch (); } if (used_at == 0 && !mips_opts.noat) { macro_build (&offset_expr, "lui", "t,u", tempreg, BFD_RELOC_MIPS_HIGHEST); macro_build (&offset_expr, "lui", "t,u", AT, BFD_RELOC_HI16_S); macro_build (&offset_expr, "daddiu", "t,r,j", tempreg, tempreg, BFD_RELOC_MIPS_HIGHER); macro_build (&offset_expr, "daddiu", "t,r,j", AT, AT, BFD_RELOC_LO16); macro_build (NULL, "dsll32", "d,w,<", tempreg, tempreg, 0); macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT); used_at = 1; } else { macro_build (&offset_expr, "lui", "t,u", tempreg, BFD_RELOC_MIPS_HIGHEST); macro_build (&offset_expr, "daddiu", "t,r,j", tempreg, tempreg, BFD_RELOC_MIPS_HIGHER); macro_build (NULL, "dsll", "d,w,<", tempreg, tempreg, 16); macro_build (&offset_expr, "daddiu", "t,r,j", tempreg, tempreg, BFD_RELOC_HI16_S); macro_build (NULL, "dsll", "d,w,<", tempreg, tempreg, 16); macro_build (&offset_expr, "daddiu", "t,r,j", tempreg, tempreg, BFD_RELOC_LO16); } if (mips_relax.sequence) relax_end (); } else { if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET && !nopic_need_relax (offset_expr.X_add_symbol, 1)) { relax_start (offset_expr.X_add_symbol); macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg, mips_gp_register, BFD_RELOC_GPREL16); relax_switch (); } if (!IS_SEXT_32BIT_NUM (offset_expr.X_add_number)) as_bad (_("offset too large")); macro_build_lui (&offset_expr, tempreg); macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg, tempreg, BFD_RELOC_LO16); if (mips_relax.sequence) relax_end (); } } else if (!mips_big_got && !HAVE_NEWABI) { int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16; /* If this is a reference to an external symbol, and there is no constant, we want lw $tempreg,($gp) (BFD_RELOC_MIPS_GOT16) or for lca or if tempreg is PIC_CALL_REG lw $tempreg,($gp) (BFD_RELOC_MIPS_CALL16) For a local symbol, we want lw $tempreg,($gp) (BFD_RELOC_MIPS_GOT16) nop addiu $tempreg,$tempreg, (BFD_RELOC_LO16) If we have a small constant, and this is a reference to an external symbol, we want lw $tempreg,($gp) (BFD_RELOC_MIPS_GOT16) nop addiu $tempreg,$tempreg, For a local symbol, we want the same instruction sequence, but we output a BFD_RELOC_LO16 reloc on the addiu instruction. If we have a large constant, and this is a reference to an external symbol, we want lw $tempreg,($gp) (BFD_RELOC_MIPS_GOT16) lui $at, addiu $at,$at, addu $tempreg,$tempreg,$at For a local symbol, we want the same instruction sequence, but we output a BFD_RELOC_LO16 reloc on the addiu instruction. */ if (offset_expr.X_add_number == 0) { if (mips_pic == SVR4_PIC && breg == 0 && (call || tempreg == PIC_CALL_REG)) lw_reloc_type = (int) BFD_RELOC_MIPS_CALL16; relax_start (offset_expr.X_add_symbol); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, lw_reloc_type, mips_gp_register); if (breg != 0) { /* We're going to put in an addu instruction using tempreg, so we may as well insert the nop right now. */ load_delay_nop (); } relax_switch (); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, BFD_RELOC_MIPS_GOT16, mips_gp_register); load_delay_nop (); macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg, tempreg, BFD_RELOC_LO16); relax_end (); /* FIXME: If breg == 0, and the next instruction uses $tempreg, then if this variant case is used an extra nop will be generated. */ } else if (offset_expr.X_add_number >= -0x8000 && offset_expr.X_add_number < 0x8000) { load_got_offset (tempreg, &offset_expr); load_delay_nop (); add_got_offset (tempreg, &offset_expr); } else { expr1.X_add_number = offset_expr.X_add_number; offset_expr.X_add_number = ((offset_expr.X_add_number + 0x8000) & 0xffff) - 0x8000; load_got_offset (tempreg, &offset_expr); offset_expr.X_add_number = expr1.X_add_number; /* If we are going to add in a base register, and the target register and the base register are the same, then we are using AT as a temporary register. Since we want to load the constant into AT, we add our current AT (from the global offset table) and the register into the register now, and pretend we were not using a base register. */ if (breg == treg) { load_delay_nop (); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, AT, breg); breg = 0; tempreg = treg; } add_got_offset_hilo (tempreg, &offset_expr, AT); used_at = 1; } } else if (!mips_big_got && HAVE_NEWABI) { int add_breg_early = 0; /* If this is a reference to an external, and there is no constant, or local symbol (*), with or without a constant, we want lw $tempreg,($gp) (BFD_RELOC_MIPS_GOT_DISP) or for lca or if tempreg is PIC_CALL_REG lw $tempreg,($gp) (BFD_RELOC_MIPS_CALL16) If we have a small constant, and this is a reference to an external symbol, we want lw $tempreg,($gp) (BFD_RELOC_MIPS_GOT_DISP) addiu $tempreg,$tempreg, If we have a large constant, and this is a reference to an external symbol, we want lw $tempreg,($gp) (BFD_RELOC_MIPS_GOT_DISP) lui $at, addiu $at,$at, addu $tempreg,$tempreg,$at (*) Other assemblers seem to prefer GOT_PAGE/GOT_OFST for local symbols, even though it introduces an additional instruction. */ if (offset_expr.X_add_number) { expr1.X_add_number = offset_expr.X_add_number; offset_expr.X_add_number = 0; relax_start (offset_expr.X_add_symbol); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, BFD_RELOC_MIPS_GOT_DISP, mips_gp_register); if (expr1.X_add_number >= -0x8000 && expr1.X_add_number < 0x8000) { macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j", tempreg, tempreg, BFD_RELOC_LO16); } else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000)) { int dreg; /* If we are going to add in a base register, and the target register and the base register are the same, then we are using AT as a temporary register. Since we want to load the constant into AT, we add our current AT (from the global offset table) and the register into the register now, and pretend we were not using a base register. */ if (breg != treg) dreg = tempreg; else { assert (tempreg == AT); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, AT, breg); dreg = treg; add_breg_early = 1; } load_register (AT, &expr1, HAVE_64BIT_ADDRESSES); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT); used_at = 1; } else as_bad (_("PIC code offset overflow (max 32 signed bits)")); relax_switch (); offset_expr.X_add_number = expr1.X_add_number; macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, BFD_RELOC_MIPS_GOT_DISP, mips_gp_register); if (add_breg_early) { macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg); breg = 0; tempreg = treg; } relax_end (); } else if (breg == 0 && (call || tempreg == PIC_CALL_REG)) { relax_start (offset_expr.X_add_symbol); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, BFD_RELOC_MIPS_CALL16, mips_gp_register); relax_switch (); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, BFD_RELOC_MIPS_GOT_DISP, mips_gp_register); relax_end (); } else { macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, BFD_RELOC_MIPS_GOT_DISP, mips_gp_register); } } else if (mips_big_got && !HAVE_NEWABI) { int gpdelay; int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16; int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16; int local_reloc_type = (int) BFD_RELOC_MIPS_GOT16; /* This is the large GOT case. If this is a reference to an external symbol, and there is no constant, we want lui $tempreg, (BFD_RELOC_MIPS_GOT_HI16) addu $tempreg,$tempreg,$gp lw $tempreg,($tempreg) (BFD_RELOC_MIPS_GOT_LO16) or for lca or if tempreg is PIC_CALL_REG lui $tempreg, (BFD_RELOC_MIPS_CALL_HI16) addu $tempreg,$tempreg,$gp lw $tempreg,($tempreg) (BFD_RELOC_MIPS_CALL_LO16) For a local symbol, we want lw $tempreg,($gp) (BFD_RELOC_MIPS_GOT16) nop addiu $tempreg,$tempreg, (BFD_RELOC_LO16) If we have a small constant, and this is a reference to an external symbol, we want lui $tempreg, (BFD_RELOC_MIPS_GOT_HI16) addu $tempreg,$tempreg,$gp lw $tempreg,($tempreg) (BFD_RELOC_MIPS_GOT_LO16) nop addiu $tempreg,$tempreg, For a local symbol, we want lw $tempreg,($gp) (BFD_RELOC_MIPS_GOT16) nop addiu $tempreg,$tempreg, (BFD_RELOC_LO16) If we have a large constant, and this is a reference to an external symbol, we want lui $tempreg, (BFD_RELOC_MIPS_GOT_HI16) addu $tempreg,$tempreg,$gp lw $tempreg,($tempreg) (BFD_RELOC_MIPS_GOT_LO16) lui $at, addiu $at,$at, addu $tempreg,$tempreg,$at For a local symbol, we want lw $tempreg,($gp) (BFD_RELOC_MIPS_GOT16) lui $at, addiu $at,$at, (BFD_RELOC_LO16) addu $tempreg,$tempreg,$at */ expr1.X_add_number = offset_expr.X_add_number; offset_expr.X_add_number = 0; relax_start (offset_expr.X_add_symbol); gpdelay = reg_needs_delay (mips_gp_register); if (expr1.X_add_number == 0 && breg == 0 && (call || tempreg == PIC_CALL_REG)) { lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16; lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16; } macro_build (&offset_expr, "lui", "t,u", tempreg, lui_reloc_type); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, mips_gp_register); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, lw_reloc_type, tempreg); if (expr1.X_add_number == 0) { if (breg != 0) { /* We're going to put in an addu instruction using tempreg, so we may as well insert the nop right now. */ load_delay_nop (); } } else if (expr1.X_add_number >= -0x8000 && expr1.X_add_number < 0x8000) { load_delay_nop (); macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j", tempreg, tempreg, BFD_RELOC_LO16); } else { int dreg; /* If we are going to add in a base register, and the target register and the base register are the same, then we are using AT as a temporary register. Since we want to load the constant into AT, we add our current AT (from the global offset table) and the register into the register now, and pretend we were not using a base register. */ if (breg != treg) dreg = tempreg; else { assert (tempreg == AT); load_delay_nop (); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, AT, breg); dreg = treg; } load_register (AT, &expr1, HAVE_64BIT_ADDRESSES); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT); used_at = 1; } offset_expr.X_add_number = ((expr1.X_add_number + 0x8000) & 0xffff) - 0x8000; relax_switch (); if (gpdelay) { /* This is needed because this instruction uses $gp, but the first instruction on the main stream does not. */ macro_build (NULL, "nop", ""); } macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, local_reloc_type, mips_gp_register); if (expr1.X_add_number >= -0x8000 && expr1.X_add_number < 0x8000) { load_delay_nop (); macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg, tempreg, BFD_RELOC_LO16); /* FIXME: If add_number is 0, and there was no base register, the external symbol case ended with a load, so if the symbol turns out to not be external, and the next instruction uses tempreg, an unnecessary nop will be inserted. */ } else { if (breg == treg) { /* We must add in the base register now, as in the external symbol case. */ assert (tempreg == AT); load_delay_nop (); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, AT, breg); tempreg = treg; /* We set breg to 0 because we have arranged to add it in in both cases. */ breg = 0; } macro_build_lui (&expr1, AT); macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", AT, AT, BFD_RELOC_LO16); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, AT); used_at = 1; } relax_end (); } else if (mips_big_got && HAVE_NEWABI) { int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16; int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16; int add_breg_early = 0; /* This is the large GOT case. If this is a reference to an external symbol, and there is no constant, we want lui $tempreg, (BFD_RELOC_MIPS_GOT_HI16) add $tempreg,$tempreg,$gp lw $tempreg,($tempreg) (BFD_RELOC_MIPS_GOT_LO16) or for lca or if tempreg is PIC_CALL_REG lui $tempreg, (BFD_RELOC_MIPS_CALL_HI16) add $tempreg,$tempreg,$gp lw $tempreg,($tempreg) (BFD_RELOC_MIPS_CALL_LO16) If we have a small constant, and this is a reference to an external symbol, we want lui $tempreg, (BFD_RELOC_MIPS_GOT_HI16) add $tempreg,$tempreg,$gp lw $tempreg,($tempreg) (BFD_RELOC_MIPS_GOT_LO16) addi $tempreg,$tempreg, If we have a large constant, and this is a reference to an external symbol, we want lui $tempreg, (BFD_RELOC_MIPS_GOT_HI16) addu $tempreg,$tempreg,$gp lw $tempreg,($tempreg) (BFD_RELOC_MIPS_GOT_LO16) lui $at, addi $at,$at, add $tempreg,$tempreg,$at If we have NewABI, and we know it's a local symbol, we want lw $reg,($gp) (BFD_RELOC_MIPS_GOT_PAGE) addiu $reg,$reg, (BFD_RELOC_MIPS_GOT_OFST) otherwise we have to resort to GOT_HI16/GOT_LO16. */ relax_start (offset_expr.X_add_symbol); expr1.X_add_number = offset_expr.X_add_number; offset_expr.X_add_number = 0; if (expr1.X_add_number == 0 && breg == 0 && (call || tempreg == PIC_CALL_REG)) { lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16; lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16; } macro_build (&offset_expr, "lui", "t,u", tempreg, lui_reloc_type); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, mips_gp_register); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, lw_reloc_type, tempreg); if (expr1.X_add_number == 0) ; else if (expr1.X_add_number >= -0x8000 && expr1.X_add_number < 0x8000) { macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j", tempreg, tempreg, BFD_RELOC_LO16); } else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000)) { int dreg; /* If we are going to add in a base register, and the target register and the base register are the same, then we are using AT as a temporary register. Since we want to load the constant into AT, we add our current AT (from the global offset table) and the register into the register now, and pretend we were not using a base register. */ if (breg != treg) dreg = tempreg; else { assert (tempreg == AT); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, AT, breg); dreg = treg; add_breg_early = 1; } load_register (AT, &expr1, HAVE_64BIT_ADDRESSES); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT); used_at = 1; } else as_bad (_("PIC code offset overflow (max 32 signed bits)")); relax_switch (); offset_expr.X_add_number = expr1.X_add_number; macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register); macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg, tempreg, BFD_RELOC_MIPS_GOT_OFST); if (add_breg_early) { macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg); breg = 0; tempreg = treg; } relax_end (); } else abort (); if (breg != 0) macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg); break; case M_J_A: /* The j instruction may not be used in PIC code, since it requires an absolute address. We convert it to a b instruction. */ if (mips_pic == NO_PIC) macro_build (&offset_expr, "j", "a"); else macro_build (&offset_expr, "b", "p"); break; /* The jal instructions must be handled as macros because when generating PIC code they expand to multi-instruction sequences. Normally they are simple instructions. */ case M_JAL_1: dreg = RA; /* Fall through. */ case M_JAL_2: if (mips_pic == NO_PIC) macro_build (NULL, "jalr", "d,s", dreg, sreg); else { if (sreg != PIC_CALL_REG) as_warn (_("MIPS PIC call to register other than $25")); macro_build (NULL, "jalr", "d,s", dreg, sreg); if (mips_pic == SVR4_PIC && !HAVE_NEWABI) { if (mips_cprestore_offset < 0) as_warn (_("No .cprestore pseudo-op used in PIC code")); else { if (! mips_frame_reg_valid) { as_warn (_("No .frame pseudo-op used in PIC code")); /* Quiet this warning. */ mips_frame_reg_valid = 1; } if (! mips_cprestore_valid) { as_warn (_("No .cprestore pseudo-op used in PIC code")); /* Quiet this warning. */ mips_cprestore_valid = 1; } expr1.X_add_number = mips_cprestore_offset; macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN, mips_gp_register, mips_frame_reg, HAVE_64BIT_ADDRESSES); } } } break; case M_JAL_A: if (mips_pic == NO_PIC) macro_build (&offset_expr, "jal", "a"); else if (mips_pic == SVR4_PIC) { /* If this is a reference to an external symbol, and we are using a small GOT, we want lw $25,($gp) (BFD_RELOC_MIPS_CALL16) nop jalr $ra,$25 nop lw $gp,cprestore($sp) The cprestore value is set using the .cprestore pseudo-op. If we are using a big GOT, we want lui $25, (BFD_RELOC_MIPS_CALL_HI16) addu $25,$25,$gp lw $25,($25) (BFD_RELOC_MIPS_CALL_LO16) nop jalr $ra,$25 nop lw $gp,cprestore($sp) If the symbol is not external, we want lw $25,($gp) (BFD_RELOC_MIPS_GOT16) nop addiu $25,$25, (BFD_RELOC_LO16) jalr $ra,$25 nop lw $gp,cprestore($sp) For NewABI, we use the same CALL16 or CALL_HI16/CALL_LO16 sequences above, minus nops, unless the symbol is local, which enables us to use GOT_PAGE/GOT_OFST (big got) or GOT_DISP. */ if (HAVE_NEWABI) { if (! mips_big_got) { relax_start (offset_expr.X_add_symbol); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", PIC_CALL_REG, BFD_RELOC_MIPS_CALL16, mips_gp_register); relax_switch (); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", PIC_CALL_REG, BFD_RELOC_MIPS_GOT_DISP, mips_gp_register); relax_end (); } else { relax_start (offset_expr.X_add_symbol); macro_build (&offset_expr, "lui", "t,u", PIC_CALL_REG, BFD_RELOC_MIPS_CALL_HI16); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG, PIC_CALL_REG, mips_gp_register); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16, PIC_CALL_REG); relax_switch (); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", PIC_CALL_REG, BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register); macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", PIC_CALL_REG, PIC_CALL_REG, BFD_RELOC_MIPS_GOT_OFST); relax_end (); } macro_build_jalr (&offset_expr); } else { relax_start (offset_expr.X_add_symbol); if (! mips_big_got) { macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", PIC_CALL_REG, BFD_RELOC_MIPS_CALL16, mips_gp_register); load_delay_nop (); relax_switch (); } else { int gpdelay; gpdelay = reg_needs_delay (mips_gp_register); macro_build (&offset_expr, "lui", "t,u", PIC_CALL_REG, BFD_RELOC_MIPS_CALL_HI16); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG, PIC_CALL_REG, mips_gp_register); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16, PIC_CALL_REG); load_delay_nop (); relax_switch (); if (gpdelay) macro_build (NULL, "nop", ""); } macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", PIC_CALL_REG, BFD_RELOC_MIPS_GOT16, mips_gp_register); load_delay_nop (); macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", PIC_CALL_REG, PIC_CALL_REG, BFD_RELOC_LO16); relax_end (); macro_build_jalr (&offset_expr); if (mips_cprestore_offset < 0) as_warn (_("No .cprestore pseudo-op used in PIC code")); else { if (! mips_frame_reg_valid) { as_warn (_("No .frame pseudo-op used in PIC code")); /* Quiet this warning. */ mips_frame_reg_valid = 1; } if (! mips_cprestore_valid) { as_warn (_("No .cprestore pseudo-op used in PIC code")); /* Quiet this warning. */ mips_cprestore_valid = 1; } if (mips_opts.noreorder) macro_build (NULL, "nop", ""); expr1.X_add_number = mips_cprestore_offset; macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN, mips_gp_register, mips_frame_reg, HAVE_64BIT_ADDRESSES); } } } else if (mips_pic == VXWORKS_PIC) as_bad (_("Non-PIC jump used in PIC library")); else abort (); break; case M_LB_AB: s = "lb"; goto ld; case M_LBU_AB: s = "lbu"; goto ld; case M_LH_AB: s = "lh"; goto ld; case M_LHU_AB: s = "lhu"; goto ld; case M_LW_AB: s = "lw"; goto ld; case M_LWC0_AB: s = "lwc0"; /* Itbl support may require additional care here. */ coproc = 1; goto ld; case M_LWC1_AB: s = "lwc1"; /* Itbl support may require additional care here. */ coproc = 1; goto ld; case M_LWC2_AB: s = "lwc2"; /* Itbl support may require additional care here. */ coproc = 1; goto ld; case M_LWC3_AB: s = "lwc3"; /* Itbl support may require additional care here. */ coproc = 1; goto ld; case M_LWL_AB: s = "lwl"; lr = 1; goto ld; case M_LWR_AB: s = "lwr"; lr = 1; goto ld; case M_LDC1_AB: if (mips_opts.arch == CPU_R4650) { as_bad (_("opcode not supported on this processor")); break; } s = "ldc1"; /* Itbl support may require additional care here. */ coproc = 1; goto ld; case M_LDC2_AB: s = "ldc2"; /* Itbl support may require additional care here. */ coproc = 1; goto ld; case M_LDC3_AB: s = "ldc3"; /* Itbl support may require additional care here. */ coproc = 1; goto ld; case M_LDL_AB: s = "ldl"; lr = 1; goto ld; case M_LDR_AB: s = "ldr"; lr = 1; goto ld; case M_LL_AB: s = "ll"; goto ld; case M_LLD_AB: s = "lld"; goto ld; case M_LWU_AB: s = "lwu"; ld: if (mips_opts.arch == CPU_OCTEON && octeon_error_on_unsupported && (mask == M_LDC1_AB || mask == M_LDC2_AB || mask == M_LDC3_AB || mask == M_L_DOB || mask == M_L_DAB || mask == M_LI_D || mask == M_LI_DD || mask == M_LI_S || mask == M_LI_SS)) { as_bad (_("opcode not implemented in Octeon `%s'"), ip->insn_mo->name); return; } if (breg == treg || coproc || lr) { tempreg = AT; used_at = 1; } else { tempreg = treg; } goto ld_st; case M_SB_AB: s = "sb"; goto st; case M_SH_AB: s = "sh"; goto st; case M_SW_AB: s = "sw"; goto st; case M_SWC0_AB: s = "swc0"; /* Itbl support may require additional care here. */ coproc = 1; goto st; case M_SWC1_AB: s = "swc1"; /* Itbl support may require additional care here. */ coproc = 1; goto st; case M_SWC2_AB: s = "swc2"; /* Itbl support may require additional care here. */ coproc = 1; goto st; case M_SWC3_AB: s = "swc3"; /* Itbl support may require additional care here. */ coproc = 1; goto st; case M_SWL_AB: s = "swl"; goto st; case M_SWR_AB: s = "swr"; goto st; case M_SC_AB: s = "sc"; goto st; case M_SCD_AB: s = "scd"; goto st; case M_CACHE_AB: s = "cache"; goto st; case M_SDC1_AB: if (mips_opts.arch == CPU_R4650) { as_bad (_("opcode not supported on this processor")); break; } s = "sdc1"; coproc = 1; /* Itbl support may require additional care here. */ goto st; case M_SDC2_AB: s = "sdc2"; /* Itbl support may require additional care here. */ coproc = 1; goto st; case M_SDC3_AB: s = "sdc3"; /* Itbl support may require additional care here. */ coproc = 1; goto st; case M_SDL_AB: s = "sdl"; goto st; case M_SDR_AB: s = "sdr"; st: if (mips_opts.arch == CPU_OCTEON && octeon_error_on_unsupported && (mask == M_SWC0_AB || mask == M_SWC1_AB || mask == M_SWC2_AB || mask == M_SDC1_AB || mask == M_SDC2_AB || mask == M_SDC3_AB || mask == M_S_DAB || mask == M_S_DOB)) { as_bad (_("opcode not implemented in Octeon `%s'"), ip->insn_mo->name); return; } tempreg = AT; used_at = 1; ld_st: /* Itbl support may require additional care here. */ if (mask == M_LWC1_AB || mask == M_SWC1_AB || mask == M_LDC1_AB || mask == M_SDC1_AB || mask == M_L_DAB || mask == M_S_DAB) fmt = "T,o(b)"; else if (mask == M_CACHE_AB) fmt = "k,o(b)"; else if (coproc) fmt = "E,o(b)"; else fmt = "t,o(b)"; if (offset_expr.X_op != O_constant && offset_expr.X_op != O_symbol) { as_bad (_("expression too complex")); offset_expr.X_op = O_constant; } if (HAVE_32BIT_ADDRESSES && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number)) { char value [32]; sprintf_vma (value, offset_expr.X_add_number); as_bad (_("Number (0x%s) larger than 32 bits"), value); } /* A constant expression in PIC code can be handled just as it is in non PIC code. */ if (offset_expr.X_op == O_constant) { expr1.X_add_number = ((offset_expr.X_add_number + 0x8000) & ~(bfd_vma) 0xffff); normalize_address_expr (&expr1); load_register (tempreg, &expr1, HAVE_64BIT_ADDRESSES); if (breg != 0) macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, breg); macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16, tempreg); } else if (mips_pic == NO_PIC) { /* If this is a reference to a GP relative symbol, and there is no base register, we want $treg,($gp) (BFD_RELOC_GPREL16) Otherwise, if there is no base register, we want lui $tempreg, (BFD_RELOC_HI16_S) $treg,($tempreg) (BFD_RELOC_LO16) If we have a constant, we need two instructions anyhow, so we always use the latter form. If we have a base register, and this is a reference to a GP relative symbol, we want addu $tempreg,$breg,$gp $treg,($tempreg) (BFD_RELOC_GPREL16) Otherwise we want lui $tempreg, (BFD_RELOC_HI16_S) addu $tempreg,$tempreg,$breg $treg,($tempreg) (BFD_RELOC_LO16) With a constant we always use the latter case. With 64bit address space and no base register and $at usable, we want lui $tempreg, (BFD_RELOC_MIPS_HIGHEST) lui $at, (BFD_RELOC_HI16_S) daddiu $tempreg, (BFD_RELOC_MIPS_HIGHER) dsll32 $tempreg,0 daddu $tempreg,$at $treg,($tempreg) (BFD_RELOC_LO16) If we have a base register, we want lui $tempreg, (BFD_RELOC_MIPS_HIGHEST) lui $at, (BFD_RELOC_HI16_S) daddiu $tempreg, (BFD_RELOC_MIPS_HIGHER) daddu $at,$breg dsll32 $tempreg,0 daddu $tempreg,$at $treg,($tempreg) (BFD_RELOC_LO16) Without $at we can't generate the optimal path for superscalar processors here since this would require two temporary registers. lui $tempreg, (BFD_RELOC_MIPS_HIGHEST) daddiu $tempreg, (BFD_RELOC_MIPS_HIGHER) dsll $tempreg,16 daddiu $tempreg, (BFD_RELOC_HI16_S) dsll $tempreg,16 $treg,($tempreg) (BFD_RELOC_LO16) If we have a base register, we want lui $tempreg, (BFD_RELOC_MIPS_HIGHEST) daddiu $tempreg, (BFD_RELOC_MIPS_HIGHER) dsll $tempreg,16 daddiu $tempreg, (BFD_RELOC_HI16_S) dsll $tempreg,16 daddu $tempreg,$tempreg,$breg $treg,($tempreg) (BFD_RELOC_LO16) For GP relative symbols in 64bit address space we can use the same sequence as in 32bit address space. */ if (HAVE_64BIT_SYMBOLS) { if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET && !nopic_need_relax (offset_expr.X_add_symbol, 1)) { relax_start (offset_expr.X_add_symbol); if (breg == 0) { macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_GPREL16, mips_gp_register); } else { macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, breg, mips_gp_register); macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_GPREL16, tempreg); } relax_switch (); } if (used_at == 0 && !mips_opts.noat) { macro_build (&offset_expr, "lui", "t,u", tempreg, BFD_RELOC_MIPS_HIGHEST); macro_build (&offset_expr, "lui", "t,u", AT, BFD_RELOC_HI16_S); macro_build (&offset_expr, "daddiu", "t,r,j", tempreg, tempreg, BFD_RELOC_MIPS_HIGHER); if (breg != 0) macro_build (NULL, "daddu", "d,v,t", AT, AT, breg); macro_build (NULL, "dsll32", "d,w,<", tempreg, tempreg, 0); macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT); macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16, tempreg); used_at = 1; } else { macro_build (&offset_expr, "lui", "t,u", tempreg, BFD_RELOC_MIPS_HIGHEST); macro_build (&offset_expr, "daddiu", "t,r,j", tempreg, tempreg, BFD_RELOC_MIPS_HIGHER); macro_build (NULL, "dsll", "d,w,<", tempreg, tempreg, 16); macro_build (&offset_expr, "daddiu", "t,r,j", tempreg, tempreg, BFD_RELOC_HI16_S); macro_build (NULL, "dsll", "d,w,<", tempreg, tempreg, 16); if (breg != 0) macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, breg); macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16, tempreg); } if (mips_relax.sequence) relax_end (); break; } if (breg == 0) { if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET && !nopic_need_relax (offset_expr.X_add_symbol, 1)) { relax_start (offset_expr.X_add_symbol); macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_GPREL16, mips_gp_register); relax_switch (); } macro_build_lui (&offset_expr, tempreg); macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16, tempreg); if (mips_relax.sequence) relax_end (); } else { if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET && !nopic_need_relax (offset_expr.X_add_symbol, 1)) { relax_start (offset_expr.X_add_symbol); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, breg, mips_gp_register); macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_GPREL16, tempreg); relax_switch (); } macro_build_lui (&offset_expr, tempreg); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, breg); macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16, tempreg); if (mips_relax.sequence) relax_end (); } } else if (!mips_big_got) { int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16; /* If this is a reference to an external symbol, we want lw $tempreg,($gp) (BFD_RELOC_MIPS_GOT16) nop $treg,0($tempreg) Otherwise we want lw $tempreg,($gp) (BFD_RELOC_MIPS_GOT16) nop addiu $tempreg,$tempreg, (BFD_RELOC_LO16) $treg,0($tempreg) For NewABI, we want lw $tempreg,($gp) (BFD_RELOC_MIPS_GOT_PAGE) $treg,($tempreg) (BFD_RELOC_MIPS_GOT_OFST) If there is a base register, we add it to $tempreg before the . If there is a constant, we stick it in the instruction. We don't handle constants larger than 16 bits, because we have no way to load the upper 16 bits (actually, we could handle them for the subset of cases in which we are not using $at). */ assert (offset_expr.X_op == O_symbol); if (HAVE_NEWABI) { macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register); if (breg != 0) macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, breg); macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_MIPS_GOT_OFST, tempreg); break; } expr1.X_add_number = offset_expr.X_add_number; offset_expr.X_add_number = 0; if (expr1.X_add_number < -0x8000 || expr1.X_add_number >= 0x8000) as_bad (_("PIC code offset overflow (max 16 signed bits)")); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, lw_reloc_type, mips_gp_register); load_delay_nop (); relax_start (offset_expr.X_add_symbol); relax_switch (); macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg, tempreg, BFD_RELOC_LO16); relax_end (); if (breg != 0) macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, breg); macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg); } else if (mips_big_got && !HAVE_NEWABI) { int gpdelay; /* If this is a reference to an external symbol, we want lui $tempreg, (BFD_RELOC_MIPS_GOT_HI16) addu $tempreg,$tempreg,$gp lw $tempreg,($tempreg) (BFD_RELOC_MIPS_GOT_LO16) $treg,0($tempreg) Otherwise we want lw $tempreg,($gp) (BFD_RELOC_MIPS_GOT16) nop addiu $tempreg,$tempreg, (BFD_RELOC_LO16) $treg,0($tempreg) If there is a base register, we add it to $tempreg before the . If there is a constant, we stick it in the instruction. We don't handle constants larger than 16 bits, because we have no way to load the upper 16 bits (actually, we could handle them for the subset of cases in which we are not using $at). */ assert (offset_expr.X_op == O_symbol); expr1.X_add_number = offset_expr.X_add_number; offset_expr.X_add_number = 0; if (expr1.X_add_number < -0x8000 || expr1.X_add_number >= 0x8000) as_bad (_("PIC code offset overflow (max 16 signed bits)")); gpdelay = reg_needs_delay (mips_gp_register); relax_start (offset_expr.X_add_symbol); macro_build (&offset_expr, "lui", "t,u", tempreg, BFD_RELOC_MIPS_GOT_HI16); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, mips_gp_register); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, BFD_RELOC_MIPS_GOT_LO16, tempreg); relax_switch (); if (gpdelay) macro_build (NULL, "nop", ""); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, BFD_RELOC_MIPS_GOT16, mips_gp_register); load_delay_nop (); macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg, tempreg, BFD_RELOC_LO16); relax_end (); if (breg != 0) macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, breg); macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg); } else if (mips_big_got && HAVE_NEWABI) { /* If this is a reference to an external symbol, we want lui $tempreg, (BFD_RELOC_MIPS_GOT_HI16) add $tempreg,$tempreg,$gp lw $tempreg,($tempreg) (BFD_RELOC_MIPS_GOT_LO16) $treg,($tempreg) Otherwise, for local symbols, we want: lw $tempreg,($gp) (BFD_RELOC_MIPS_GOT_PAGE) $treg,($tempreg) (BFD_RELOC_MIPS_GOT_OFST) */ assert (offset_expr.X_op == O_symbol); expr1.X_add_number = offset_expr.X_add_number; offset_expr.X_add_number = 0; if (expr1.X_add_number < -0x8000 || expr1.X_add_number >= 0x8000) as_bad (_("PIC code offset overflow (max 16 signed bits)")); relax_start (offset_expr.X_add_symbol); macro_build (&offset_expr, "lui", "t,u", tempreg, BFD_RELOC_MIPS_GOT_HI16); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, mips_gp_register); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, BFD_RELOC_MIPS_GOT_LO16, tempreg); if (breg != 0) macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, breg); macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg); relax_switch (); offset_expr.X_add_number = expr1.X_add_number; macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg, BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register); if (breg != 0) macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, breg); macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_MIPS_GOT_OFST, tempreg); relax_end (); } else abort (); break; case M_LI: case M_LI_S: load_register (treg, &imm_expr, 0); break; case M_DLI: load_register (treg, &imm_expr, 1); break; case M_LI_SS: if (imm_expr.X_op == O_constant) { used_at = 1; load_register (AT, &imm_expr, 0); macro_build (NULL, "mtc1", "t,G", AT, treg); break; } else { assert (offset_expr.X_op == O_symbol && strcmp (segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol)), ".lit4") == 0 && offset_expr.X_add_number == 0); macro_build (&offset_expr, "lwc1", "T,o(b)", treg, BFD_RELOC_MIPS_LITERAL, mips_gp_register); break; } case M_LI_D: /* Check if we have a constant in IMM_EXPR. If the GPRs are 64 bits wide, IMM_EXPR is the entire value. Otherwise IMM_EXPR is the high order 32 bits of the value and the low order 32 bits are either zero or in OFFSET_EXPR. */ if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big) { if (HAVE_64BIT_GPRS) load_register (treg, &imm_expr, 1); else { int hreg, lreg; if (target_big_endian) { hreg = treg; lreg = treg + 1; } else { hreg = treg + 1; lreg = treg; } if (hreg <= 31) load_register (hreg, &imm_expr, 0); if (lreg <= 31) { if (offset_expr.X_op == O_absent) move_register (lreg, 0); else { assert (offset_expr.X_op == O_constant); load_register (lreg, &offset_expr, 0); } } } break; } /* We know that sym is in the .rdata section. First we get the upper 16 bits of the address. */ if (mips_pic == NO_PIC) { macro_build_lui (&offset_expr, AT); used_at = 1; } else { macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT, BFD_RELOC_MIPS_GOT16, mips_gp_register); used_at = 1; } /* Now we load the register(s). */ if (HAVE_64BIT_GPRS) { used_at = 1; macro_build (&offset_expr, "ld", "t,o(b)", treg, BFD_RELOC_LO16, AT); } else { used_at = 1; macro_build (&offset_expr, "lw", "t,o(b)", treg, BFD_RELOC_LO16, AT); if (treg != RA) { /* FIXME: How in the world do we deal with the possible overflow here? */ offset_expr.X_add_number += 4; macro_build (&offset_expr, "lw", "t,o(b)", treg + 1, BFD_RELOC_LO16, AT); } } break; case M_LI_DD: /* Check if we have a constant in IMM_EXPR. If the FPRs are 64 bits wide, IMM_EXPR is the entire value and the GPRs are known to be 64 bits wide as well. Otherwise IMM_EXPR is the high order 32 bits of the value and the low order 32 bits are either zero or in OFFSET_EXPR. */ if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big) { used_at = 1; load_register (AT, &imm_expr, HAVE_64BIT_FPRS); if (HAVE_64BIT_FPRS) { assert (HAVE_64BIT_GPRS); macro_build (NULL, "dmtc1", "t,S", AT, treg); } else { macro_build (NULL, "mtc1", "t,G", AT, treg + 1); if (offset_expr.X_op == O_absent) macro_build (NULL, "mtc1", "t,G", 0, treg); else { assert (offset_expr.X_op == O_constant); load_register (AT, &offset_expr, 0); macro_build (NULL, "mtc1", "t,G", AT, treg); } } break; } assert (offset_expr.X_op == O_symbol && offset_expr.X_add_number == 0); s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol)); if (strcmp (s, ".lit8") == 0) { if (mips_opts.isa != ISA_MIPS1) { macro_build (&offset_expr, "ldc1", "T,o(b)", treg, BFD_RELOC_MIPS_LITERAL, mips_gp_register); break; } breg = mips_gp_register; r = BFD_RELOC_MIPS_LITERAL; goto dob; } else { assert (strcmp (s, RDATA_SECTION_NAME) == 0); used_at = 1; if (mips_pic != NO_PIC) macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT, BFD_RELOC_MIPS_GOT16, mips_gp_register); else { /* FIXME: This won't work for a 64 bit address. */ macro_build_lui (&offset_expr, AT); } if (mips_opts.isa != ISA_MIPS1) { macro_build (&offset_expr, "ldc1", "T,o(b)", treg, BFD_RELOC_LO16, AT); break; } breg = AT; r = BFD_RELOC_LO16; goto dob; } case M_L_DOB: if (mips_opts.arch == CPU_R4650) { as_bad (_("opcode not supported on this processor")); break; } /* Even on a big endian machine $fn comes before $fn+1. We have to adjust when loading from memory. */ r = BFD_RELOC_LO16; dob: assert (mips_opts.isa == ISA_MIPS1); macro_build (&offset_expr, "lwc1", "T,o(b)", target_big_endian ? treg + 1 : treg, r, breg); /* FIXME: A possible overflow which I don't know how to deal with. */ offset_expr.X_add_number += 4; macro_build (&offset_expr, "lwc1", "T,o(b)", target_big_endian ? treg : treg + 1, r, breg); break; case M_L_DAB: /* * The MIPS assembler seems to check for X_add_number not * being double aligned and generating: * lui at,%hi(foo+1) * addu at,at,v1 * addiu at,at,%lo(foo+1) * lwc1 f2,0(at) * lwc1 f3,4(at) * But, the resulting address is the same after relocation so why * generate the extra instruction? */ if (mips_opts.arch == CPU_R4650) { as_bad (_("opcode not supported on this processor")); break; } /* Itbl support may require additional care here. */ coproc = 1; if (mips_opts.isa != ISA_MIPS1) { s = "ldc1"; goto ld; } s = "lwc1"; fmt = "T,o(b)"; goto ldd_std; case M_S_DAB: if (mips_opts.arch == CPU_R4650) { as_bad (_("opcode not supported on this processor")); break; } if (mips_opts.isa != ISA_MIPS1) { s = "sdc1"; goto st; } s = "swc1"; fmt = "T,o(b)"; /* Itbl support may require additional care here. */ coproc = 1; goto ldd_std; case M_LD_AB: if (HAVE_64BIT_GPRS) { s = "ld"; goto ld; } s = "lw"; fmt = "t,o(b)"; goto ldd_std; case M_SD_AB: if (HAVE_64BIT_GPRS) { s = "sd"; goto st; } s = "sw"; fmt = "t,o(b)"; ldd_std: if (offset_expr.X_op != O_symbol && offset_expr.X_op != O_constant) { as_bad (_("expression too complex")); offset_expr.X_op = O_constant; } if (HAVE_32BIT_ADDRESSES && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number)) { char value [32]; sprintf_vma (value, offset_expr.X_add_number); as_bad (_("Number (0x%s) larger than 32 bits"), value); } /* Even on a big endian machine $fn comes before $fn+1. We have to adjust when loading from memory. We set coproc if we must load $fn+1 first. */ /* Itbl support may require additional care here. */ if (! target_big_endian) coproc = 0; if (mips_pic == NO_PIC || offset_expr.X_op == O_constant) { /* If this is a reference to a GP relative symbol, we want $treg,($gp) (BFD_RELOC_GPREL16) $treg+1,+4($gp) (BFD_RELOC_GPREL16) If we have a base register, we use this addu $at,$breg,$gp $treg,($at) (BFD_RELOC_GPREL16) $treg+1,+4($at) (BFD_RELOC_GPREL16) If this is not a GP relative symbol, we want lui $at, (BFD_RELOC_HI16_S) $treg,($at) (BFD_RELOC_LO16) $treg+1,+4($at) (BFD_RELOC_LO16) If there is a base register, we add it to $at after the lui instruction. If there is a constant, we always use the last case. */ if (offset_expr.X_op == O_symbol && (valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET && !nopic_need_relax (offset_expr.X_add_symbol, 1)) { relax_start (offset_expr.X_add_symbol); if (breg == 0) { tempreg = mips_gp_register; } else { macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, mips_gp_register); tempreg = AT; used_at = 1; } /* Itbl support may require additional care here. */ macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg, BFD_RELOC_GPREL16, tempreg); offset_expr.X_add_number += 4; /* Set mips_optimize to 2 to avoid inserting an undesired nop. */ hold_mips_optimize = mips_optimize; mips_optimize = 2; /* Itbl support may require additional care here. */ macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1, BFD_RELOC_GPREL16, tempreg); mips_optimize = hold_mips_optimize; relax_switch (); /* We just generated two relocs. When tc_gen_reloc handles this case, it will skip the first reloc and handle the second. The second reloc already has an extra addend of 4, which we added above. We must subtract it out, and then subtract another 4 to make the first reloc come out right. The second reloc will come out right because we are going to add 4 to offset_expr when we build its instruction below. If we have a symbol, then we don't want to include the offset, because it will wind up being included when we generate the reloc. */ if (offset_expr.X_op == O_constant) offset_expr.X_add_number -= 8; else { offset_expr.X_add_number = -4; offset_expr.X_op = O_constant; } } used_at = 1; macro_build_lui (&offset_expr, AT); if (breg != 0) macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT); /* Itbl support may require additional care here. */ macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg, BFD_RELOC_LO16, AT); /* FIXME: How do we handle overflow here? */ offset_expr.X_add_number += 4; /* Itbl support may require additional care here. */ macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1, BFD_RELOC_LO16, AT); if (mips_relax.sequence) relax_end (); } else if (!mips_big_got) { /* If this is a reference to an external symbol, we want lw $at,($gp) (BFD_RELOC_MIPS_GOT16) nop $treg,0($at) $treg+1,4($at) Otherwise we want lw $at,($gp) (BFD_RELOC_MIPS_GOT16) nop $treg,($at) (BFD_RELOC_LO16) $treg+1,+4($at) (BFD_RELOC_LO16) If there is a base register we add it to $at before the lwc1 instructions. If there is a constant we include it in the lwc1 instructions. */ used_at = 1; expr1.X_add_number = offset_expr.X_add_number; if (expr1.X_add_number < -0x8000 || expr1.X_add_number >= 0x8000 - 4) as_bad (_("PIC code offset overflow (max 16 signed bits)")); load_got_offset (AT, &offset_expr); load_delay_nop (); if (breg != 0) macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT); /* Set mips_optimize to 2 to avoid inserting an undesired nop. */ hold_mips_optimize = mips_optimize; mips_optimize = 2; /* Itbl support may require additional care here. */ relax_start (offset_expr.X_add_symbol); macro_build (&expr1, s, fmt, coproc ? treg + 1 : treg, BFD_RELOC_LO16, AT); expr1.X_add_number += 4; macro_build (&expr1, s, fmt, coproc ? treg : treg + 1, BFD_RELOC_LO16, AT); relax_switch (); macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg, BFD_RELOC_LO16, AT); offset_expr.X_add_number += 4; macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1, BFD_RELOC_LO16, AT); relax_end (); mips_optimize = hold_mips_optimize; } else if (mips_big_got) { int gpdelay; /* If this is a reference to an external symbol, we want lui $at, (BFD_RELOC_MIPS_GOT_HI16) addu $at,$at,$gp lw $at,($at) (BFD_RELOC_MIPS_GOT_LO16) nop $treg,0($at) $treg+1,4($at) Otherwise we want lw $at,($gp) (BFD_RELOC_MIPS_GOT16) nop $treg,($at) (BFD_RELOC_LO16) $treg+1,+4($at) (BFD_RELOC_LO16) If there is a base register we add it to $at before the lwc1 instructions. If there is a constant we include it in the lwc1 instructions. */ used_at = 1; expr1.X_add_number = offset_expr.X_add_number; offset_expr.X_add_number = 0; if (expr1.X_add_number < -0x8000 || expr1.X_add_number >= 0x8000 - 4) as_bad (_("PIC code offset overflow (max 16 signed bits)")); gpdelay = reg_needs_delay (mips_gp_register); relax_start (offset_expr.X_add_symbol); macro_build (&offset_expr, "lui", "t,u", AT, BFD_RELOC_MIPS_GOT_HI16); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, mips_gp_register); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT, BFD_RELOC_MIPS_GOT_LO16, AT); load_delay_nop (); if (breg != 0) macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT); /* Itbl support may require additional care here. */ macro_build (&expr1, s, fmt, coproc ? treg + 1 : treg, BFD_RELOC_LO16, AT); expr1.X_add_number += 4; /* Set mips_optimize to 2 to avoid inserting an undesired nop. */ hold_mips_optimize = mips_optimize; mips_optimize = 2; /* Itbl support may require additional care here. */ macro_build (&expr1, s, fmt, coproc ? treg : treg + 1, BFD_RELOC_LO16, AT); mips_optimize = hold_mips_optimize; expr1.X_add_number -= 4; relax_switch (); offset_expr.X_add_number = expr1.X_add_number; if (gpdelay) macro_build (NULL, "nop", ""); macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT, BFD_RELOC_MIPS_GOT16, mips_gp_register); load_delay_nop (); if (breg != 0) macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT); /* Itbl support may require additional care here. */ macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg, BFD_RELOC_LO16, AT); offset_expr.X_add_number += 4; /* Set mips_optimize to 2 to avoid inserting an undesired nop. */ hold_mips_optimize = mips_optimize; mips_optimize = 2; /* Itbl support may require additional care here. */ macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1, BFD_RELOC_LO16, AT); mips_optimize = hold_mips_optimize; relax_end (); } else abort (); break; case M_LD_OB: s = "lw"; goto sd_ob; case M_SD_OB: s = "sw"; sd_ob: assert (HAVE_32BIT_ADDRESSES); macro_build (&offset_expr, s, "t,o(b)", treg, BFD_RELOC_LO16, breg); offset_expr.X_add_number += 4; macro_build (&offset_expr, s, "t,o(b)", treg + 1, BFD_RELOC_LO16, breg); break; case M_SAA_AB: s = "saa"; goto saa_saad; case M_SAAD_AB: s = "saad"; saa_saad: /* The "saa/saad" instructions are new in CN58XX. These instructions do not specify offset. When invoked with address or symbol, then load the address or value of symbol in a register using the dla macro into AT, and pass the register for emitting "saa/saad" instruction. This will get expanded to dla AT, constant/label saa/saad $treg,(AT) */ { char *name = "dla"; char *fmt = "t,A(b)"; const struct mips_opcode *mo; struct mips_cl_insn insn; mo = hash_find (op_hash, name); assert (strcmp (name, mo->name) == 0); assert (strcmp (fmt, mo->args) == 0); create_insn (&insn, mo); insn.insn_opcode = insn.insn_mo->match; used_at = 1; INSERT_OPERAND (RT, insn, AT); if (breg) INSERT_OPERAND (RS, insn, breg); /* The address part is forwarded through the global offset_expr. */ macro (&insn); macro_build (NULL, s, "t,(b)", treg, AT); break; } /* New code added to support COPZ instructions. This code builds table entries out of the macros in mip_opcodes. R4000 uses interlocks to handle coproc delays. Other chips (like the R3000) require nops to be inserted for delays. FIXME: Currently, we require that the user handle delays. In order to fill delay slots for non-interlocked chips, we must have a way to specify delays based on the coprocessor. Eg. 4 cycles if load coproc reg from memory, 1 if in cache, etc. What are the side-effects of the cop instruction? What cache support might we have and what are its effects? Both coprocessor & memory require delays. how long??? What registers are read/set/modified? If an itbl is provided to interpret cop instructions, this knowledge can be encoded in the itbl spec. */ case M_COP0: s = "c0"; goto copz; case M_COP1: s = "c1"; goto copz; case M_COP2: s = "c2"; goto copz; case M_COP3: s = "c3"; copz: if (!strcmp (s,"c2") && mips_opts.arch == CPU_OCTEON && octeon_error_on_unsupported) { as_bad (_("opcode not implemented in Octeon `%s'"), ip->insn_mo->name); return; } /* For now we just do C (same as Cz). The parameter will be stored in insn_opcode by mips_ip. */ macro_build (NULL, s, "C", ip->insn_opcode); break; case M_MOVE: move_register (dreg, sreg); break; #ifdef LOSING_COMPILER default: /* Try and see if this is a new itbl instruction. This code builds table entries out of the macros in mip_opcodes. FIXME: For now we just assemble the expression and pass it's value along as a 32-bit immediate. We may want to have the assembler assemble this value, so that we gain the assembler's knowledge of delay slots, symbols, etc. Would it be more efficient to use mask (id) here? */ if (itbl_have_entries && (immed_expr = itbl_assemble (ip->insn_mo->name, ""))) { s = ip->insn_mo->name; s2 = "cop3"; coproc = ITBL_DECODE_PNUM (immed_expr);; macro_build (&immed_expr, s, "C"); break; } macro2 (ip); break; } if (mips_opts.noat && used_at) as_bad (_("Macro used $at after \".set noat\"")); } static void macro2 (struct mips_cl_insn *ip) { int treg, sreg, dreg, breg; int tempreg; int mask; int used_at; expressionS expr1; const char *s; const char *s2; const char *fmt; int likely = 0; int dbl = 0; int coproc = 0; int lr = 0; int imm = 0; int off; offsetT maxnum; bfd_reloc_code_real_type r; treg = (ip->insn_opcode >> 16) & 0x1f; dreg = (ip->insn_opcode >> 11) & 0x1f; sreg = breg = (ip->insn_opcode >> 21) & 0x1f; mask = ip->insn_mo->mask; expr1.X_op = O_constant; expr1.X_op_symbol = NULL; expr1.X_add_symbol = NULL; expr1.X_add_number = 1; switch (mask) { #endif /* LOSING_COMPILER */ case M_DMUL: dbl = 1; case M_MUL: macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", sreg, treg); macro_build (NULL, "mflo", "d", dreg); break; case M_DMUL_I: dbl = 1; case M_MUL_I: /* The MIPS assembler some times generates shifts and adds. I'm not trying to be that fancy. GCC should do this for us anyway. */ used_at = 1; load_register (AT, &imm_expr, dbl); macro_build (NULL, dbl ? "dmult" : "mult", "s,t", sreg, AT); macro_build (NULL, "mflo", "d", dreg); break; case M_DMULO_I: dbl = 1; case M_MULO_I: imm = 1; goto do_mulo; case M_DMULO: dbl = 1; case M_MULO: do_mulo: start_noreorder (); used_at = 1; if (imm) load_register (AT, &imm_expr, dbl); macro_build (NULL, dbl ? "dmult" : "mult", "s,t", sreg, imm ? AT : treg); macro_build (NULL, "mflo", "d", dreg); macro_build (NULL, dbl ? "dsra32" : "sra", "d,w,<", dreg, dreg, RA); macro_build (NULL, "mfhi", "d", AT); if (mips_trap) macro_build (NULL, "tne", "s,t,q", dreg, AT, 6); else { expr1.X_add_number = 8; macro_build (&expr1, "beq", "s,t,p", dreg, AT); macro_build (NULL, "nop", "", 0); macro_build (NULL, "break", "c", 6); } end_noreorder (); macro_build (NULL, "mflo", "d", dreg); break; case M_DMULOU_I: dbl = 1; case M_MULOU_I: imm = 1; goto do_mulou; case M_DMULOU: dbl = 1; case M_MULOU: do_mulou: start_noreorder (); used_at = 1; if (imm) load_register (AT, &imm_expr, dbl); macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", sreg, imm ? AT : treg); macro_build (NULL, "mfhi", "d", AT); macro_build (NULL, "mflo", "d", dreg); if (mips_trap) macro_build (NULL, "tne", "s,t,q", AT, 0, 6); else { expr1.X_add_number = 8; macro_build (&expr1, "beq", "s,t,p", AT, 0); macro_build (NULL, "nop", "", 0); macro_build (NULL, "break", "c", 6); } end_noreorder (); break; case M_DROL: if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch)) { if (dreg == sreg) { tempreg = AT; used_at = 1; } else { tempreg = dreg; } macro_build (NULL, "dnegu", "d,w", tempreg, treg); macro_build (NULL, "drorv", "d,t,s", dreg, sreg, tempreg); break; } used_at = 1; macro_build (NULL, "dsubu", "d,v,t", AT, 0, treg); macro_build (NULL, "dsrlv", "d,t,s", AT, sreg, AT); macro_build (NULL, "dsllv", "d,t,s", dreg, sreg, treg); macro_build (NULL, "or", "d,v,t", dreg, dreg, AT); break; case M_ROL: if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch)) { if (dreg == sreg) { tempreg = AT; used_at = 1; } else { tempreg = dreg; } macro_build (NULL, "negu", "d,w", tempreg, treg); macro_build (NULL, "rorv", "d,t,s", dreg, sreg, tempreg); break; } used_at = 1; macro_build (NULL, "subu", "d,v,t", AT, 0, treg); macro_build (NULL, "srlv", "d,t,s", AT, sreg, AT); macro_build (NULL, "sllv", "d,t,s", dreg, sreg, treg); macro_build (NULL, "or", "d,v,t", dreg, dreg, AT); break; case M_DROL_I: { unsigned int rot; char *l, *r; if (imm_expr.X_op != O_constant) as_bad (_("Improper rotate count")); rot = imm_expr.X_add_number & 0x3f; if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch)) { rot = (64 - rot) & 0x3f; if (rot >= 32) macro_build (NULL, "dror32", "d,w,<", dreg, sreg, rot - 32); else macro_build (NULL, "dror", "d,w,<", dreg, sreg, rot); break; } if (rot == 0) { macro_build (NULL, "dsrl", "d,w,<", dreg, sreg, 0); break; } l = (rot < 0x20) ? "dsll" : "dsll32"; r = ((0x40 - rot) < 0x20) ? "dsrl" : "dsrl32"; rot &= 0x1f; used_at = 1; macro_build (NULL, l, "d,w,<", AT, sreg, rot); macro_build (NULL, r, "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f); macro_build (NULL, "or", "d,v,t", dreg, dreg, AT); } break; case M_ROL_I: { unsigned int rot; if (imm_expr.X_op != O_constant) as_bad (_("Improper rotate count")); rot = imm_expr.X_add_number & 0x1f; if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch)) { macro_build (NULL, "ror", "d,w,<", dreg, sreg, (32 - rot) & 0x1f); break; } if (rot == 0) { macro_build (NULL, "srl", "d,w,<", dreg, sreg, 0); break; } used_at = 1; macro_build (NULL, "sll", "d,w,<", AT, sreg, rot); macro_build (NULL, "srl", "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f); macro_build (NULL, "or", "d,v,t", dreg, dreg, AT); } break; case M_DROR: if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch)) { macro_build (NULL, "drorv", "d,t,s", dreg, sreg, treg); break; } used_at = 1; macro_build (NULL, "dsubu", "d,v,t", AT, 0, treg); macro_build (NULL, "dsllv", "d,t,s", AT, sreg, AT); macro_build (NULL, "dsrlv", "d,t,s", dreg, sreg, treg); macro_build (NULL, "or", "d,v,t", dreg, dreg, AT); break; case M_ROR: if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch)) { macro_build (NULL, "rorv", "d,t,s", dreg, sreg, treg); break; } used_at = 1; macro_build (NULL, "subu", "d,v,t", AT, 0, treg); macro_build (NULL, "sllv", "d,t,s", AT, sreg, AT); macro_build (NULL, "srlv", "d,t,s", dreg, sreg, treg); macro_build (NULL, "or", "d,v,t", dreg, dreg, AT); break; case M_DROR_I: { unsigned int rot; char *l, *r; if (imm_expr.X_op != O_constant) as_bad (_("Improper rotate count")); rot = imm_expr.X_add_number & 0x3f; if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch)) { if (rot >= 32) macro_build (NULL, "dror32", "d,w,<", dreg, sreg, rot - 32); else macro_build (NULL, "dror", "d,w,<", dreg, sreg, rot); break; } if (rot == 0) { macro_build (NULL, "dsrl", "d,w,<", dreg, sreg, 0); break; } r = (rot < 0x20) ? "dsrl" : "dsrl32"; l = ((0x40 - rot) < 0x20) ? "dsll" : "dsll32"; rot &= 0x1f; used_at = 1; macro_build (NULL, r, "d,w,<", AT, sreg, rot); macro_build (NULL, l, "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f); macro_build (NULL, "or", "d,v,t", dreg, dreg, AT); } break; case M_ROR_I: { unsigned int rot; if (imm_expr.X_op != O_constant) as_bad (_("Improper rotate count")); rot = imm_expr.X_add_number & 0x1f; if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch)) { macro_build (NULL, "ror", "d,w,<", dreg, sreg, rot); break; } if (rot == 0) { macro_build (NULL, "srl", "d,w,<", dreg, sreg, 0); break; } used_at = 1; macro_build (NULL, "srl", "d,w,<", AT, sreg, rot); macro_build (NULL, "sll", "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f); macro_build (NULL, "or", "d,v,t", dreg, dreg, AT); } break; case M_S_DOB: if (mips_opts.arch == CPU_R4650) { as_bad (_("opcode not supported on this processor")); break; } assert (mips_opts.isa == ISA_MIPS1); /* Even on a big endian machine $fn comes before $fn+1. We have to adjust when storing to memory. */ macro_build (&offset_expr, "swc1", "T,o(b)", target_big_endian ? treg + 1 : treg, BFD_RELOC_LO16, breg); offset_expr.X_add_number += 4; macro_build (&offset_expr, "swc1", "T,o(b)", target_big_endian ? treg : treg + 1, BFD_RELOC_LO16, breg); break; case M_SEQ: if (sreg == 0) macro_build (&expr1, "sltiu", "t,r,j", dreg, treg, BFD_RELOC_LO16); else if (treg == 0) macro_build (&expr1, "sltiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16); else { macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg); macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16); } break; case M_SEQ_I: if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0) { macro_build (&expr1, "sltiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16); break; } if (sreg == 0) { as_warn (_("Instruction %s: result is always false"), ip->insn_mo->name); move_register (dreg, 0); break; } if (imm_expr.X_op == O_constant && imm_expr.X_add_number >= 0 && imm_expr.X_add_number < 0x10000) { macro_build (&imm_expr, "xori", "t,r,i", dreg, sreg, BFD_RELOC_LO16); } else if (imm_expr.X_op == O_constant && imm_expr.X_add_number > -0x8000 && imm_expr.X_add_number < 0) { imm_expr.X_add_number = -imm_expr.X_add_number; macro_build (&imm_expr, HAVE_32BIT_GPRS ? "addiu" : "daddiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16); } else { load_register (AT, &imm_expr, HAVE_64BIT_GPRS); macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT); used_at = 1; } macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16); break; case M_SGE: /* sreg >= treg <==> not (sreg < treg) */ s = "slt"; goto sge; case M_SGEU: s = "sltu"; sge: macro_build (NULL, s, "d,v,t", dreg, sreg, treg); macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16); break; case M_SGE_I: /* sreg >= I <==> not (sreg < I) */ case M_SGEU_I: if (imm_expr.X_op == O_constant && imm_expr.X_add_number >= -0x8000 && imm_expr.X_add_number < 0x8000) { macro_build (&imm_expr, mask == M_SGE_I ? "slti" : "sltiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16); } else { load_register (AT, &imm_expr, HAVE_64BIT_GPRS); macro_build (NULL, mask == M_SGE_I ? "slt" : "sltu", "d,v,t", dreg, sreg, AT); used_at = 1; } macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16); break; case M_SGT: /* sreg > treg <==> treg < sreg */ s = "slt"; goto sgt; case M_SGTU: s = "sltu"; sgt: macro_build (NULL, s, "d,v,t", dreg, treg, sreg); break; case M_SGT_I: /* sreg > I <==> I < sreg */ s = "slt"; goto sgti; case M_SGTU_I: s = "sltu"; sgti: used_at = 1; load_register (AT, &imm_expr, HAVE_64BIT_GPRS); macro_build (NULL, s, "d,v,t", dreg, AT, sreg); break; case M_SLE: /* sreg <= treg <==> treg >= sreg <==> not (treg < sreg) */ s = "slt"; goto sle; case M_SLEU: s = "sltu"; sle: macro_build (NULL, s, "d,v,t", dreg, treg, sreg); macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16); break; case M_SLE_I: /* sreg <= I <==> I >= sreg <==> not (I < sreg) */ s = "slt"; goto slei; case M_SLEU_I: s = "sltu"; slei: used_at = 1; load_register (AT, &imm_expr, HAVE_64BIT_GPRS); macro_build (NULL, s, "d,v,t", dreg, AT, sreg); macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16); break; case M_SLT_I: if (imm_expr.X_op == O_constant && imm_expr.X_add_number >= -0x8000 && imm_expr.X_add_number < 0x8000) { macro_build (&imm_expr, "slti", "t,r,j", dreg, sreg, BFD_RELOC_LO16); break; } used_at = 1; load_register (AT, &imm_expr, HAVE_64BIT_GPRS); macro_build (NULL, "slt", "d,v,t", dreg, sreg, AT); break; case M_SLTU_I: if (imm_expr.X_op == O_constant && imm_expr.X_add_number >= -0x8000 && imm_expr.X_add_number < 0x8000) { macro_build (&imm_expr, "sltiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16); break; } used_at = 1; load_register (AT, &imm_expr, HAVE_64BIT_GPRS); macro_build (NULL, "sltu", "d,v,t", dreg, sreg, AT); break; case M_SNE: if (sreg == 0) macro_build (NULL, "sltu", "d,v,t", dreg, 0, treg); else if (treg == 0) macro_build (NULL, "sltu", "d,v,t", dreg, 0, sreg); else { macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg); macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg); } break; case M_SNE_I: if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0) { macro_build (NULL, "sltu", "d,v,t", dreg, 0, sreg); break; } if (sreg == 0) { as_warn (_("Instruction %s: result is always true"), ip->insn_mo->name); macro_build (&expr1, HAVE_32BIT_GPRS ? "addiu" : "daddiu", "t,r,j", dreg, 0, BFD_RELOC_LO16); break; } if (imm_expr.X_op == O_constant && imm_expr.X_add_number >= 0 && imm_expr.X_add_number < 0x10000) { macro_build (&imm_expr, "xori", "t,r,i", dreg, sreg, BFD_RELOC_LO16); } else if (imm_expr.X_op == O_constant && imm_expr.X_add_number > -0x8000 && imm_expr.X_add_number < 0) { imm_expr.X_add_number = -imm_expr.X_add_number; macro_build (&imm_expr, HAVE_32BIT_GPRS ? "addiu" : "daddiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16); } else { load_register (AT, &imm_expr, HAVE_64BIT_GPRS); macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT); used_at = 1; } macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg); break; case M_DSUB_I: dbl = 1; case M_SUB_I: if (imm_expr.X_op == O_constant && imm_expr.X_add_number > -0x8000 && imm_expr.X_add_number <= 0x8000) { imm_expr.X_add_number = -imm_expr.X_add_number; macro_build (&imm_expr, dbl ? "daddi" : "addi", "t,r,j", dreg, sreg, BFD_RELOC_LO16); break; } used_at = 1; load_register (AT, &imm_expr, dbl); macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, sreg, AT); break; case M_DSUBU_I: dbl = 1; case M_SUBU_I: if (imm_expr.X_op == O_constant && imm_expr.X_add_number > -0x8000 && imm_expr.X_add_number <= 0x8000) { imm_expr.X_add_number = -imm_expr.X_add_number; macro_build (&imm_expr, dbl ? "daddiu" : "addiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16); break; } used_at = 1; load_register (AT, &imm_expr, dbl); macro_build (NULL, dbl ? "dsubu" : "subu", "d,v,t", dreg, sreg, AT); break; case M_TEQ_I: s = "teq"; goto trap; case M_TGE_I: s = "tge"; goto trap; case M_TGEU_I: s = "tgeu"; goto trap; case M_TLT_I: s = "tlt"; goto trap; case M_TLTU_I: s = "tltu"; goto trap; case M_TNE_I: s = "tne"; trap: used_at = 1; load_register (AT, &imm_expr, HAVE_64BIT_GPRS); macro_build (NULL, s, "s,t", sreg, AT); break; case M_TRUNCWS: case M_TRUNCWD: if (mips_opts.arch == CPU_OCTEON && octeon_error_on_unsupported) { as_bad (_("opcode not implemented in Octeon `%s'"), ip->insn_mo->name); return; } assert (mips_opts.isa == ISA_MIPS1); used_at = 1; sreg = (ip->insn_opcode >> 11) & 0x1f; /* floating reg */ dreg = (ip->insn_opcode >> 06) & 0x1f; /* floating reg */ /* * Is the double cfc1 instruction a bug in the mips assembler; * or is there a reason for it? */ start_noreorder (); macro_build (NULL, "cfc1", "t,G", treg, RA); macro_build (NULL, "cfc1", "t,G", treg, RA); macro_build (NULL, "nop", ""); expr1.X_add_number = 3; macro_build (&expr1, "ori", "t,r,i", AT, treg, BFD_RELOC_LO16); expr1.X_add_number = 2; macro_build (&expr1, "xori", "t,r,i", AT, AT, BFD_RELOC_LO16); macro_build (NULL, "ctc1", "t,G", AT, RA); macro_build (NULL, "nop", ""); macro_build (NULL, mask == M_TRUNCWD ? "cvt.w.d" : "cvt.w.s", "D,S", dreg, sreg); macro_build (NULL, "ctc1", "t,G", treg, RA); macro_build (NULL, "nop", ""); end_noreorder (); break; case M_ULH: s = "lb"; goto ulh; case M_ULHU: s = "lbu"; ulh: used_at = 1; if (offset_expr.X_add_number >= 0x7fff) as_bad (_("operand overflow")); /* Expand the ulh to "lb, lbu, ins" instead of "lb, lbu, sll, ori". */ if (! target_big_endian) ++offset_expr.X_add_number; macro_build (&offset_expr, s, "t,o(b)", AT, BFD_RELOC_LO16, breg); if (! target_big_endian) --offset_expr.X_add_number; else ++offset_expr.X_add_number; macro_build (&offset_expr, "lbu", "t,o(b)", treg, BFD_RELOC_LO16, breg); if (ISA_HAS_INS (mips_opts.isa)) macro_build (NULL, "ins", "t,r,+A,+B", treg, AT, 8, 31); else { macro_build (NULL, "sll", "d,w,<", AT, AT, 8); macro_build (NULL, "or", "d,v,t", treg, treg, AT); } break; case M_ULD: s = "ldl"; s2 = "ldr"; off = 7; goto ulw; case M_ULW: s = "lwl"; s2 = "lwr"; off = 3; ulw: if (offset_expr.X_add_number >= 0x8000 - off) as_bad (_("operand overflow")); if (treg != breg) tempreg = treg; else { used_at = 1; tempreg = AT; } /* For small variables the compiler uses gp_rel to load the value of the variables. While parsing instructions "uld $2,%gp_rel(var)($28)" the offset_reloc[0] is set to BFD_RELOC_GPREL16. Use this relocation type while emitting instructions otherwise use BFD_RELOC_LO16. */ if (offset_reloc[0] == BFD_RELOC_UNUSED) offset_reloc[0] = BFD_RELOC_LO16; if (octeon_use_unalign && mips_opts.arch == CPU_OCTEON) { /* Reset used_at as tempreg is not used while generating Octeon unaligned load/store. */ used_at = 0; macro_build (&offset_expr, (mask == M_ULW ? "ulw" : "uld"), "t,o(b)", treg, offset_reloc[0], breg); break; } if (! target_big_endian) offset_expr.X_add_number += off; macro_build (&offset_expr, s, "t,o(b)", tempreg, offset_reloc[0], breg); if (! target_big_endian) offset_expr.X_add_number -= off; else offset_expr.X_add_number += off; macro_build (&offset_expr, s2, "t,o(b)", tempreg, offset_reloc[0], breg); /* If necessary, move the result in tempreg the final destination. */ if (treg == tempreg) break; /* Protect second load's delay slot. */ load_delay_nop (); move_register (treg, tempreg); break; case M_ULD_A: s = "ldl"; s2 = "ldr"; off = 7; goto ulwa; case M_ULW_A: s = "lwl"; s2 = "lwr"; off = 3; ulwa: used_at = 1; load_address (AT, &offset_expr, &used_at); if (breg != 0) macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg); /* For small variables the compiler uses gp_rel to load the value of the variables. While parsing instructions "uld $2,%gp_rel(var)($28)" the offset_reloc[0] is set to BFD_RELOC_GPREL16. Use this relocation type while emitting instructions otherwise use BFD_RELOC_LO16. */ if (offset_reloc[0] == BFD_RELOC_UNUSED) offset_reloc[0] = BFD_RELOC_LO16; if (octeon_use_unalign && mips_opts.arch == CPU_OCTEON) { macro_build (&offset_expr, (mask == M_ULW_A ? "ulw" : "uld"), "t,o(b)", treg, offset_reloc[0], AT); break; } if (! target_big_endian) expr1.X_add_number = off; else expr1.X_add_number = 0; macro_build (&expr1, s, "t,o(b)", treg, offset_reloc[0], AT); if (! target_big_endian) expr1.X_add_number = 0; else expr1.X_add_number = off; macro_build (&expr1, s2, "t,o(b)", treg, offset_reloc[0], AT); break; case M_ULH_A: case M_ULHU_A: used_at = 1; load_address (AT, &offset_expr, &used_at); if (breg != 0) macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg); if (ISA_HAS_INS (mips_opts.isa)) { if (target_big_endian) expr1.X_add_number = 1; else expr1.X_add_number = 0; macro_build (&expr1, "lbu", "t,o(b)", treg, BFD_RELOC_LO16, AT); if (target_big_endian) expr1.X_add_number = 0; else expr1.X_add_number = 1; macro_build (&expr1, mask == M_ULH_A ? "lb" : "lbu", "t,o(b)", AT, BFD_RELOC_LO16, AT); macro_build (NULL, "ins", "t,r,+A,+B", treg, AT, 8, 31); break; } if (target_big_endian) expr1.X_add_number = 0; macro_build (&expr1, mask == M_ULH_A ? "lb" : "lbu", "t,o(b)", treg, BFD_RELOC_LO16, AT); if (target_big_endian) expr1.X_add_number = 1; else expr1.X_add_number = 0; macro_build (&expr1, "lbu", "t,o(b)", AT, BFD_RELOC_LO16, AT); macro_build (NULL, "sll", "d,w,<", treg, treg, 8); macro_build (NULL, "or", "d,v,t", treg, treg, AT); break; case M_USH: used_at = 1; if (offset_expr.X_add_number >= 0x7fff) as_bad (_("operand overflow")); if (target_big_endian) ++offset_expr.X_add_number; macro_build (&offset_expr, "sb", "t,o(b)", treg, BFD_RELOC_LO16, breg); macro_build (NULL, "srl", "d,w,<", AT, treg, 8); if (target_big_endian) --offset_expr.X_add_number; else ++offset_expr.X_add_number; macro_build (&offset_expr, "sb", "t,o(b)", AT, BFD_RELOC_LO16, breg); break; case M_USD: s = "sdl"; s2 = "sdr"; off = 7; goto usw; case M_USW: s = "swl"; s2 = "swr"; off = 3; usw: if (offset_expr.X_add_number >= 0x8000 - off) as_bad (_("operand overflow")); /* For small variables the compiler uses gp_rel to load the value of the variables. While parsing instructions "uld $2,%gp_rel(var)($28)" the offset_reloc[0] is set to BFD_RELOC_GPREL16. Use this relocation type while emitting instructions otherwise use BFD_RELOC_LO16. */ if (offset_reloc[0] == BFD_RELOC_UNUSED) offset_reloc[0] = BFD_RELOC_LO16; if (octeon_use_unalign && mips_opts.arch == CPU_OCTEON) { macro_build (&offset_expr, (mask == M_USD ? "usd" : "usw"), "t,o(b)", treg, offset_reloc[0], breg); break; } if (! target_big_endian) offset_expr.X_add_number += off; macro_build (&offset_expr, s, "t,o(b)", treg, offset_reloc[0], breg); if (! target_big_endian) offset_expr.X_add_number -= off; else offset_expr.X_add_number += off; macro_build (&offset_expr, s2, "t,o(b)", treg, offset_reloc[0], breg); break; case M_USD_A: s = "sdl"; s2 = "sdr"; off = 7; goto uswa; case M_USW_A: s = "swl"; s2 = "swr"; off = 3; uswa: used_at = 1; load_address (AT, &offset_expr, &used_at); if (breg != 0) macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg); /* For small variables the compiler uses gp_rel to load the value of the variables. While parsing instructions "uld $2,%gp_rel(var)($28)" the offset_reloc[0] is set to BFD_RELOC_GPREL16. Use this relocation type while emitting instructions otherwise use BFD_RELOC_LO16. */ if (offset_reloc[0] == BFD_RELOC_UNUSED) offset_reloc[0] = BFD_RELOC_LO16; if (octeon_use_unalign && mips_opts.arch == CPU_OCTEON) { macro_build (&offset_expr, (mask == M_USW_A ? "usw" : "usd"), "t,o(b)", treg, offset_reloc[0], AT); break; } if (! target_big_endian) expr1.X_add_number = off; else expr1.X_add_number = 0; macro_build (&expr1, s, "t,o(b)", treg, offset_reloc[0], AT); if (! target_big_endian) expr1.X_add_number = 0; else expr1.X_add_number = off; macro_build (&expr1, s2, "t,o(b)", treg, offset_reloc[0], AT); break; case M_USH_A: used_at = 1; load_address (AT, &offset_expr, &used_at); if (breg != 0) macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg); if (! target_big_endian) expr1.X_add_number = 0; macro_build (&expr1, "sb", "t,o(b)", treg, BFD_RELOC_LO16, AT); macro_build (NULL, "srl", "d,w,<", treg, treg, 8); if (! target_big_endian) expr1.X_add_number = 1; else expr1.X_add_number = 0; macro_build (&expr1, "sb", "t,o(b)", treg, BFD_RELOC_LO16, AT); if (! target_big_endian) expr1.X_add_number = 0; else expr1.X_add_number = 1; macro_build (&expr1, "lbu", "t,o(b)", AT, BFD_RELOC_LO16, AT); macro_build (NULL, "sll", "d,w,<", treg, treg, 8); macro_build (NULL, "or", "d,v,t", treg, treg, AT); break; default: /* FIXME: Check if this is one of the itbl macros, since they are added dynamically. */ as_bad (_("Macro %s not implemented yet"), ip->insn_mo->name); break; } if (mips_opts.noat && used_at) as_bad (_("Macro used $at after \".set noat\"")); } /* Implement macros in mips16 mode. */ static void mips16_macro (struct mips_cl_insn *ip) { int mask; int xreg, yreg, zreg, tmp; expressionS expr1; int dbl; const char *s, *s2, *s3; mask = ip->insn_mo->mask; xreg = MIPS16_EXTRACT_OPERAND (RX, *ip); yreg = MIPS16_EXTRACT_OPERAND (RY, *ip); zreg = MIPS16_EXTRACT_OPERAND (RZ, *ip); expr1.X_op = O_constant; expr1.X_op_symbol = NULL; expr1.X_add_symbol = NULL; expr1.X_add_number = 1; dbl = 0; switch (mask) { default: internalError (); case M_DDIV_3: dbl = 1; case M_DIV_3: s = "mflo"; goto do_div3; case M_DREM_3: dbl = 1; case M_REM_3: s = "mfhi"; do_div3: start_noreorder (); macro_build (NULL, dbl ? "ddiv" : "div", "0,x,y", xreg, yreg); expr1.X_add_number = 2; macro_build (&expr1, "bnez", "x,p", yreg); macro_build (NULL, "break", "6", 7); /* FIXME: The normal code checks for of -1 / -0x80000000 here, since that causes an overflow. We should do that as well, but I don't see how to do the comparisons without a temporary register. */ end_noreorder (); macro_build (NULL, s, "x", zreg); break; case M_DIVU_3: s = "divu"; s2 = "mflo"; goto do_divu3; case M_REMU_3: s = "divu"; s2 = "mfhi"; goto do_divu3; case M_DDIVU_3: s = "ddivu"; s2 = "mflo"; goto do_divu3; case M_DREMU_3: s = "ddivu"; s2 = "mfhi"; do_divu3: start_noreorder (); macro_build (NULL, s, "0,x,y", xreg, yreg); expr1.X_add_number = 2; macro_build (&expr1, "bnez", "x,p", yreg); macro_build (NULL, "break", "6", 7); end_noreorder (); macro_build (NULL, s2, "x", zreg); break; case M_DMUL: dbl = 1; case M_MUL: macro_build (NULL, dbl ? "dmultu" : "multu", "x,y", xreg, yreg); macro_build (NULL, "mflo", "x", zreg); break; case M_DSUBU_I: dbl = 1; goto do_subu; case M_SUBU_I: do_subu: if (imm_expr.X_op != O_constant) as_bad (_("Unsupported large constant")); imm_expr.X_add_number = -imm_expr.X_add_number; macro_build (&imm_expr, dbl ? "daddiu" : "addiu", "y,x,4", yreg, xreg); break; case M_SUBU_I_2: if (imm_expr.X_op != O_constant) as_bad (_("Unsupported large constant")); imm_expr.X_add_number = -imm_expr.X_add_number; macro_build (&imm_expr, "addiu", "x,k", xreg); break; case M_DSUBU_I_2: if (imm_expr.X_op != O_constant) as_bad (_("Unsupported large constant")); imm_expr.X_add_number = -imm_expr.X_add_number; macro_build (&imm_expr, "daddiu", "y,j", yreg); break; case M_BEQ: s = "cmp"; s2 = "bteqz"; goto do_branch; case M_BNE: s = "cmp"; s2 = "btnez"; goto do_branch; case M_BLT: s = "slt"; s2 = "btnez"; goto do_branch; case M_BLTU: s = "sltu"; s2 = "btnez"; goto do_branch; case M_BLE: s = "slt"; s2 = "bteqz"; goto do_reverse_branch; case M_BLEU: s = "sltu"; s2 = "bteqz"; goto do_reverse_branch; case M_BGE: s = "slt"; s2 = "bteqz"; goto do_branch; case M_BGEU: s = "sltu"; s2 = "bteqz"; goto do_branch; case M_BGT: s = "slt"; s2 = "btnez"; goto do_reverse_branch; case M_BGTU: s = "sltu"; s2 = "btnez"; do_reverse_branch: tmp = xreg; xreg = yreg; yreg = tmp; do_branch: macro_build (NULL, s, "x,y", xreg, yreg); macro_build (&offset_expr, s2, "p"); break; case M_BEQ_I: s = "cmpi"; s2 = "bteqz"; s3 = "x,U"; goto do_branch_i; case M_BNE_I: s = "cmpi"; s2 = "btnez"; s3 = "x,U"; goto do_branch_i; case M_BLT_I: s = "slti"; s2 = "btnez"; s3 = "x,8"; goto do_branch_i; case M_BLTU_I: s = "sltiu"; s2 = "btnez"; s3 = "x,8"; goto do_branch_i; case M_BLE_I: s = "slti"; s2 = "btnez"; s3 = "x,8"; goto do_addone_branch_i; case M_BLEU_I: s = "sltiu"; s2 = "btnez"; s3 = "x,8"; goto do_addone_branch_i; case M_BGE_I: s = "slti"; s2 = "bteqz"; s3 = "x,8"; goto do_branch_i; case M_BGEU_I: s = "sltiu"; s2 = "bteqz"; s3 = "x,8"; goto do_branch_i; case M_BGT_I: s = "slti"; s2 = "bteqz"; s3 = "x,8"; goto do_addone_branch_i; case M_BGTU_I: s = "sltiu"; s2 = "bteqz"; s3 = "x,8"; do_addone_branch_i: if (imm_expr.X_op != O_constant) as_bad (_("Unsupported large constant")); ++imm_expr.X_add_number; do_branch_i: macro_build (&imm_expr, s, s3, xreg); macro_build (&offset_expr, s2, "p"); break; case M_ABS: expr1.X_add_number = 0; macro_build (&expr1, "slti", "x,8", yreg); if (xreg != yreg) move_register (xreg, yreg); expr1.X_add_number = 2; macro_build (&expr1, "bteqz", "p"); macro_build (NULL, "neg", "x,w", xreg, xreg); } } /* For consistency checking, verify that all bits are specified either by the match/mask part of the instruction definition, or by the operand list. */ static int validate_mips_insn (const struct mips_opcode *opc) { const char *p = opc->args; char c; unsigned long used_bits = opc->mask; if ((used_bits & opc->match) != opc->match) { as_bad (_("internal: bad mips opcode (mask error): %s %s"), opc->name, opc->args); return 0; } #define USE_BITS(mask,shift) (used_bits |= ((mask) << (shift))) while (*p) switch (c = *p++) { case ',': break; case '(': break; case ')': break; case '^': USE_BITS (OP_MASK_BITIND, OP_SH_BITIND); break; case '~': USE_BITS (OP_MASK_BITIND, OP_SH_BITIND); break; case '+': switch (c = *p++) { case '1': USE_BITS (OP_MASK_UDI1, OP_SH_UDI1); break; case '2': USE_BITS (OP_MASK_UDI2, OP_SH_UDI2); break; case '3': USE_BITS (OP_MASK_UDI3, OP_SH_UDI3); break; case '4': USE_BITS (OP_MASK_UDI4, OP_SH_UDI4); break; case 'A': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break; case 'B': USE_BITS (OP_MASK_INSMSB, OP_SH_INSMSB); break; case 'C': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break; case 'D': USE_BITS (OP_MASK_RD, OP_SH_RD); USE_BITS (OP_MASK_SEL, OP_SH_SEL); break; case 'E': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break; case 'F': USE_BITS (OP_MASK_INSMSB, OP_SH_INSMSB); break; case 'G': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break; case 'H': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break; case 'I': break; case 't': USE_BITS (OP_MASK_RT, OP_SH_RT); break; case 'T': USE_BITS (OP_MASK_RT, OP_SH_RT); USE_BITS (OP_MASK_SEL, OP_SH_SEL); break; default: as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"), c, opc->name, opc->args); return 0; } break; case '<': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break; case '>': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break; case 'A': break; case 'B': USE_BITS (OP_MASK_CODE20, OP_SH_CODE20); break; case 'C': USE_BITS (OP_MASK_COPZ, OP_SH_COPZ); break; case 'D': USE_BITS (OP_MASK_FD, OP_SH_FD); break; case 'E': USE_BITS (OP_MASK_RT, OP_SH_RT); break; case 'F': break; case 'G': USE_BITS (OP_MASK_RD, OP_SH_RD); break; case 'H': USE_BITS (OP_MASK_SEL, OP_SH_SEL); break; case 'I': break; case 'J': USE_BITS (OP_MASK_CODE19, OP_SH_CODE19); break; case 'K': USE_BITS (OP_MASK_RD, OP_SH_RD); break; case 'L': break; case 'M': USE_BITS (OP_MASK_CCC, OP_SH_CCC); break; case 'N': USE_BITS (OP_MASK_BCC, OP_SH_BCC); break; case 'O': USE_BITS (OP_MASK_ALN, OP_SH_ALN); break; case 'Q': USE_BITS (OP_MASK_VSEL, OP_SH_VSEL); USE_BITS (OP_MASK_FT, OP_SH_FT); break; case 'R': USE_BITS (OP_MASK_FR, OP_SH_FR); break; case 'S': USE_BITS (OP_MASK_FS, OP_SH_FS); break; case 'T': USE_BITS (OP_MASK_FT, OP_SH_FT); break; case 'V': USE_BITS (OP_MASK_FS, OP_SH_FS); break; case 'W': USE_BITS (OP_MASK_FT, OP_SH_FT); break; case 'X': USE_BITS (OP_MASK_FD, OP_SH_FD); break; case 'Y': USE_BITS (OP_MASK_FS, OP_SH_FS); break; case 'Z': USE_BITS (OP_MASK_FT, OP_SH_FT); break; case 'a': USE_BITS (OP_MASK_TARGET, OP_SH_TARGET); break; case 'b': USE_BITS (OP_MASK_RS, OP_SH_RS); break; case 'c': USE_BITS (OP_MASK_CODE, OP_SH_CODE); break; case 'd': USE_BITS (OP_MASK_RD, OP_SH_RD); break; case 'f': break; case 'h': USE_BITS (OP_MASK_PREFX, OP_SH_PREFX); break; case 'i': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break; case 'j': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break; case 'k': USE_BITS (OP_MASK_CACHE, OP_SH_CACHE); break; case 'l': break; case 'o': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break; case 'p': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break; case 'q': USE_BITS (OP_MASK_CODE2, OP_SH_CODE2); break; case 'r': USE_BITS (OP_MASK_RS, OP_SH_RS); break; case 's': USE_BITS (OP_MASK_RS, OP_SH_RS); break; case 't': USE_BITS (OP_MASK_RT, OP_SH_RT); break; case 'u': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break; case 'v': USE_BITS (OP_MASK_RS, OP_SH_RS); break; case 'w': USE_BITS (OP_MASK_RT, OP_SH_RT); break; case 'x': break; case 'y': USE_BITS (OP_MASK_CODE2, OP_SH_CODE2); break; case 'z': break; case 'P': USE_BITS (OP_MASK_PERFREG, OP_SH_PERFREG); break; case 'U': USE_BITS (OP_MASK_RD, OP_SH_RD); USE_BITS (OP_MASK_RT, OP_SH_RT); break; case 'e': USE_BITS (OP_MASK_VECBYTE, OP_SH_VECBYTE); break; case '%': USE_BITS (OP_MASK_VECALIGN, OP_SH_VECALIGN); break; case '[': break; case ']': break; case '2': USE_BITS (OP_MASK_BP, OP_SH_BP); break; case '3': USE_BITS (OP_MASK_SA3, OP_SH_SA3); break; case '4': USE_BITS (OP_MASK_SA4, OP_SH_SA4); break; case '5': USE_BITS (OP_MASK_IMM8, OP_SH_IMM8); break; case '6': USE_BITS (OP_MASK_RS, OP_SH_RS); break; case '7': USE_BITS (OP_MASK_DSPACC, OP_SH_DSPACC); break; case '8': USE_BITS (OP_MASK_WRDSP, OP_SH_WRDSP); break; case '9': USE_BITS (OP_MASK_DSPACC_S, OP_SH_DSPACC_S);break; case '0': USE_BITS (OP_MASK_DSPSFT, OP_SH_DSPSFT); break; case '\'': USE_BITS (OP_MASK_RDDSP, OP_SH_RDDSP); break; case ':': USE_BITS (OP_MASK_DSPSFT_7, OP_SH_DSPSFT_7);break; case '@': USE_BITS (OP_MASK_IMM10, OP_SH_IMM10); break; case '!': USE_BITS (OP_MASK_MT_U, OP_SH_MT_U); break; case '$': USE_BITS (OP_MASK_MT_H, OP_SH_MT_H); break; case '*': USE_BITS (OP_MASK_MTACC_T, OP_SH_MTACC_T); break; case '&': USE_BITS (OP_MASK_MTACC_D, OP_SH_MTACC_D); break; case 'g': USE_BITS (OP_MASK_RD, OP_SH_RD); break; default: as_bad (_("internal: bad mips opcode (unknown operand type `%c'): %s %s"), c, opc->name, opc->args); return 0; } #undef USE_BITS if (used_bits != 0xffffffff) { as_bad (_("internal: bad mips opcode (bits 0x%lx undefined): %s %s"), ~used_bits & 0xffffffff, opc->name, opc->args); return 0; } return 1; } /* UDI immediates. */ struct mips_immed { char type; unsigned int shift; unsigned long mask; const char * desc; }; static const struct mips_immed mips_immed[] = { { '1', OP_SH_UDI1, OP_MASK_UDI1, 0}, { '2', OP_SH_UDI2, OP_MASK_UDI2, 0}, { '3', OP_SH_UDI3, OP_MASK_UDI3, 0}, { '4', OP_SH_UDI4, OP_MASK_UDI4, 0}, { 0,0,0,0 } }; /* Check whether an odd floating-point register is allowed. */ static int mips_oddfpreg_ok (const struct mips_opcode *insn, int argnum) { const char *s = insn->name; if (insn->pinfo == INSN_MACRO) /* Let a macro pass, we'll catch it later when it is expanded. */ return 1; if (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa)) { /* Allow odd registers for single-precision ops. */ switch (insn->pinfo & (FP_S | FP_D)) { case FP_S: case 0: return 1; /* both single precision - ok */ case FP_D: return 0; /* both double precision - fail */ default: break; } /* Cvt.w.x and cvt.x.w allow an odd register for a 'w' or 's' operand. */ s = strchr (insn->name, '.'); if (argnum == 2) s = s != NULL ? strchr (s + 1, '.') : NULL; return (s != NULL && (s[1] == 'w' || s[1] == 's')); } /* Single-precision coprocessor loads and moves are OK too. */ if ((insn->pinfo & FP_S) && (insn->pinfo & (INSN_COPROC_MEMORY_DELAY | INSN_STORE_MEMORY | INSN_LOAD_COPROC_DELAY | INSN_COPROC_MOVE_DELAY))) return 1; return 0; } /* This routine assembles an instruction into its binary format. As a side effect, it sets one of the global variables imm_reloc or offset_reloc to the type of relocation to do if one of the operands is an address expression. */ static void mips_ip (char *str, struct mips_cl_insn *ip) { char *s; const char *args; char c = 0; struct mips_opcode *insn; char *argsStart; unsigned int regno; unsigned int lastregno = 0; unsigned int lastpos = 0; unsigned int limlo, limhi; char *s_reset; char save_c = 0; offsetT min_range, max_range; int argnum; unsigned int rtype; insn_error = NULL; /* If the instruction contains a '.', we first try to match an instruction including the '.'. Then we try again without the '.'. */ insn = NULL; for (s = str; *s != '\0' && !ISSPACE (*s); ++s) continue; /* If we stopped on whitespace, then replace the whitespace with null for the call to hash_find. Save the character we replaced just in case we have to re-parse the instruction. */ if (ISSPACE (*s)) { save_c = *s; *s++ = '\0'; } insn = (struct mips_opcode *) hash_find (op_hash, str); /* If we didn't find the instruction in the opcode table, try again, but this time with just the instruction up to, but not including the first '.'. */ if (insn == NULL) { /* Restore the character we overwrite above (if any). */ if (save_c) *(--s) = save_c; /* Scan up to the first '.' or whitespace. */ for (s = str; *s != '\0' && *s != '.' && !ISSPACE (*s); ++s) continue; /* If we did not find a '.', then we can quit now. */ if (*s != '.') { insn_error = "unrecognized opcode"; return; } /* Lookup the instruction in the hash table. */ *s++ = '\0'; if ((insn = (struct mips_opcode *) hash_find (op_hash, str)) == NULL) { insn_error = "unrecognized opcode"; return; } } argsStart = s; for (;;) { bfd_boolean ok; assert (strcmp (insn->name, str) == 0); if (OPCODE_IS_MEMBER (insn, (mips_opts.isa /* We don't check for mips_opts.mips16 here since we want to allow jalx if -mips16 was specified on the command line. */ | (file_ase_mips16 ? INSN_MIPS16 : 0) | (mips_opts.ase_mdmx ? INSN_MDMX : 0) | (mips_opts.ase_dsp ? INSN_DSP : 0) | ((mips_opts.ase_dsp && ISA_SUPPORTS_DSP64_ASE) ? INSN_DSP64 : 0) | (mips_opts.ase_dspr2 ? INSN_DSPR2 : 0) | (mips_opts.ase_mt ? INSN_MT : 0) | (mips_opts.ase_mips3d ? INSN_MIPS3D : 0) | (mips_opts.ase_smartmips ? INSN_SMARTMIPS : 0)), mips_opts.arch)) ok = TRUE; else ok = FALSE; if (insn->pinfo != INSN_MACRO) { if (mips_opts.arch == CPU_R4650 && (insn->pinfo & FP_D) != 0) ok = FALSE; if (mips_opts.arch == CPU_OCTEON && octeon_error_on_unsupported && ((insn->pinfo & FP_D) != 0 || (insn->pinfo & FP_S) !=0 || strcmp (insn->name, "prefx") == 0)) { insn_error = "opcode not implemented in Octeon"; return; } if (mips_opts.arch == CPU_OCTEON && octeon_error_on_unsupported && (strcmp (insn->name, "swc2") == 0 || strcmp (insn->name, "lwc2") == 0 || strcmp (insn->name, "sdc2") == 0 || strcmp (insn->name, "ldc2") == 0 || strcmp (insn->name, "bc2f") == 0 || strcmp (insn->name, "bc2t") == 0 || strcmp (insn->name, "mfc2") == 0 || strcmp (insn->name, "mtc2") == 0 || strcmp (insn->name, "ctc2") == 0 || strcmp (insn->name, "cfc2") == 0 || strcmp (insn->name, "mfhc2") == 0 || strcmp (insn->name, "mthc2") == 0)) { insn_error = "opcode not implemented in Octeon"; return; } /* Issue a warning message for Octeon unaligned load/store instructions used when octeon_use_unalign is not set. */ if (mips_opts.arch == CPU_OCTEON && ! octeon_use_unalign && (strcmp (insn->name, "ulw") == 0 || strcmp (insn->name, "uld") == 0 || strcmp (insn->name, "usw") == 0 || strcmp (insn->name, "usd") == 0)) { static char buf[120]; sprintf (buf, _("Octeon specific unaligned load/store instructions are not allowed with -mno-octeon-useun")); insn_error = buf; return; } /* Issue a warning message for MIPS unaligned load/store instructions used when octeon_use_unalign is set. */ if (mips_opts.arch == CPU_OCTEON && octeon_use_unalign && (strcmp (insn->name, "lwl") == 0 || strcmp (insn->name, "lwr") == 0 || strcmp (insn->name, "ldl") == 0 || strcmp (insn->name, "ldr") == 0 || strcmp (insn->name, "sdl") == 0 || strcmp (insn->name, "sdr") == 0 || strcmp (insn->name, "swr") == 0 || strcmp (insn->name, "swl") == 0)) { static char buf[100]; sprintf (buf, _("Unaligned load/store instructions are not allowed with -mocteon-useun")); insn_error = buf; return; } } /* Octeon has its own version of dmtc2/dmfc2 instructions, error on other formats. */ if (mips_opts.arch == CPU_OCTEON && (strcmp (insn->name, "dmtc2") == 0 || strcmp (insn->name, "dmfc2") == 0) && (insn->membership & INSN_OCTEON) != INSN_OCTEON) { static char buf[100]; sprintf (buf, _("opcode not supported in %s"), mips_cpu_info_from_arch (mips_opts.arch)->name); insn_error = buf; ok = FALSE; } if (! ok) { if (insn + 1 < &mips_opcodes[NUMOPCODES] && strcmp (insn->name, insn[1].name) == 0) { ++insn; continue; } else { if (!insn_error) { static char buf[100]; sprintf (buf, _("opcode not supported on this processor: %s (%s)"), mips_cpu_info_from_arch (mips_opts.arch)->name, mips_cpu_info_from_isa (mips_opts.isa)->name); insn_error = buf; } if (save_c) *(--s) = save_c; return; } } create_insn (ip, insn); insn_error = NULL; argnum = 1; for (args = insn->args;; ++args) { int is_mdmx; s += strspn (s, " \t"); is_mdmx = 0; switch (*args) { case '\0': /* end of args */ if (*s == '\0') return; break; case '2': /* dsp 2-bit unsigned immediate in bit 11 */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number != 1 && (unsigned long) imm_expr.X_add_number != 3) { as_bad (_("BALIGN immediate not 1 or 3 (%lu)"), (unsigned long) imm_expr.X_add_number); } INSERT_OPERAND (BP, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case '3': /* dsp 3-bit unsigned immediate in bit 21 */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number & ~OP_MASK_SA3) { as_bad (_("DSP immediate not in range 0..%d (%lu)"), OP_MASK_SA3, (unsigned long) imm_expr.X_add_number); } INSERT_OPERAND (SA3, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case '4': /* dsp 4-bit unsigned immediate in bit 21 */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number & ~OP_MASK_SA4) { as_bad (_("DSP immediate not in range 0..%d (%lu)"), OP_MASK_SA4, (unsigned long) imm_expr.X_add_number); } INSERT_OPERAND (SA4, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case '5': /* dsp 8-bit unsigned immediate in bit 16 */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number & ~OP_MASK_IMM8) { as_bad (_("DSP immediate not in range 0..%d (%lu)"), OP_MASK_IMM8, (unsigned long) imm_expr.X_add_number); } INSERT_OPERAND (IMM8, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case '6': /* dsp 5-bit unsigned immediate in bit 21 */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number & ~OP_MASK_RS) { as_bad (_("DSP immediate not in range 0..%d (%lu)"), OP_MASK_RS, (unsigned long) imm_expr.X_add_number); } INSERT_OPERAND (RS, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case '7': /* four dsp accumulators in bits 11,12 */ if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' && s[3] >= '0' && s[3] <= '3') { regno = s[3] - '0'; s += 4; INSERT_OPERAND (DSPACC, *ip, regno); continue; } else as_bad (_("Invalid dsp acc register")); break; case '8': /* dsp 6-bit unsigned immediate in bit 11 */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number & ~OP_MASK_WRDSP) { as_bad (_("DSP immediate not in range 0..%d (%lu)"), OP_MASK_WRDSP, (unsigned long) imm_expr.X_add_number); } INSERT_OPERAND (WRDSP, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case '9': /* four dsp accumulators in bits 21,22 */ if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' && s[3] >= '0' && s[3] <= '3') { regno = s[3] - '0'; s += 4; INSERT_OPERAND (DSPACC_S, *ip, regno); continue; } else as_bad (_("Invalid dsp acc register")); break; case '0': /* dsp 6-bit signed immediate in bit 20 */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); min_range = -((OP_MASK_DSPSFT + 1) >> 1); max_range = ((OP_MASK_DSPSFT + 1) >> 1) - 1; if (imm_expr.X_add_number < min_range || imm_expr.X_add_number > max_range) { as_bad (_("DSP immediate not in range %ld..%ld (%ld)"), (long) min_range, (long) max_range, (long) imm_expr.X_add_number); } INSERT_OPERAND (DSPSFT, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case '\'': /* dsp 6-bit unsigned immediate in bit 16 */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number & ~OP_MASK_RDDSP) { as_bad (_("DSP immediate not in range 0..%d (%lu)"), OP_MASK_RDDSP, (unsigned long) imm_expr.X_add_number); } INSERT_OPERAND (RDDSP, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case ':': /* dsp 7-bit signed immediate in bit 19 */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); min_range = -((OP_MASK_DSPSFT_7 + 1) >> 1); max_range = ((OP_MASK_DSPSFT_7 + 1) >> 1) - 1; if (imm_expr.X_add_number < min_range || imm_expr.X_add_number > max_range) { as_bad (_("DSP immediate not in range %ld..%ld (%ld)"), (long) min_range, (long) max_range, (long) imm_expr.X_add_number); } INSERT_OPERAND (DSPSFT_7, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case '@': /* dsp 10-bit signed immediate in bit 16 */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); min_range = -((OP_MASK_IMM10 + 1) >> 1); max_range = ((OP_MASK_IMM10 + 1) >> 1) - 1; if (imm_expr.X_add_number < min_range || imm_expr.X_add_number > max_range) { as_bad (_("DSP immediate not in range %ld..%ld (%ld)"), (long) min_range, (long) max_range, (long) imm_expr.X_add_number); } INSERT_OPERAND (IMM10, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case '!': /* MT usermode flag bit. */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number & ~OP_MASK_MT_U) as_bad (_("MT usermode bit not 0 or 1 (%lu)"), (unsigned long) imm_expr.X_add_number); INSERT_OPERAND (MT_U, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case '$': /* MT load high flag bit. */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number & ~OP_MASK_MT_H) as_bad (_("MT load high bit not 0 or 1 (%lu)"), (unsigned long) imm_expr.X_add_number); INSERT_OPERAND (MT_H, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case '*': /* four dsp accumulators in bits 18,19 */ if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' && s[3] >= '0' && s[3] <= '3') { regno = s[3] - '0'; s += 4; INSERT_OPERAND (MTACC_T, *ip, regno); continue; } else as_bad (_("Invalid dsp/smartmips acc register")); break; case '&': /* four dsp accumulators in bits 13,14 */ if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' && s[3] >= '0' && s[3] <= '3') { regno = s[3] - '0'; s += 4; INSERT_OPERAND (MTACC_D, *ip, regno); continue; } else as_bad (_("Invalid dsp/smartmips acc register")); break; case ',': ++argnum; if (*s++ == *args) continue; s--; switch (*++args) { case 'r': case 'v': INSERT_OPERAND (RS, *ip, lastregno); continue; case 'w': INSERT_OPERAND (RT, *ip, lastregno); continue; case 'W': INSERT_OPERAND (FT, *ip, lastregno); continue; case 'V': INSERT_OPERAND (FS, *ip, lastregno); continue; } break; case '(': /* Handle optional base register. Either the base register is omitted or we must have a left paren. */ /* This is dependent on the next operand specifier is a base register specification. */ assert (args[1] == 'b' || args[1] == '5' || args[1] == '-' || args[1] == '4'); if (*s == '\0') return; case ')': /* these must match exactly */ case '[': case ']': if (*s++ == *args) continue; break; case '+': /* Opcode extension character. */ switch (*++args) { case '1': /* UDI immediates. */ case '2': case '3': case '4': { const struct mips_immed *imm = mips_immed; while (imm->type && imm->type != *args) ++imm; if (! imm->type) internalError (); my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number & ~imm->mask) { as_warn (_("Illegal %s number (%lu, 0x%lx)"), imm->desc ? imm->desc : ip->insn_mo->name, (unsigned long) imm_expr.X_add_number, (unsigned long) imm_expr.X_add_number); imm_expr.X_add_number &= imm->mask; } ip->insn_opcode |= ((unsigned long) imm_expr.X_add_number << imm->shift); imm_expr.X_op = O_absent; s = expr_end; } continue; case 'A': /* ins/ext position, becomes LSB. */ limlo = 0; limhi = 31; goto do_lsb; case 'E': limlo = 32; limhi = 63; goto do_lsb; do_lsb: my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number < limlo || (unsigned long) imm_expr.X_add_number > limhi) { as_bad (_("Improper position (%lu)"), (unsigned long) imm_expr.X_add_number); imm_expr.X_add_number = limlo; } lastpos = imm_expr.X_add_number; INSERT_OPERAND (SHAMT, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case 'B': /* ins size, becomes MSB. */ limlo = 1; limhi = 32; goto do_msb; case 'F': limlo = 33; limhi = 64; goto do_msb; do_msb: my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); /* Check for negative input so that small negative numbers will not succeed incorrectly. The checks against (pos+size) transitively check "size" itself, assuming that "pos" is reasonable. */ if ((long) imm_expr.X_add_number < 0 || ((unsigned long) imm_expr.X_add_number + lastpos) < limlo || ((unsigned long) imm_expr.X_add_number + lastpos) > limhi) { as_bad (_("Improper insert size (%lu, position %lu)"), (unsigned long) imm_expr.X_add_number, (unsigned long) lastpos); imm_expr.X_add_number = limlo - lastpos; } INSERT_OPERAND (INSMSB, *ip, lastpos + imm_expr.X_add_number - 1); imm_expr.X_op = O_absent; s = expr_end; continue; case 'C': /* ext size, becomes MSBD. */ limlo = 1; limhi = 32; goto do_msbd; case 'G': limlo = 33; limhi = 64; goto do_msbd; case 'H': limlo = 33; limhi = 64; goto do_msbd; do_msbd: my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); /* Check for negative input so that small negative numbers will not succeed incorrectly. The checks against (pos+size) transitively check "size" itself, assuming that "pos" is reasonable. */ if ((long) imm_expr.X_add_number < 0 || ((unsigned long) imm_expr.X_add_number + lastpos) < limlo || ((unsigned long) imm_expr.X_add_number + lastpos) > limhi) { as_bad (_("Improper extract size (%lu, position %lu)"), (unsigned long) imm_expr.X_add_number, (unsigned long) lastpos); imm_expr.X_add_number = limlo - lastpos; } INSERT_OPERAND (EXTMSBD, *ip, imm_expr.X_add_number - 1); imm_expr.X_op = O_absent; s = expr_end; continue; case 'D': /* +D is for disassembly only; never match. */ break; case 'I': /* "+I" is like "I", except that imm2_expr is used. */ my_getExpression (&imm2_expr, s); if (imm2_expr.X_op != O_big && imm2_expr.X_op != O_constant) insn_error = _("absolute expression required"); if (HAVE_32BIT_GPRS) normalize_constant_expr (&imm2_expr); s = expr_end; continue; case 'T': /* Coprocessor register. */ /* +T is for disassembly only; never match. */ break; case 't': /* Coprocessor register number. */ if (s[0] == '$' && ISDIGIT (s[1])) { ++s; regno = 0; do { regno *= 10; regno += *s - '0'; ++s; } while (ISDIGIT (*s)); if (regno > 31) as_bad (_("Invalid register number (%d)"), regno); else { INSERT_OPERAND (RT, *ip, regno); continue; } } else as_bad (_("Invalid coprocessor 0 register number")); break; default: as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"), *args, insn->name, insn->args); /* Further processing is fruitless. */ return; } break; case '<': /* must be at least one digit */ /* * According to the manual, if the shift amount is greater * than 31 or less than 0, then the shift amount should be * mod 32. In reality the mips assembler issues an error. * We issue a warning and mask out all but the low 5 bits. */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number > 31) as_warn (_("Improper shift amount (%lu)"), (unsigned long) imm_expr.X_add_number); INSERT_OPERAND (SHAMT, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case '>': /* shift amount minus 32 */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number < 32 || (unsigned long) imm_expr.X_add_number > 63) break; INSERT_OPERAND (SHAMT, *ip, imm_expr.X_add_number - 32); imm_expr.X_op = O_absent; s = expr_end; continue; case '^': /* must be at least one digit */ /* Decode 5-bits of bbit0/1's bit index amount. If the value is greater than 31, issue a warning and mask out all but the low 5 bits. */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number > 31) { as_warn (_("Improper bit index amount (%lu)"), (unsigned long) imm_expr.X_add_number); imm_expr.X_add_number &= OP_MASK_BITIND; } ip->insn_opcode |= imm_expr.X_add_number << OP_SH_BITIND; imm_expr.X_op = O_absent; s = expr_end; continue; case '~': /* bit index minus 32 */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number < 32 || (unsigned long) imm_expr.X_add_number > 63) break; ip->insn_opcode |= (imm_expr.X_add_number - 32) << OP_SH_BITIND; imm_expr.X_op = O_absent; s = expr_end; continue; case 'k': /* cache code */ case 'h': /* prefx code */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number > 31) as_warn (_("Invalid value for `%s' (%lu)"), ip->insn_mo->name, (unsigned long) imm_expr.X_add_number); if (*args == 'k') INSERT_OPERAND (CACHE, *ip, imm_expr.X_add_number); else INSERT_OPERAND (PREFX, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case 'c': /* break code */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE) as_warn (_("Code for %s not in range 0..1023 (%lu)"), ip->insn_mo->name, (unsigned long) imm_expr.X_add_number); INSERT_OPERAND (CODE, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case 'q': /* lower break code */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE2) as_warn (_("Lower code for %s not in range 0..1023 (%lu)"), ip->insn_mo->name, (unsigned long) imm_expr.X_add_number); INSERT_OPERAND (CODE2, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case 'y': /* Decode 10-bits of seqi/snei's signed constant offset. Issue a warning message if the value is not within the range. */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if (((unsigned long) imm_expr.X_add_number + 0x200) > 1023) { as_warn (_("Illegal 10-bit signed constant (%lu)"), (unsigned long) imm_expr.X_add_number); imm_expr.X_add_number &= OP_MASK_CODE2; } ip->insn_opcode |= (imm_expr.X_add_number & OP_MASK_CODE2) << OP_SH_CODE2; imm_expr.X_op = O_absent; s = expr_end; continue; case 'B': /* 20-bit syscall/break code. */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE20) as_warn (_("Code for %s not in range 0..1048575 (%lu)"), ip->insn_mo->name, (unsigned long) imm_expr.X_add_number); INSERT_OPERAND (CODE20, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case 'C': /* Coprocessor code */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number > OP_MASK_COPZ) { as_warn (_("Coproccesor code > 25 bits (%lu)"), (unsigned long) imm_expr.X_add_number); imm_expr.X_add_number &= OP_MASK_COPZ; } INSERT_OPERAND (COPZ, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case 'J': /* 19-bit wait code. */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE19) { as_warn (_("Illegal 19-bit code (%lu)"), (unsigned long) imm_expr.X_add_number); imm_expr.X_add_number &= OP_MASK_CODE19; } INSERT_OPERAND (CODE19, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case 'P': /* Performance register. */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number != 0 && imm_expr.X_add_number != 1) as_warn (_("Invalid performance register (%lu)"), (unsigned long) imm_expr.X_add_number); INSERT_OPERAND (PERFREG, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case 'G': /* Coprocessor destination register. */ if (((ip->insn_opcode >> OP_SH_OP) & OP_MASK_OP) == OP_OP_COP0) ok = reg_lookup (&s, RTYPE_NUM | RTYPE_CP0, ®no); else ok = reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no); INSERT_OPERAND (RD, *ip, regno); if (ok) { lastregno = regno; continue; } else break; case 'b': /* base register */ case 'd': /* destination register */ case 's': /* source register */ case 't': /* target register */ case 'r': /* both target and source */ case 'v': /* both dest and source */ case 'w': /* both dest and target */ case 'E': /* coprocessor target register */ case 'K': /* 'rdhwr' destination register */ case 'x': /* ignore register name */ case 'z': /* must be zero register */ case 'U': /* destination register (clo/clz). */ case 'g': /* coprocessor destination register */ s_reset = s; if (*args == 'E' || *args == 'K') ok = reg_lookup (&s, RTYPE_NUM, ®no); else { ok = reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no); if (regno == AT && ! mips_opts.noat) as_warn ("Used $at without \".set noat\""); } if (ok) { c = *args; if (*s == ' ') ++s; if (args[1] != *s) { if (c == 'r' || c == 'v' || c == 'w') { regno = lastregno; s = s_reset; ++args; } } /* 'z' only matches $0. */ if (c == 'z' && regno != 0) break; /* Now that we have assembled one operand, we use the args string * to figure out where it goes in the instruction. */ switch (c) { case 'r': case 's': case 'v': case 'b': INSERT_OPERAND (RS, *ip, regno); break; case 'd': case 'G': case 'K': case 'g': INSERT_OPERAND (RD, *ip, regno); break; case 'U': INSERT_OPERAND (RD, *ip, regno); INSERT_OPERAND (RT, *ip, regno); break; case 'w': case 't': case 'E': INSERT_OPERAND (RT, *ip, regno); break; case 'x': /* This case exists because on the r3000 trunc expands into a macro which requires a gp register. On the r6000 or r4000 it is assembled into a single instruction which ignores the register. Thus the insn version is MIPS_ISA2 and uses 'x', and the macro version is MIPS_ISA1 and uses 't'. */ break; case 'z': /* This case is for the div instruction, which acts differently if the destination argument is $0. This only matches $0, and is checked outside the switch. */ break; case 'D': /* Itbl operand; not yet implemented. FIXME ?? */ break; /* What about all other operands like 'i', which can be specified in the opcode table? */ } lastregno = regno; continue; } switch (*args++) { case 'r': case 'v': INSERT_OPERAND (RS, *ip, lastregno); continue; case 'w': INSERT_OPERAND (RT, *ip, lastregno); continue; } break; case 'O': /* MDMX alignment immediate constant. */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number > OP_MASK_ALN) as_warn ("Improper align amount (%ld), using low bits", (long) imm_expr.X_add_number); INSERT_OPERAND (ALN, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case 'Q': /* MDMX vector, element sel, or const. */ if (s[0] != '$') { /* MDMX Immediate. */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number > OP_MASK_FT) as_warn (_("Invalid MDMX Immediate (%ld)"), (long) imm_expr.X_add_number); INSERT_OPERAND (FT, *ip, imm_expr.X_add_number); if (ip->insn_opcode & (OP_MASK_VSEL << OP_SH_VSEL)) ip->insn_opcode |= MDMX_FMTSEL_IMM_QH << OP_SH_VSEL; else ip->insn_opcode |= MDMX_FMTSEL_IMM_OB << OP_SH_VSEL; imm_expr.X_op = O_absent; s = expr_end; continue; } /* Not MDMX Immediate. Fall through. */ case 'X': /* MDMX destination register. */ case 'Y': /* MDMX source register. */ case 'Z': /* MDMX target register. */ is_mdmx = 1; case 'D': /* floating point destination register */ case 'S': /* floating point source register */ case 'T': /* floating point target register */ case 'R': /* floating point source register */ case 'V': case 'W': rtype = RTYPE_FPU; if (is_mdmx || (mips_opts.ase_mdmx && (ip->insn_mo->pinfo & FP_D) && (ip->insn_mo->pinfo & (INSN_COPROC_MOVE_DELAY | INSN_COPROC_MEMORY_DELAY | INSN_LOAD_COPROC_DELAY | INSN_LOAD_MEMORY_DELAY | INSN_STORE_MEMORY)))) rtype |= RTYPE_VEC; s_reset = s; if (mips_opts.arch == CPU_OCTEON && octeon_error_on_unsupported) { insn_error = "opcode not implemented in Octeon"; return; } if (reg_lookup (&s, rtype, ®no)) { if ((regno & 1) != 0 && HAVE_32BIT_FPRS && ! mips_oddfpreg_ok (ip->insn_mo, argnum)) as_warn (_("Float register should be even, was %d"), regno); c = *args; if (*s == ' ') ++s; if (args[1] != *s) { if (c == 'V' || c == 'W') { regno = lastregno; s = s_reset; ++args; } } switch (c) { case 'D': case 'X': INSERT_OPERAND (FD, *ip, regno); break; case 'V': case 'S': case 'Y': INSERT_OPERAND (FS, *ip, regno); break; case 'Q': /* This is like 'Z', but also needs to fix the MDMX vector/scalar select bits. Note that the scalar immediate case is handled above. */ if (*s == '[') { int is_qh = (ip->insn_opcode & (1 << OP_SH_VSEL)); int max_el = (is_qh ? 3 : 7); s++; my_getExpression(&imm_expr, s); check_absolute_expr (ip, &imm_expr); s = expr_end; if (imm_expr.X_add_number > max_el) as_bad(_("Bad element selector %ld"), (long) imm_expr.X_add_number); imm_expr.X_add_number &= max_el; ip->insn_opcode |= (imm_expr.X_add_number << (OP_SH_VSEL + (is_qh ? 2 : 1))); imm_expr.X_op = O_absent; if (*s != ']') as_warn(_("Expecting ']' found '%s'"), s); else s++; } else { if (ip->insn_opcode & (OP_MASK_VSEL << OP_SH_VSEL)) ip->insn_opcode |= (MDMX_FMTSEL_VEC_QH << OP_SH_VSEL); else ip->insn_opcode |= (MDMX_FMTSEL_VEC_OB << OP_SH_VSEL); } /* Fall through */ case 'W': case 'T': case 'Z': INSERT_OPERAND (FT, *ip, regno); break; case 'R': INSERT_OPERAND (FR, *ip, regno); break; } lastregno = regno; continue; } switch (*args++) { case 'V': INSERT_OPERAND (FS, *ip, lastregno); continue; case 'W': INSERT_OPERAND (FT, *ip, lastregno); continue; } break; case 'I': my_getExpression (&imm_expr, s); if (imm_expr.X_op != O_big && imm_expr.X_op != O_constant) insn_error = _("absolute expression required"); if (HAVE_32BIT_GPRS) normalize_constant_expr (&imm_expr); s = expr_end; continue; case 'A': my_getExpression (&offset_expr, s); normalize_address_expr (&offset_expr); *imm_reloc = BFD_RELOC_32; s = expr_end; continue; case 'F': case 'L': case 'f': case 'l': { int f64; int using_gprs; char *save_in; char *err; unsigned char temp[8]; int len; unsigned int length; segT seg; subsegT subseg; char *p; /* These only appear as the last operand in an instruction, and every instruction that accepts them in any variant accepts them in all variants. This means we don't have to worry about backing out any changes if the instruction does not match. The difference between them is the size of the floating point constant and where it goes. For 'F' and 'L' the constant is 64 bits; for 'f' and 'l' it is 32 bits. Where the constant is placed is based on how the MIPS assembler does things: F -- .rdata L -- .lit8 f -- immediate value l -- .lit4 The .lit4 and .lit8 sections are only used if permitted by the -G argument. The code below needs to know whether the target register is 32 or 64 bits wide. It relies on the fact 'f' and 'F' are used with GPR-based instructions and 'l' and 'L' are used with FPR-based instructions. */ f64 = *args == 'F' || *args == 'L'; using_gprs = *args == 'F' || *args == 'f'; save_in = input_line_pointer; input_line_pointer = s; err = md_atof (f64 ? 'd' : 'f', (char *) temp, &len); length = len; s = input_line_pointer; input_line_pointer = save_in; if (err != NULL && *err != '\0') { as_bad (_("Bad floating point constant: %s"), err); memset (temp, '\0', sizeof temp); length = f64 ? 8 : 4; } assert (length == (unsigned) (f64 ? 8 : 4)); if (*args == 'f' || (*args == 'l' && (g_switch_value < 4 || (temp[0] == 0 && temp[1] == 0) || (temp[2] == 0 && temp[3] == 0)))) { imm_expr.X_op = O_constant; if (! target_big_endian) imm_expr.X_add_number = bfd_getl32 (temp); else imm_expr.X_add_number = bfd_getb32 (temp); } else if (length > 4 && ! mips_disable_float_construction /* Constants can only be constructed in GPRs and copied to FPRs if the GPRs are at least as wide as the FPRs. Force the constant into memory if we are using 64-bit FPRs but the GPRs are only 32 bits wide. */ && (using_gprs || ! (HAVE_64BIT_FPRS && HAVE_32BIT_GPRS)) && ((temp[0] == 0 && temp[1] == 0) || (temp[2] == 0 && temp[3] == 0)) && ((temp[4] == 0 && temp[5] == 0) || (temp[6] == 0 && temp[7] == 0))) { /* The value is simple enough to load with a couple of instructions. If using 32-bit registers, set imm_expr to the high order 32 bits and offset_expr to the low order 32 bits. Otherwise, set imm_expr to the entire 64 bit constant. */ if (using_gprs ? HAVE_32BIT_GPRS : HAVE_32BIT_FPRS) { imm_expr.X_op = O_constant; offset_expr.X_op = O_constant; if (! target_big_endian) { imm_expr.X_add_number = bfd_getl32 (temp + 4); offset_expr.X_add_number = bfd_getl32 (temp); } else { imm_expr.X_add_number = bfd_getb32 (temp); offset_expr.X_add_number = bfd_getb32 (temp + 4); } if (offset_expr.X_add_number == 0) offset_expr.X_op = O_absent; } else if (sizeof (imm_expr.X_add_number) > 4) { imm_expr.X_op = O_constant; if (! target_big_endian) imm_expr.X_add_number = bfd_getl64 (temp); else imm_expr.X_add_number = bfd_getb64 (temp); } else { imm_expr.X_op = O_big; imm_expr.X_add_number = 4; if (! target_big_endian) { generic_bignum[0] = bfd_getl16 (temp); generic_bignum[1] = bfd_getl16 (temp + 2); generic_bignum[2] = bfd_getl16 (temp + 4); generic_bignum[3] = bfd_getl16 (temp + 6); } else { generic_bignum[0] = bfd_getb16 (temp + 6); generic_bignum[1] = bfd_getb16 (temp + 4); generic_bignum[2] = bfd_getb16 (temp + 2); generic_bignum[3] = bfd_getb16 (temp); } } } else { const char *newname; segT new_seg; /* Switch to the right section. */ seg = now_seg; subseg = now_subseg; switch (*args) { default: /* unused default case avoids warnings. */ case 'L': newname = RDATA_SECTION_NAME; if (g_switch_value >= 8) newname = ".lit8"; break; case 'F': newname = RDATA_SECTION_NAME; break; case 'l': assert (g_switch_value >= 4); newname = ".lit4"; break; } new_seg = subseg_new (newname, (subsegT) 0); if (IS_ELF) bfd_set_section_flags (stdoutput, new_seg, (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA)); frag_align (*args == 'l' ? 2 : 3, 0, 0); if (IS_ELF && strcmp (TARGET_OS, "elf") != 0) record_alignment (new_seg, 4); else record_alignment (new_seg, *args == 'l' ? 2 : 3); if (seg == now_seg) as_bad (_("Can't use floating point insn in this section")); /* Set the argument to the current address in the section. */ offset_expr.X_op = O_symbol; offset_expr.X_add_symbol = symbol_new ("L0\001", now_seg, (valueT) frag_now_fix (), frag_now); offset_expr.X_add_number = 0; /* Put the floating point number into the section. */ p = frag_more ((int) length); memcpy (p, temp, length); /* Switch back to the original section. */ subseg_set (seg, subseg); } } continue; case 'i': /* 16 bit unsigned immediate */ case 'j': /* 16 bit signed immediate */ *imm_reloc = BFD_RELOC_LO16; if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0) { int more; offsetT minval, maxval; more = (insn + 1 < &mips_opcodes[NUMOPCODES] && strcmp (insn->name, insn[1].name) == 0); /* If the expression was written as an unsigned number, only treat it as signed if there are no more alternatives. */ if (more && *args == 'j' && sizeof (imm_expr.X_add_number) <= 4 && imm_expr.X_op == O_constant && imm_expr.X_add_number < 0 && imm_expr.X_unsigned && HAVE_64BIT_GPRS) break; /* For compatibility with older assemblers, we accept 0x8000-0xffff as signed 16-bit numbers when only signed numbers are allowed. */ if (*args == 'i') minval = 0, maxval = 0xffff; else if (more) minval = -0x8000, maxval = 0x7fff; else minval = -0x8000, maxval = 0xffff; if (imm_expr.X_op != O_constant || imm_expr.X_add_number < minval || imm_expr.X_add_number > maxval) { if (more) break; if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big) as_bad (_("expression out of range")); } } s = expr_end; continue; case 'o': /* 16 bit offset */ /* Check whether there is only a single bracketed expression left. If so, it must be the base register and the constant must be zero. */ if (*s == '(' && strchr (s + 1, '(') == 0) { offset_expr.X_op = O_constant; offset_expr.X_add_number = 0; continue; } /* If this value won't fit into a 16 bit offset, then go find a macro that will generate the 32 bit offset code pattern. */ if (my_getSmallExpression (&offset_expr, offset_reloc, s) == 0 && (offset_expr.X_op != O_constant || offset_expr.X_add_number >= 0x8000 || offset_expr.X_add_number < -0x8000)) break; s = expr_end; continue; case 'p': /* pc relative offset */ *offset_reloc = BFD_RELOC_16_PCREL_S2; my_getExpression (&offset_expr, s); s = expr_end; continue; case 'u': /* upper 16 bits */ if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0 && imm_expr.X_op == O_constant && (imm_expr.X_add_number < 0 || imm_expr.X_add_number >= 0x10000)) as_bad (_("lui expression not in range 0..65535")); s = expr_end; continue; case 'a': /* 26 bit address */ my_getExpression (&offset_expr, s); s = expr_end; *offset_reloc = BFD_RELOC_MIPS_JMP; continue; case 'N': /* 3 bit branch condition code */ case 'M': /* 3 bit compare condition code */ rtype = RTYPE_CCC; if (ip->insn_mo->pinfo & (FP_D| FP_S)) rtype |= RTYPE_FCC; if (!reg_lookup (&s, rtype, ®no)) break; if ((strcmp(str + strlen(str) - 3, ".ps") == 0 || strcmp(str + strlen(str) - 5, "any2f") == 0 || strcmp(str + strlen(str) - 5, "any2t") == 0) && (regno & 1) != 0) as_warn(_("Condition code register should be even for %s, was %d"), str, regno); if ((strcmp(str + strlen(str) - 5, "any4f") == 0 || strcmp(str + strlen(str) - 5, "any4t") == 0) && (regno & 3) != 0) as_warn(_("Condition code register should be 0 or 4 for %s, was %d"), str, regno); if (*args == 'N') INSERT_OPERAND (BCC, *ip, regno); else INSERT_OPERAND (CCC, *ip, regno); continue; case 'H': if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) s += 2; if (ISDIGIT (*s)) { c = 0; do { c *= 10; c += *s - '0'; ++s; } while (ISDIGIT (*s)); } else c = 8; /* Invalid sel value. */ if (c > 7) as_bad (_("invalid coprocessor sub-selection value (0-7)")); ip->insn_opcode |= c; continue; case 'e': /* Must be at least one digit. */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number > (unsigned long) OP_MASK_VECBYTE) { as_bad (_("bad byte vector index (%ld)"), (long) imm_expr.X_add_number); imm_expr.X_add_number = 0; } INSERT_OPERAND (VECBYTE, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case '%': my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number > (unsigned long) OP_MASK_VECALIGN) { as_bad (_("bad byte vector index (%ld)"), (long) imm_expr.X_add_number); imm_expr.X_add_number = 0; } INSERT_OPERAND (VECALIGN, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; default: as_bad (_("bad char = '%c'\n"), *args); internalError (); } break; } /* Args don't match. */ if (insn + 1 < &mips_opcodes[NUMOPCODES] && !strcmp (insn->name, insn[1].name)) { ++insn; s = argsStart; insn_error = _("illegal operands"); continue; } if (save_c) *(--s) = save_c; insn_error = _("illegal operands"); return; } } #define SKIP_SPACE_TABS(S) { while (*(S) == ' ' || *(S) == '\t') ++(S); } /* This routine assembles an instruction into its binary format when assembling for the mips16. As a side effect, it sets one of the global variables imm_reloc or offset_reloc to the type of relocation to do if one of the operands is an address expression. It also sets mips16_small and mips16_ext if the user explicitly requested a small or extended instruction. */ static void mips16_ip (char *str, struct mips_cl_insn *ip) { char *s; const char *args; struct mips_opcode *insn; char *argsstart; unsigned int regno; unsigned int lastregno = 0; char *s_reset; size_t i; insn_error = NULL; mips16_small = FALSE; mips16_ext = FALSE; for (s = str; ISLOWER (*s); ++s) ; switch (*s) { case '\0': break; case ' ': *s++ = '\0'; break; case '.': if (s[1] == 't' && s[2] == ' ') { *s = '\0'; mips16_small = TRUE; s += 3; break; } else if (s[1] == 'e' && s[2] == ' ') { *s = '\0'; mips16_ext = TRUE; s += 3; break; } /* Fall through. */ default: insn_error = _("unknown opcode"); return; } if (mips_opts.noautoextend && ! mips16_ext) mips16_small = TRUE; if ((insn = (struct mips_opcode *) hash_find (mips16_op_hash, str)) == NULL) { insn_error = _("unrecognized opcode"); return; } argsstart = s; for (;;) { bfd_boolean ok; assert (strcmp (insn->name, str) == 0); if (OPCODE_IS_MEMBER (insn, mips_opts.isa, mips_opts.arch)) ok = TRUE; else ok = FALSE; if (! ok) { if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes] && strcmp (insn->name, insn[1].name) == 0) { ++insn; continue; } else { if (!insn_error) { static char buf[100]; sprintf (buf, _("opcode not supported on this processor: %s (%s)"), mips_cpu_info_from_arch (mips_opts.arch)->name, mips_cpu_info_from_isa (mips_opts.isa)->name); insn_error = buf; } return; } } create_insn (ip, insn); imm_expr.X_op = O_absent; imm_reloc[0] = BFD_RELOC_UNUSED; imm_reloc[1] = BFD_RELOC_UNUSED; imm_reloc[2] = BFD_RELOC_UNUSED; imm2_expr.X_op = O_absent; offset_expr.X_op = O_absent; offset_reloc[0] = BFD_RELOC_UNUSED; offset_reloc[1] = BFD_RELOC_UNUSED; offset_reloc[2] = BFD_RELOC_UNUSED; for (args = insn->args; 1; ++args) { int c; if (*s == ' ') ++s; /* In this switch statement we call break if we did not find a match, continue if we did find a match, or return if we are done. */ c = *args; switch (c) { case '\0': if (*s == '\0') { /* Stuff the immediate value in now, if we can. */ if (imm_expr.X_op == O_constant && *imm_reloc > BFD_RELOC_UNUSED && insn->pinfo != INSN_MACRO) { valueT tmp; switch (*offset_reloc) { case BFD_RELOC_MIPS16_HI16_S: tmp = (imm_expr.X_add_number + 0x8000) >> 16; break; case BFD_RELOC_MIPS16_HI16: tmp = imm_expr.X_add_number >> 16; break; case BFD_RELOC_MIPS16_LO16: tmp = ((imm_expr.X_add_number + 0x8000) & 0xffff) - 0x8000; break; case BFD_RELOC_UNUSED: tmp = imm_expr.X_add_number; break; default: internalError (); } *offset_reloc = BFD_RELOC_UNUSED; mips16_immed (NULL, 0, *imm_reloc - BFD_RELOC_UNUSED, tmp, TRUE, mips16_small, mips16_ext, &ip->insn_opcode, &ip->use_extend, &ip->extend); imm_expr.X_op = O_absent; *imm_reloc = BFD_RELOC_UNUSED; } return; } break; case ',': if (*s++ == c) continue; s--; switch (*++args) { case 'v': MIPS16_INSERT_OPERAND (RX, *ip, lastregno); continue; case 'w': MIPS16_INSERT_OPERAND (RY, *ip, lastregno); continue; } break; case '(': case ')': if (*s++ == c) continue; break; case 'v': case 'w': if (s[0] != '$') { if (c == 'v') MIPS16_INSERT_OPERAND (RX, *ip, lastregno); else MIPS16_INSERT_OPERAND (RY, *ip, lastregno); ++args; continue; } /* Fall through. */ case 'x': case 'y': case 'z': case 'Z': case '0': case 'S': case 'R': case 'X': case 'Y': s_reset = s; if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no)) { if (c == 'v' || c == 'w') { if (c == 'v') MIPS16_INSERT_OPERAND (RX, *ip, lastregno); else MIPS16_INSERT_OPERAND (RY, *ip, lastregno); ++args; continue; } break; } if (*s == ' ') ++s; if (args[1] != *s) { if (c == 'v' || c == 'w') { regno = mips16_to_32_reg_map[lastregno]; s = s_reset; ++args; } } switch (c) { case 'x': case 'y': case 'z': case 'v': case 'w': case 'Z': regno = mips32_to_16_reg_map[regno]; break; case '0': if (regno != 0) regno = ILLEGAL_REG; break; case 'S': if (regno != SP) regno = ILLEGAL_REG; break; case 'R': if (regno != RA) regno = ILLEGAL_REG; break; case 'X': case 'Y': if (regno == AT && ! mips_opts.noat) as_warn (_("used $at without \".set noat\"")); break; default: internalError (); } if (regno == ILLEGAL_REG) break; switch (c) { case 'x': case 'v': MIPS16_INSERT_OPERAND (RX, *ip, regno); break; case 'y': case 'w': MIPS16_INSERT_OPERAND (RY, *ip, regno); break; case 'z': MIPS16_INSERT_OPERAND (RZ, *ip, regno); break; case 'Z': MIPS16_INSERT_OPERAND (MOVE32Z, *ip, regno); case '0': case 'S': case 'R': break; case 'X': MIPS16_INSERT_OPERAND (REGR32, *ip, regno); break; case 'Y': regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3); MIPS16_INSERT_OPERAND (REG32R, *ip, regno); break; default: internalError (); } lastregno = regno; continue; case 'P': if (strncmp (s, "$pc", 3) == 0) { s += 3; continue; } break; case '5': case 'H': case 'W': case 'D': case 'j': case 'V': case 'C': case 'U': case 'k': case 'K': i = my_getSmallExpression (&imm_expr, imm_reloc, s); if (i > 0) { if (imm_expr.X_op != O_constant) { mips16_ext = TRUE; ip->use_extend = TRUE; ip->extend = 0; } else { /* We need to relax this instruction. */ *offset_reloc = *imm_reloc; *imm_reloc = (int) BFD_RELOC_UNUSED + c; } s = expr_end; continue; } *imm_reloc = BFD_RELOC_UNUSED; /* Fall through. */ case '<': case '>': case '[': case ']': case '4': case '8': my_getExpression (&imm_expr, s); if (imm_expr.X_op == O_register) { /* What we thought was an expression turned out to be a register. */ if (s[0] == '(' && args[1] == '(') { /* It looks like the expression was omitted before a register indirection, which means that the expression is implicitly zero. We still set up imm_expr, so that we handle explicit extensions correctly. */ imm_expr.X_op = O_constant; imm_expr.X_add_number = 0; *imm_reloc = (int) BFD_RELOC_UNUSED + c; continue; } break; } /* We need to relax this instruction. */ *imm_reloc = (int) BFD_RELOC_UNUSED + c; s = expr_end; continue; case 'p': case 'q': case 'A': case 'B': case 'E': /* We use offset_reloc rather than imm_reloc for the PC relative operands. This lets macros with both immediate and address operands work correctly. */ my_getExpression (&offset_expr, s); if (offset_expr.X_op == O_register) break; /* We need to relax this instruction. */ *offset_reloc = (int) BFD_RELOC_UNUSED + c; s = expr_end; continue; case '6': /* break code */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number > 63) as_warn (_("Invalid value for `%s' (%lu)"), ip->insn_mo->name, (unsigned long) imm_expr.X_add_number); MIPS16_INSERT_OPERAND (IMM6, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; case 'a': /* 26 bit address */ my_getExpression (&offset_expr, s); s = expr_end; *offset_reloc = BFD_RELOC_MIPS16_JMP; ip->insn_opcode <<= 16; continue; case 'l': /* register list for entry macro */ case 'L': /* register list for exit macro */ { int mask; if (c == 'l') mask = 0; else mask = 7 << 3; while (*s != '\0') { unsigned int freg, reg1, reg2; while (*s == ' ' || *s == ',') ++s; if (reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®1)) freg = 0; else if (reg_lookup (&s, RTYPE_FPU, ®1)) freg = 1; else { as_bad (_("can't parse register list")); break; } if (*s == ' ') ++s; if (*s != '-') reg2 = reg1; else { ++s; if (!reg_lookup (&s, freg ? RTYPE_FPU : (RTYPE_GP | RTYPE_NUM), ®2)) { as_bad (_("invalid register list")); break; } } if (freg && reg1 == 0 && reg2 == 0 && c == 'L') { mask &= ~ (7 << 3); mask |= 5 << 3; } else if (freg && reg1 == 0 && reg2 == 1 && c == 'L') { mask &= ~ (7 << 3); mask |= 6 << 3; } else if (reg1 == 4 && reg2 >= 4 && reg2 <= 7 && c != 'L') mask |= (reg2 - 3) << 3; else if (reg1 == 16 && reg2 >= 16 && reg2 <= 17) mask |= (reg2 - 15) << 1; else if (reg1 == RA && reg2 == RA) mask |= 1; else { as_bad (_("invalid register list")); break; } } /* The mask is filled in in the opcode table for the benefit of the disassembler. We remove it before applying the actual mask. */ ip->insn_opcode &= ~ ((7 << 3) << MIPS16OP_SH_IMM6); ip->insn_opcode |= mask << MIPS16OP_SH_IMM6; } continue; case 'm': /* Register list for save insn. */ case 'M': /* Register list for restore insn. */ { int opcode = 0; int framesz = 0, seen_framesz = 0; int args = 0, statics = 0, sregs = 0; while (*s != '\0') { unsigned int reg1, reg2; SKIP_SPACE_TABS (s); while (*s == ',') ++s; SKIP_SPACE_TABS (s); my_getExpression (&imm_expr, s); if (imm_expr.X_op == O_constant) { /* Handle the frame size. */ if (seen_framesz) { as_bad (_("more than one frame size in list")); break; } seen_framesz = 1; framesz = imm_expr.X_add_number; imm_expr.X_op = O_absent; s = expr_end; continue; } if (! reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®1)) { as_bad (_("can't parse register list")); break; } while (*s == ' ') ++s; if (*s != '-') reg2 = reg1; else { ++s; if (! reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®2) || reg2 < reg1) { as_bad (_("can't parse register list")); break; } } while (reg1 <= reg2) { if (reg1 >= 4 && reg1 <= 7) { if (!seen_framesz) /* args $a0-$a3 */ args |= 1 << (reg1 - 4); else /* statics $a0-$a3 */ statics |= 1 << (reg1 - 4); } else if ((reg1 >= 16 && reg1 <= 23) || reg1 == 30) { /* $s0-$s8 */ sregs |= 1 << ((reg1 == 30) ? 8 : (reg1 - 16)); } else if (reg1 == 31) { /* Add $ra to insn. */ opcode |= 0x40; } else { as_bad (_("unexpected register in list")); break; } if (++reg1 == 24) reg1 = 30; } } /* Encode args/statics combination. */ if (args & statics) as_bad (_("arg/static registers overlap")); else if (args == 0xf) /* All $a0-$a3 are args. */ opcode |= MIPS16_ALL_ARGS << 16; else if (statics == 0xf) /* All $a0-$a3 are statics. */ opcode |= MIPS16_ALL_STATICS << 16; else { int narg = 0, nstat = 0; /* Count arg registers. */ while (args & 0x1) { args >>= 1; narg++; } if (args != 0) as_bad (_("invalid arg register list")); /* Count static registers. */ while (statics & 0x8) { statics = (statics << 1) & 0xf; nstat++; } if (statics != 0) as_bad (_("invalid static register list")); /* Encode args/statics. */ opcode |= ((narg << 2) | nstat) << 16; } /* Encode $s0/$s1. */ if (sregs & (1 << 0)) /* $s0 */ opcode |= 0x20; if (sregs & (1 << 1)) /* $s1 */ opcode |= 0x10; sregs >>= 2; if (sregs != 0) { /* Count regs $s2-$s8. */ int nsreg = 0; while (sregs & 1) { sregs >>= 1; nsreg++; } if (sregs != 0) as_bad (_("invalid static register list")); /* Encode $s2-$s8. */ opcode |= nsreg << 24; } /* Encode frame size. */ if (!seen_framesz) as_bad (_("missing frame size")); else if ((framesz & 7) != 0 || framesz < 0 || framesz > 0xff * 8) as_bad (_("invalid frame size")); else if (framesz != 128 || (opcode >> 16) != 0) { framesz /= 8; opcode |= (((framesz & 0xf0) << 16) | (framesz & 0x0f)); } /* Finally build the instruction. */ if ((opcode >> 16) != 0 || framesz == 0) { ip->use_extend = TRUE; ip->extend = opcode >> 16; } ip->insn_opcode |= opcode & 0x7f; } continue; case 'e': /* extend code */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number > 0x7ff) { as_warn (_("Invalid value for `%s' (%lu)"), ip->insn_mo->name, (unsigned long) imm_expr.X_add_number); imm_expr.X_add_number &= 0x7ff; } ip->insn_opcode |= imm_expr.X_add_number; imm_expr.X_op = O_absent; s = expr_end; continue; default: internalError (); } break; } /* Args don't match. */ if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes] && strcmp (insn->name, insn[1].name) == 0) { ++insn; s = argsstart; continue; } insn_error = _("illegal operands"); return; } } /* This structure holds information we know about a mips16 immediate argument type. */ struct mips16_immed_operand { /* The type code used in the argument string in the opcode table. */ int type; /* The number of bits in the short form of the opcode. */ int nbits; /* The number of bits in the extended form of the opcode. */ int extbits; /* The amount by which the short form is shifted when it is used; for example, the sw instruction has a shift count of 2. */ int shift; /* The amount by which the short form is shifted when it is stored into the instruction code. */ int op_shift; /* Non-zero if the short form is unsigned. */ int unsp; /* Non-zero if the extended form is unsigned. */ int extu; /* Non-zero if the value is PC relative. */ int pcrel; }; /* The mips16 immediate operand types. */ static const struct mips16_immed_operand mips16_immed_operands[] = { { '<', 3, 5, 0, MIPS16OP_SH_RZ, 1, 1, 0 }, { '>', 3, 5, 0, MIPS16OP_SH_RX, 1, 1, 0 }, { '[', 3, 6, 0, MIPS16OP_SH_RZ, 1, 1, 0 }, { ']', 3, 6, 0, MIPS16OP_SH_RX, 1, 1, 0 }, { '4', 4, 15, 0, MIPS16OP_SH_IMM4, 0, 0, 0 }, { '5', 5, 16, 0, MIPS16OP_SH_IMM5, 1, 0, 0 }, { 'H', 5, 16, 1, MIPS16OP_SH_IMM5, 1, 0, 0 }, { 'W', 5, 16, 2, MIPS16OP_SH_IMM5, 1, 0, 0 }, { 'D', 5, 16, 3, MIPS16OP_SH_IMM5, 1, 0, 0 }, { 'j', 5, 16, 0, MIPS16OP_SH_IMM5, 0, 0, 0 }, { '8', 8, 16, 0, MIPS16OP_SH_IMM8, 1, 0, 0 }, { 'V', 8, 16, 2, MIPS16OP_SH_IMM8, 1, 0, 0 }, { 'C', 8, 16, 3, MIPS16OP_SH_IMM8, 1, 0, 0 }, { 'U', 8, 16, 0, MIPS16OP_SH_IMM8, 1, 1, 0 }, { 'k', 8, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 0 }, { 'K', 8, 16, 3, MIPS16OP_SH_IMM8, 0, 0, 0 }, { 'p', 8, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 1 }, { 'q', 11, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 1 }, { 'A', 8, 16, 2, MIPS16OP_SH_IMM8, 1, 0, 1 }, { 'B', 5, 16, 3, MIPS16OP_SH_IMM5, 1, 0, 1 }, { 'E', 5, 16, 2, MIPS16OP_SH_IMM5, 1, 0, 1 } }; #define MIPS16_NUM_IMMED \ (sizeof mips16_immed_operands / sizeof mips16_immed_operands[0]) /* Handle a mips16 instruction with an immediate value. This or's the small immediate value into *INSN. It sets *USE_EXTEND to indicate whether an extended value is needed; if one is needed, it sets *EXTEND to the value. The argument type is TYPE. The value is VAL. If SMALL is true, an unextended opcode was explicitly requested. If EXT is true, an extended opcode was explicitly requested. If WARN is true, warn if EXT does not match reality. */ static void mips16_immed (char *file, unsigned int line, int type, offsetT val, bfd_boolean warn, bfd_boolean small, bfd_boolean ext, unsigned long *insn, bfd_boolean *use_extend, unsigned short *extend) { const struct mips16_immed_operand *op; int mintiny, maxtiny; bfd_boolean needext; op = mips16_immed_operands; while (op->type != type) { ++op; assert (op < mips16_immed_operands + MIPS16_NUM_IMMED); } if (op->unsp) { if (type == '<' || type == '>' || type == '[' || type == ']') { mintiny = 1; maxtiny = 1 << op->nbits; } else { mintiny = 0; maxtiny = (1 << op->nbits) - 1; } } else { mintiny = - (1 << (op->nbits - 1)); maxtiny = (1 << (op->nbits - 1)) - 1; } /* Branch offsets have an implicit 0 in the lowest bit. */ if (type == 'p' || type == 'q') val /= 2; if ((val & ((1 << op->shift) - 1)) != 0 || val < (mintiny << op->shift) || val > (maxtiny << op->shift)) needext = TRUE; else needext = FALSE; if (warn && ext && ! needext) as_warn_where (file, line, _("extended operand requested but not required")); if (small && needext) as_bad_where (file, line, _("invalid unextended operand value")); if (small || (! ext && ! needext)) { int insnval; *use_extend = FALSE; insnval = ((val >> op->shift) & ((1 << op->nbits) - 1)); insnval <<= op->op_shift; *insn |= insnval; } else { long minext, maxext; int extval; if (op->extu) { minext = 0; maxext = (1 << op->extbits) - 1; } else { minext = - (1 << (op->extbits - 1)); maxext = (1 << (op->extbits - 1)) - 1; } if (val < minext || val > maxext) as_bad_where (file, line, _("operand value out of range for instruction")); *use_extend = TRUE; if (op->extbits == 16) { extval = ((val >> 11) & 0x1f) | (val & 0x7e0); val &= 0x1f; } else if (op->extbits == 15) { extval = ((val >> 11) & 0xf) | (val & 0x7f0); val &= 0xf; } else { extval = ((val & 0x1f) << 6) | (val & 0x20); val = 0; } *extend = (unsigned short) extval; *insn |= val; } } struct percent_op_match { const char *str; bfd_reloc_code_real_type reloc; }; static const struct percent_op_match mips_percent_op[] = { {"%lo", BFD_RELOC_LO16}, #ifdef OBJ_ELF {"%call_hi", BFD_RELOC_MIPS_CALL_HI16}, {"%call_lo", BFD_RELOC_MIPS_CALL_LO16}, {"%call16", BFD_RELOC_MIPS_CALL16}, {"%got_disp", BFD_RELOC_MIPS_GOT_DISP}, {"%got_page", BFD_RELOC_MIPS_GOT_PAGE}, {"%got_ofst", BFD_RELOC_MIPS_GOT_OFST}, {"%got_hi", BFD_RELOC_MIPS_GOT_HI16}, {"%got_lo", BFD_RELOC_MIPS_GOT_LO16}, {"%got", BFD_RELOC_MIPS_GOT16}, {"%gp_rel", BFD_RELOC_GPREL16}, {"%half", BFD_RELOC_16}, {"%highest", BFD_RELOC_MIPS_HIGHEST}, {"%higher", BFD_RELOC_MIPS_HIGHER}, {"%neg", BFD_RELOC_MIPS_SUB}, {"%tlsgd", BFD_RELOC_MIPS_TLS_GD}, {"%tlsldm", BFD_RELOC_MIPS_TLS_LDM}, {"%dtprel_hi", BFD_RELOC_MIPS_TLS_DTPREL_HI16}, {"%dtprel_lo", BFD_RELOC_MIPS_TLS_DTPREL_LO16}, {"%tprel_hi", BFD_RELOC_MIPS_TLS_TPREL_HI16}, {"%tprel_lo", BFD_RELOC_MIPS_TLS_TPREL_LO16}, {"%gottprel", BFD_RELOC_MIPS_TLS_GOTTPREL}, #endif {"%hi", BFD_RELOC_HI16_S} }; static const struct percent_op_match mips16_percent_op[] = { {"%lo", BFD_RELOC_MIPS16_LO16}, {"%gprel", BFD_RELOC_MIPS16_GPREL}, {"%hi", BFD_RELOC_MIPS16_HI16_S} }; /* Return true if *STR points to a relocation operator. When returning true, move *STR over the operator and store its relocation code in *RELOC. Leave both *STR and *RELOC alone when returning false. */ static bfd_boolean parse_relocation (char **str, bfd_reloc_code_real_type *reloc) { const struct percent_op_match *percent_op; size_t limit, i; if (mips_opts.mips16) { percent_op = mips16_percent_op; limit = ARRAY_SIZE (mips16_percent_op); } else { percent_op = mips_percent_op; limit = ARRAY_SIZE (mips_percent_op); } for (i = 0; i < limit; i++) if (strncasecmp (*str, percent_op[i].str, strlen (percent_op[i].str)) == 0) { int len = strlen (percent_op[i].str); if (!ISSPACE ((*str)[len]) && (*str)[len] != '(') continue; *str += strlen (percent_op[i].str); *reloc = percent_op[i].reloc; /* Check whether the output BFD supports this relocation. If not, issue an error and fall back on something safe. */ if (!bfd_reloc_type_lookup (stdoutput, percent_op[i].reloc)) { as_bad ("relocation %s isn't supported by the current ABI", percent_op[i].str); *reloc = BFD_RELOC_UNUSED; } return TRUE; } return FALSE; } /* Parse string STR as a 16-bit relocatable operand. Store the expression in *EP and the relocations in the array starting at RELOC. Return the number of relocation operators used. On exit, EXPR_END points to the first character after the expression. */ static size_t my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc, char *str) { bfd_reloc_code_real_type reversed_reloc[3]; size_t reloc_index, i; int crux_depth, str_depth; char *crux; /* Search for the start of the main expression, recoding relocations in REVERSED_RELOC. End the loop with CRUX pointing to the start of the main expression and with CRUX_DEPTH containing the number of open brackets at that point. */ reloc_index = -1; str_depth = 0; do { reloc_index++; crux = str; crux_depth = str_depth; /* Skip over whitespace and brackets, keeping count of the number of brackets. */ while (*str == ' ' || *str == '\t' || *str == '(') if (*str++ == '(') str_depth++; } while (*str == '%' && reloc_index < (HAVE_NEWABI ? 3 : 1) && parse_relocation (&str, &reversed_reloc[reloc_index])); my_getExpression (ep, crux); str = expr_end; /* Match every open bracket. */ while (crux_depth > 0 && (*str == ')' || *str == ' ' || *str == '\t')) if (*str++ == ')') crux_depth--; if (crux_depth > 0) as_bad ("unclosed '('"); expr_end = str; if (reloc_index != 0) { prev_reloc_op_frag = frag_now; for (i = 0; i < reloc_index; i++) reloc[i] = reversed_reloc[reloc_index - 1 - i]; } return reloc_index; } static void my_getExpression (expressionS *ep, char *str) { char *save_in; valueT val; save_in = input_line_pointer; input_line_pointer = str; expression (ep); expr_end = input_line_pointer; input_line_pointer = save_in; /* If we are in mips16 mode, and this is an expression based on `.', then we bump the value of the symbol by 1 since that is how other text symbols are handled. We don't bother to handle complex expressions, just `.' plus or minus a constant. */ if (mips_opts.mips16 && ep->X_op == O_symbol && strcmp (S_GET_NAME (ep->X_add_symbol), FAKE_LABEL_NAME) == 0 && S_GET_SEGMENT (ep->X_add_symbol) == now_seg && symbol_get_frag (ep->X_add_symbol) == frag_now && symbol_constant_p (ep->X_add_symbol) && (val = S_GET_VALUE (ep->X_add_symbol)) == frag_now_fix ()) S_SET_VALUE (ep->X_add_symbol, val + 1); } /* Turn a string in input_line_pointer into a floating point constant of type TYPE, and store the appropriate bytes in *LITP. The number of LITTLENUMS emitted is stored in *SIZEP. An error message is returned, or NULL on OK. */ char * md_atof (int type, char *litP, int *sizeP) { int prec; LITTLENUM_TYPE words[4]; char *t; int i; switch (type) { case 'f': prec = 2; break; case 'd': prec = 4; break; default: *sizeP = 0; return _("bad call to md_atof"); } t = atof_ieee (input_line_pointer, type, words); if (t) input_line_pointer = t; *sizeP = prec * 2; if (! target_big_endian) { for (i = prec - 1; i >= 0; i--) { md_number_to_chars (litP, words[i], 2); litP += 2; } } else { for (i = 0; i < prec; i++) { md_number_to_chars (litP, words[i], 2); litP += 2; } } return NULL; } void md_number_to_chars (char *buf, valueT val, int n) { if (target_big_endian) number_to_chars_bigendian (buf, val, n); else number_to_chars_littleendian (buf, val, n); } #ifdef OBJ_ELF static int support_64bit_objects(void) { const char **list, **l; int yes; list = bfd_target_list (); for (l = list; *l != NULL; l++) #ifdef TE_TMIPS /* This is traditional mips */ if (strcmp (*l, "elf64-tradbigmips") == 0 || strcmp (*l, "elf64-tradlittlemips") == 0) #else if (strcmp (*l, "elf64-bigmips") == 0 || strcmp (*l, "elf64-littlemips") == 0) #endif break; yes = (*l != NULL); free (list); return yes; } #endif /* OBJ_ELF */ const char *md_shortopts = "O::g::G:"; struct option md_longopts[] = { /* Options which specify architecture. */ #define OPTION_ARCH_BASE (OPTION_MD_BASE) #define OPTION_MARCH (OPTION_ARCH_BASE + 0) {"march", required_argument, NULL, OPTION_MARCH}, #define OPTION_MTUNE (OPTION_ARCH_BASE + 1) {"mtune", required_argument, NULL, OPTION_MTUNE}, #define OPTION_MIPS1 (OPTION_ARCH_BASE + 2) {"mips0", no_argument, NULL, OPTION_MIPS1}, {"mips1", no_argument, NULL, OPTION_MIPS1}, #define OPTION_MIPS2 (OPTION_ARCH_BASE + 3) {"mips2", no_argument, NULL, OPTION_MIPS2}, #define OPTION_MIPS3 (OPTION_ARCH_BASE + 4) {"mips3", no_argument, NULL, OPTION_MIPS3}, #define OPTION_MIPS4 (OPTION_ARCH_BASE + 5) {"mips4", no_argument, NULL, OPTION_MIPS4}, #define OPTION_MIPS5 (OPTION_ARCH_BASE + 6) {"mips5", no_argument, NULL, OPTION_MIPS5}, #define OPTION_MIPS32 (OPTION_ARCH_BASE + 7) {"mips32", no_argument, NULL, OPTION_MIPS32}, #define OPTION_MIPS64 (OPTION_ARCH_BASE + 8) {"mips64", no_argument, NULL, OPTION_MIPS64}, #define OPTION_MIPS32R2 (OPTION_ARCH_BASE + 9) {"mips32r2", no_argument, NULL, OPTION_MIPS32R2}, #define OPTION_MIPS64R2 (OPTION_ARCH_BASE + 10) {"mips64r2", no_argument, NULL, OPTION_MIPS64R2}, /* Options which specify Application Specific Extensions (ASEs). */ #define OPTION_ASE_BASE (OPTION_ARCH_BASE + 11) #define OPTION_MIPS16 (OPTION_ASE_BASE + 0) {"mips16", no_argument, NULL, OPTION_MIPS16}, #define OPTION_NO_MIPS16 (OPTION_ASE_BASE + 1) {"no-mips16", no_argument, NULL, OPTION_NO_MIPS16}, #define OPTION_MIPS3D (OPTION_ASE_BASE + 2) {"mips3d", no_argument, NULL, OPTION_MIPS3D}, #define OPTION_NO_MIPS3D (OPTION_ASE_BASE + 3) {"no-mips3d", no_argument, NULL, OPTION_NO_MIPS3D}, #define OPTION_MDMX (OPTION_ASE_BASE + 4) {"mdmx", no_argument, NULL, OPTION_MDMX}, #define OPTION_NO_MDMX (OPTION_ASE_BASE + 5) {"no-mdmx", no_argument, NULL, OPTION_NO_MDMX}, #define OPTION_DSP (OPTION_ASE_BASE + 6) {"mdsp", no_argument, NULL, OPTION_DSP}, #define OPTION_NO_DSP (OPTION_ASE_BASE + 7) {"mno-dsp", no_argument, NULL, OPTION_NO_DSP}, #define OPTION_MT (OPTION_ASE_BASE + 8) {"mmt", no_argument, NULL, OPTION_MT}, #define OPTION_NO_MT (OPTION_ASE_BASE + 9) {"mno-mt", no_argument, NULL, OPTION_NO_MT}, #define OPTION_SMARTMIPS (OPTION_ASE_BASE + 10) {"msmartmips", no_argument, NULL, OPTION_SMARTMIPS}, #define OPTION_NO_SMARTMIPS (OPTION_ASE_BASE + 11) {"mno-smartmips", no_argument, NULL, OPTION_NO_SMARTMIPS}, #define OPTION_DSPR2 (OPTION_ASE_BASE + 12) {"mdspr2", no_argument, NULL, OPTION_DSPR2}, #define OPTION_NO_DSPR2 (OPTION_ASE_BASE + 13) {"mno-dspr2", no_argument, NULL, OPTION_NO_DSPR2}, /* Old-style architecture options. Don't add more of these. */ #define OPTION_COMPAT_ARCH_BASE (OPTION_ASE_BASE + 14) #define OPTION_M4650 (OPTION_COMPAT_ARCH_BASE + 0) {"m4650", no_argument, NULL, OPTION_M4650}, #define OPTION_NO_M4650 (OPTION_COMPAT_ARCH_BASE + 1) {"no-m4650", no_argument, NULL, OPTION_NO_M4650}, #define OPTION_M4010 (OPTION_COMPAT_ARCH_BASE + 2) {"m4010", no_argument, NULL, OPTION_M4010}, #define OPTION_NO_M4010 (OPTION_COMPAT_ARCH_BASE + 3) {"no-m4010", no_argument, NULL, OPTION_NO_M4010}, #define OPTION_M4100 (OPTION_COMPAT_ARCH_BASE + 4) {"m4100", no_argument, NULL, OPTION_M4100}, #define OPTION_NO_M4100 (OPTION_COMPAT_ARCH_BASE + 5) {"no-m4100", no_argument, NULL, OPTION_NO_M4100}, #define OPTION_M3900 (OPTION_COMPAT_ARCH_BASE + 6) {"m3900", no_argument, NULL, OPTION_M3900}, #define OPTION_NO_M3900 (OPTION_COMPAT_ARCH_BASE + 7) {"no-m3900", no_argument, NULL, OPTION_NO_M3900}, /* Options which enable bug fixes. */ #define OPTION_FIX_BASE (OPTION_COMPAT_ARCH_BASE + 8) #define OPTION_M7000_HILO_FIX (OPTION_FIX_BASE + 0) {"mfix7000", no_argument, NULL, OPTION_M7000_HILO_FIX}, #define OPTION_MNO_7000_HILO_FIX (OPTION_FIX_BASE + 1) {"no-fix-7000", no_argument, NULL, OPTION_MNO_7000_HILO_FIX}, {"mno-fix7000", no_argument, NULL, OPTION_MNO_7000_HILO_FIX}, #define OPTION_FIX_VR4120 (OPTION_FIX_BASE + 2) #define OPTION_NO_FIX_VR4120 (OPTION_FIX_BASE + 3) {"mfix-vr4120", no_argument, NULL, OPTION_FIX_VR4120}, {"mno-fix-vr4120", no_argument, NULL, OPTION_NO_FIX_VR4120}, #define OPTION_FIX_VR4130 (OPTION_FIX_BASE + 4) #define OPTION_NO_FIX_VR4130 (OPTION_FIX_BASE + 5) {"mfix-vr4130", no_argument, NULL, OPTION_FIX_VR4130}, {"mno-fix-vr4130", no_argument, NULL, OPTION_NO_FIX_VR4130}, /* Miscellaneous options. */ #define OPTION_MISC_BASE (OPTION_FIX_BASE + 6) #define OPTION_TRAP (OPTION_MISC_BASE + 0) {"trap", no_argument, NULL, OPTION_TRAP}, {"no-break", no_argument, NULL, OPTION_TRAP}, #define OPTION_BREAK (OPTION_MISC_BASE + 1) {"break", no_argument, NULL, OPTION_BREAK}, {"no-trap", no_argument, NULL, OPTION_BREAK}, #define OPTION_EB (OPTION_MISC_BASE + 2) {"EB", no_argument, NULL, OPTION_EB}, #define OPTION_EL (OPTION_MISC_BASE + 3) {"EL", no_argument, NULL, OPTION_EL}, #define OPTION_FP32 (OPTION_MISC_BASE + 4) {"mfp32", no_argument, NULL, OPTION_FP32}, #define OPTION_GP32 (OPTION_MISC_BASE + 5) {"mgp32", no_argument, NULL, OPTION_GP32}, #define OPTION_CONSTRUCT_FLOATS (OPTION_MISC_BASE + 6) {"construct-floats", no_argument, NULL, OPTION_CONSTRUCT_FLOATS}, #define OPTION_NO_CONSTRUCT_FLOATS (OPTION_MISC_BASE + 7) {"no-construct-floats", no_argument, NULL, OPTION_NO_CONSTRUCT_FLOATS}, #define OPTION_FP64 (OPTION_MISC_BASE + 8) {"mfp64", no_argument, NULL, OPTION_FP64}, #define OPTION_GP64 (OPTION_MISC_BASE + 9) {"mgp64", no_argument, NULL, OPTION_GP64}, #define OPTION_RELAX_BRANCH (OPTION_MISC_BASE + 10) #define OPTION_NO_RELAX_BRANCH (OPTION_MISC_BASE + 11) {"relax-branch", no_argument, NULL, OPTION_RELAX_BRANCH}, {"no-relax-branch", no_argument, NULL, OPTION_NO_RELAX_BRANCH}, #define OPTION_MSHARED (OPTION_MISC_BASE + 12) #define OPTION_MNO_SHARED (OPTION_MISC_BASE + 13) {"mshared", no_argument, NULL, OPTION_MSHARED}, {"mno-shared", no_argument, NULL, OPTION_MNO_SHARED}, #define OPTION_MSYM32 (OPTION_MISC_BASE + 14) #define OPTION_MNO_SYM32 (OPTION_MISC_BASE + 15) {"msym32", no_argument, NULL, OPTION_MSYM32}, {"mno-sym32", no_argument, NULL, OPTION_MNO_SYM32}, /* ELF-specific options. */ #ifdef OBJ_ELF #define OPTION_ELF_BASE (OPTION_MISC_BASE + 16) #define OPTION_CALL_SHARED (OPTION_ELF_BASE + 0) {"KPIC", no_argument, NULL, OPTION_CALL_SHARED}, {"call_shared", no_argument, NULL, OPTION_CALL_SHARED}, #define OPTION_NON_SHARED (OPTION_ELF_BASE + 1) {"non_shared", no_argument, NULL, OPTION_NON_SHARED}, #define OPTION_XGOT (OPTION_ELF_BASE + 2) {"xgot", no_argument, NULL, OPTION_XGOT}, #define OPTION_MABI (OPTION_ELF_BASE + 3) {"mabi", required_argument, NULL, OPTION_MABI}, #define OPTION_32 (OPTION_ELF_BASE + 4) {"32", no_argument, NULL, OPTION_32}, #define OPTION_N32 (OPTION_ELF_BASE + 5) {"n32", no_argument, NULL, OPTION_N32}, #define OPTION_64 (OPTION_ELF_BASE + 6) {"64", no_argument, NULL, OPTION_64}, #define OPTION_MDEBUG (OPTION_ELF_BASE + 7) {"mdebug", no_argument, NULL, OPTION_MDEBUG}, #define OPTION_NO_MDEBUG (OPTION_ELF_BASE + 8) {"no-mdebug", no_argument, NULL, OPTION_NO_MDEBUG}, #define OPTION_PDR (OPTION_ELF_BASE + 9) {"mpdr", no_argument, NULL, OPTION_PDR}, #define OPTION_NO_PDR (OPTION_ELF_BASE + 10) {"mno-pdr", no_argument, NULL, OPTION_NO_PDR}, #define OPTION_MVXWORKS_PIC (OPTION_ELF_BASE + 11) {"mvxworks-pic", no_argument, NULL, OPTION_MVXWORKS_PIC}, #endif /* OBJ_ELF */ #define OPTION_MOCTEON_UNSUPPORTED (OPTION_MISC_BASE + 28) #define OPTION_NO_MOCTEON_UNSUPPORTED (OPTION_MISC_BASE + 29) {"mocteon-unsupported", no_argument, NULL, OPTION_MOCTEON_UNSUPPORTED}, {"mno-octeon-unsupported", no_argument, NULL, OPTION_NO_MOCTEON_UNSUPPORTED}, #define OPTION_MOCTEON_USEUN (OPTION_MISC_BASE + 30) #define OPTION_NO_MOCTEON_USEUN (OPTION_MISC_BASE + 31) {"mocteon-useun", no_argument, NULL, OPTION_MOCTEON_USEUN}, {"mno-octeon-useun", no_argument, NULL, OPTION_NO_MOCTEON_USEUN}, {NULL, no_argument, NULL, 0} }; size_t md_longopts_size = sizeof (md_longopts); /* Set STRING_PTR (either &mips_arch_string or &mips_tune_string) to NEW_VALUE. Warn if another value was already specified. Note: we have to defer parsing the -march and -mtune arguments in order to handle 'from-abi' correctly, since the ABI might be specified in a later argument. */ static void mips_set_option_string (const char **string_ptr, const char *new_value) { if (*string_ptr != 0 && strcasecmp (*string_ptr, new_value) != 0) as_warn (_("A different %s was already specified, is now %s"), string_ptr == &mips_arch_string ? "-march" : "-mtune", new_value); *string_ptr = new_value; } int md_parse_option (int c, char *arg) { switch (c) { case OPTION_CONSTRUCT_FLOATS: mips_disable_float_construction = 0; break; case OPTION_NO_CONSTRUCT_FLOATS: mips_disable_float_construction = 1; break; case OPTION_TRAP: mips_trap = 1; break; case OPTION_BREAK: mips_trap = 0; break; case OPTION_EB: target_big_endian = 1; break; case OPTION_EL: target_big_endian = 0; break; case OPTION_MOCTEON_UNSUPPORTED: octeon_error_on_unsupported = 1; break; case OPTION_NO_MOCTEON_UNSUPPORTED: octeon_error_on_unsupported = 0; break; case OPTION_MOCTEON_USEUN: octeon_use_unalign = 1; break; case OPTION_NO_MOCTEON_USEUN: octeon_use_unalign = 0; break; case 'O': if (arg && arg[0] == '0') mips_optimize = 1; else mips_optimize = 2; break; case 'g': if (arg == NULL) mips_debug = 2; else mips_debug = atoi (arg); break; case OPTION_MIPS1: file_mips_isa = ISA_MIPS1; break; case OPTION_MIPS2: file_mips_isa = ISA_MIPS2; break; case OPTION_MIPS3: file_mips_isa = ISA_MIPS3; break; case OPTION_MIPS4: file_mips_isa = ISA_MIPS4; break; case OPTION_MIPS5: file_mips_isa = ISA_MIPS5; break; case OPTION_MIPS32: file_mips_isa = ISA_MIPS32; break; case OPTION_MIPS32R2: file_mips_isa = ISA_MIPS32R2; break; case OPTION_MIPS64R2: file_mips_isa = ISA_MIPS64R2; break; case OPTION_MIPS64: file_mips_isa = ISA_MIPS64; break; case OPTION_MTUNE: mips_set_option_string (&mips_tune_string, arg); break; case OPTION_MARCH: mips_set_option_string (&mips_arch_string, arg); break; case OPTION_M4650: mips_set_option_string (&mips_arch_string, "4650"); mips_set_option_string (&mips_tune_string, "4650"); break; case OPTION_NO_M4650: break; case OPTION_M4010: mips_set_option_string (&mips_arch_string, "4010"); mips_set_option_string (&mips_tune_string, "4010"); break; case OPTION_NO_M4010: break; case OPTION_M4100: mips_set_option_string (&mips_arch_string, "4100"); mips_set_option_string (&mips_tune_string, "4100"); break; case OPTION_NO_M4100: break; case OPTION_M3900: mips_set_option_string (&mips_arch_string, "3900"); mips_set_option_string (&mips_tune_string, "3900"); break; case OPTION_NO_M3900: break; case OPTION_MDMX: mips_opts.ase_mdmx = 1; break; case OPTION_NO_MDMX: mips_opts.ase_mdmx = 0; break; case OPTION_DSP: mips_opts.ase_dsp = 1; mips_opts.ase_dspr2 = 0; break; case OPTION_NO_DSP: mips_opts.ase_dsp = 0; mips_opts.ase_dspr2 = 0; break; case OPTION_DSPR2: mips_opts.ase_dspr2 = 1; mips_opts.ase_dsp = 1; break; case OPTION_NO_DSPR2: mips_opts.ase_dspr2 = 0; mips_opts.ase_dsp = 0; break; case OPTION_MT: mips_opts.ase_mt = 1; break; case OPTION_NO_MT: mips_opts.ase_mt = 0; break; case OPTION_MIPS16: mips_opts.mips16 = 1; mips_no_prev_insn (); break; case OPTION_NO_MIPS16: mips_opts.mips16 = 0; mips_no_prev_insn (); break; case OPTION_MIPS3D: mips_opts.ase_mips3d = 1; break; case OPTION_NO_MIPS3D: mips_opts.ase_mips3d = 0; break; case OPTION_SMARTMIPS: mips_opts.ase_smartmips = 1; break; case OPTION_NO_SMARTMIPS: mips_opts.ase_smartmips = 0; break; case OPTION_FIX_VR4120: mips_fix_vr4120 = 1; break; case OPTION_NO_FIX_VR4120: mips_fix_vr4120 = 0; break; case OPTION_FIX_VR4130: mips_fix_vr4130 = 1; break; case OPTION_NO_FIX_VR4130: mips_fix_vr4130 = 0; break; case OPTION_RELAX_BRANCH: mips_relax_branch = 1; break; case OPTION_NO_RELAX_BRANCH: mips_relax_branch = 0; break; case OPTION_MSHARED: mips_in_shared = TRUE; break; case OPTION_MNO_SHARED: mips_in_shared = FALSE; break; case OPTION_MSYM32: mips_opts.sym32 = TRUE; break; case OPTION_MNO_SYM32: mips_opts.sym32 = FALSE; break; #ifdef OBJ_ELF /* When generating ELF code, we permit -KPIC and -call_shared to select SVR4_PIC, and -non_shared to select no PIC. This is intended to be compatible with Irix 5. */ case OPTION_CALL_SHARED: if (!IS_ELF) { as_bad (_("-call_shared is supported only for ELF format")); return 0; } mips_pic = SVR4_PIC; mips_abicalls = TRUE; break; case OPTION_NON_SHARED: if (!IS_ELF) { as_bad (_("-non_shared is supported only for ELF format")); return 0; } mips_pic = NO_PIC; mips_abicalls = FALSE; break; /* The -xgot option tells the assembler to use 32 bit offsets when accessing the got in SVR4_PIC mode. It is for Irix compatibility. */ case OPTION_XGOT: mips_big_got = 1; break; #endif /* OBJ_ELF */ case 'G': g_switch_value = atoi (arg); g_switch_seen = 1; break; #ifdef OBJ_ELF /* The -32, -n32 and -64 options are shortcuts for -mabi=32, -mabi=n32 and -mabi=64. */ case OPTION_32: if (!IS_ELF) { as_bad (_("-32 is supported for ELF format only")); return 0; } mips_abi = O32_ABI; break; case OPTION_N32: if (!IS_ELF) { as_bad (_("-n32 is supported for ELF format only")); return 0; } mips_abi = N32_ABI; break; case OPTION_64: if (!IS_ELF) { as_bad (_("-64 is supported for ELF format only")); return 0; } mips_abi = N64_ABI; if (!support_64bit_objects()) as_fatal (_("No compiled in support for 64 bit object file format")); break; #endif /* OBJ_ELF */ case OPTION_GP32: file_mips_gp32 = 1; break; case OPTION_GP64: file_mips_gp32 = 0; break; case OPTION_FP32: file_mips_fp32 = 1; break; case OPTION_FP64: file_mips_fp32 = 0; break; #ifdef OBJ_ELF case OPTION_MABI: if (!IS_ELF) { as_bad (_("-mabi is supported for ELF format only")); return 0; } if (strcmp (arg, "32") == 0) mips_abi = O32_ABI; else if (strcmp (arg, "o64") == 0) mips_abi = O64_ABI; else if (strcmp (arg, "n32") == 0) mips_abi = N32_ABI; else if (strcmp (arg, "64") == 0) { mips_abi = N64_ABI; if (! support_64bit_objects()) as_fatal (_("No compiled in support for 64 bit object file " "format")); } else if (strcmp (arg, "eabi") == 0) mips_abi = EABI_ABI; else { as_fatal (_("invalid abi -mabi=%s"), arg); return 0; } break; #endif /* OBJ_ELF */ case OPTION_M7000_HILO_FIX: mips_7000_hilo_fix = TRUE; break; case OPTION_MNO_7000_HILO_FIX: mips_7000_hilo_fix = FALSE; break; #ifdef OBJ_ELF case OPTION_MDEBUG: mips_flag_mdebug = TRUE; break; case OPTION_NO_MDEBUG: mips_flag_mdebug = FALSE; break; case OPTION_PDR: mips_flag_pdr = TRUE; break; case OPTION_NO_PDR: mips_flag_pdr = FALSE; break; case OPTION_MVXWORKS_PIC: mips_pic = VXWORKS_PIC; break; #endif /* OBJ_ELF */ default: return 0; } return 1; } /* Set up globals to generate code for the ISA or processor described by INFO. */ static void mips_set_architecture (const struct mips_cpu_info *info) { if (info != 0) { file_mips_arch = info->cpu; mips_opts.arch = info->cpu; mips_opts.isa = info->isa; } } /* Likewise for tuning. */ static void mips_set_tune (const struct mips_cpu_info *info) { if (info != 0) mips_tune = info->cpu; } void mips_after_parse_args (void) { const struct mips_cpu_info *arch_info = 0; const struct mips_cpu_info *tune_info = 0; /* GP relative stuff not working for PE */ if (strncmp (TARGET_OS, "pe", 2) == 0) { if (g_switch_seen && g_switch_value != 0) as_bad (_("-G not supported in this configuration.")); g_switch_value = 0; } if (mips_abi == NO_ABI) mips_abi = MIPS_DEFAULT_ABI; /* The following code determines the architecture and register size. Similar code was added to GCC 3.3 (see override_options() in config/mips/mips.c). The GAS and GCC code should be kept in sync as much as possible. */ if (mips_arch_string != 0) arch_info = mips_parse_cpu ("-march", mips_arch_string); if (file_mips_isa != ISA_UNKNOWN) { /* Handle -mipsN. At this point, file_mips_isa contains the ISA level specified by -mipsN, while arch_info->isa contains the -march selection (if any). */ if (arch_info != 0) { /* -march takes precedence over -mipsN, since it is more descriptive. There's no harm in specifying both as long as the ISA levels are the same. */ if (file_mips_isa != arch_info->isa) as_bad (_("-%s conflicts with the other architecture options, which imply -%s"), mips_cpu_info_from_isa (file_mips_isa)->name, mips_cpu_info_from_isa (arch_info->isa)->name); } else arch_info = mips_cpu_info_from_isa (file_mips_isa); } if (arch_info == 0) arch_info = mips_parse_cpu ("default CPU", MIPS_CPU_STRING_DEFAULT); if (ABI_NEEDS_64BIT_REGS (mips_abi) && !ISA_HAS_64BIT_REGS (arch_info->isa)) as_bad ("-march=%s is not compatible with the selected ABI", arch_info->name); mips_set_architecture (arch_info); /* Optimize for file_mips_arch, unless -mtune selects a different processor. */ if (mips_tune_string != 0) tune_info = mips_parse_cpu ("-mtune", mips_tune_string); if (tune_info == 0) mips_set_tune (arch_info); else mips_set_tune (tune_info); if (file_mips_gp32 >= 0) { /* The user specified the size of the integer registers. Make sure it agrees with the ABI and ISA. */ if (file_mips_gp32 == 0 && !ISA_HAS_64BIT_REGS (mips_opts.isa)) as_bad (_("-mgp64 used with a 32-bit processor")); else if (file_mips_gp32 == 1 && ABI_NEEDS_64BIT_REGS (mips_abi)) as_bad (_("-mgp32 used with a 64-bit ABI")); else if (file_mips_gp32 == 0 && ABI_NEEDS_32BIT_REGS (mips_abi)) as_bad (_("-mgp64 used with a 32-bit ABI")); } else { /* Infer the integer register size from the ABI and processor. Restrict ourselves to 32-bit registers if that's all the processor has, or if the ABI cannot handle 64-bit registers. */ file_mips_gp32 = (ABI_NEEDS_32BIT_REGS (mips_abi) || !ISA_HAS_64BIT_REGS (mips_opts.isa)); } switch (file_mips_fp32) { default: case -1: /* No user specified float register size. ??? GAS treats single-float processors as though they had 64-bit float registers (although it complains when double-precision instructions are used). As things stand, saying they have 32-bit registers would lead to spurious "register must be even" messages. So here we assume float registers are never smaller than the integer ones. */ if (file_mips_gp32 == 0) /* 64-bit integer registers implies 64-bit float registers. */ file_mips_fp32 = 0; else if ((mips_opts.ase_mips3d > 0 || mips_opts.ase_mdmx > 0) && ISA_HAS_64BIT_FPRS (mips_opts.isa)) /* -mips3d and -mdmx imply 64-bit float registers, if possible. */ file_mips_fp32 = 0; else /* 32-bit float registers. */ file_mips_fp32 = 1; break; /* The user specified the size of the float registers. Check if it agrees with the ABI and ISA. */ case 0: if (!ISA_HAS_64BIT_FPRS (mips_opts.isa)) as_bad (_("-mfp64 used with a 32-bit fpu")); else if (ABI_NEEDS_32BIT_REGS (mips_abi) && !ISA_HAS_MXHC1 (mips_opts.isa)) as_warn (_("-mfp64 used with a 32-bit ABI")); break; case 1: if (ABI_NEEDS_64BIT_REGS (mips_abi)) as_warn (_("-mfp32 used with a 64-bit ABI")); break; } /* End of GCC-shared inference code. */ /* This flag is set when we have a 64-bit capable CPU but use only 32-bit wide registers. Note that EABI does not use it. */ if (ISA_HAS_64BIT_REGS (mips_opts.isa) && ((mips_abi == NO_ABI && file_mips_gp32 == 1) || mips_abi == O32_ABI)) mips_32bitmode = 1; if (mips_opts.isa == ISA_MIPS1 && mips_trap) as_bad (_("trap exception not supported at ISA 1")); /* If the selected architecture includes support for ASEs, enable generation of code for them. */ if (mips_opts.mips16 == -1) mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_arch)) ? 1 : 0; if (mips_opts.ase_mips3d == -1) mips_opts.ase_mips3d = ((arch_info->flags & MIPS_CPU_ASE_MIPS3D) && file_mips_fp32 == 0) ? 1 : 0; if (mips_opts.ase_mips3d && file_mips_fp32 == 1) as_bad (_("-mfp32 used with -mips3d")); if (mips_opts.ase_mdmx == -1) mips_opts.ase_mdmx = ((arch_info->flags & MIPS_CPU_ASE_MDMX) && file_mips_fp32 == 0) ? 1 : 0; if (mips_opts.ase_mdmx && file_mips_fp32 == 1) as_bad (_("-mfp32 used with -mdmx")); if (mips_opts.ase_smartmips == -1) mips_opts.ase_smartmips = (arch_info->flags & MIPS_CPU_ASE_SMARTMIPS) ? 1 : 0; if (mips_opts.ase_smartmips && !ISA_SUPPORTS_SMARTMIPS) as_warn ("%s ISA does not support SmartMIPS", mips_cpu_info_from_isa (mips_opts.isa)->name); if (mips_opts.ase_dsp == -1) mips_opts.ase_dsp = (arch_info->flags & MIPS_CPU_ASE_DSP) ? 1 : 0; if (mips_opts.ase_dsp && !ISA_SUPPORTS_DSP_ASE) as_warn ("%s ISA does not support DSP ASE", mips_cpu_info_from_isa (mips_opts.isa)->name); if (mips_opts.ase_dspr2 == -1) { mips_opts.ase_dspr2 = (arch_info->flags & MIPS_CPU_ASE_DSPR2) ? 1 : 0; mips_opts.ase_dsp = (arch_info->flags & MIPS_CPU_ASE_DSP) ? 1 : 0; } if (mips_opts.ase_dspr2 && !ISA_SUPPORTS_DSPR2_ASE) as_warn ("%s ISA does not support DSP R2 ASE", mips_cpu_info_from_isa (mips_opts.isa)->name); if (mips_opts.ase_mt == -1) mips_opts.ase_mt = (arch_info->flags & MIPS_CPU_ASE_MT) ? 1 : 0; if (mips_opts.ase_mt && !ISA_SUPPORTS_MT_ASE) as_warn ("%s ISA does not support MT ASE", mips_cpu_info_from_isa (mips_opts.isa)->name); file_mips_isa = mips_opts.isa; file_ase_mips16 = mips_opts.mips16; file_ase_mips3d = mips_opts.ase_mips3d; file_ase_mdmx = mips_opts.ase_mdmx; file_ase_smartmips = mips_opts.ase_smartmips; file_ase_dsp = mips_opts.ase_dsp; file_ase_dspr2 = mips_opts.ase_dspr2; file_ase_mt = mips_opts.ase_mt; mips_opts.gp32 = file_mips_gp32; mips_opts.fp32 = file_mips_fp32; if (mips_flag_mdebug < 0) { #ifdef OBJ_MAYBE_ECOFF if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour) mips_flag_mdebug = 1; else #endif /* OBJ_MAYBE_ECOFF */ mips_flag_mdebug = 0; } } void mips_init_after_args (void) { /* initialize opcodes */ bfd_mips_num_opcodes = bfd_mips_num_builtin_opcodes; mips_opcodes = (struct mips_opcode *) mips_builtin_opcodes; } long md_pcrel_from (fixS *fixP) { valueT addr = fixP->fx_where + fixP->fx_frag->fr_address; switch (fixP->fx_r_type) { case BFD_RELOC_16_PCREL_S2: case BFD_RELOC_MIPS_JMP: /* Return the address of the delay slot. */ return addr + 4; default: /* We have no relocation type for PC relative MIPS16 instructions. */ if (fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != now_seg) as_bad_where (fixP->fx_file, fixP->fx_line, _("PC relative MIPS16 instruction references a different section")); return addr; } } /* This is called before the symbol table is processed. In order to work with gcc when using mips-tfile, we must keep all local labels. However, in other cases, we want to discard them. If we were called with -g, but we didn't see any debugging information, it may mean that gcc is smuggling debugging information through to mips-tfile, in which case we must generate all local labels. */ void mips_frob_file_before_adjust (void) { #ifndef NO_ECOFF_DEBUGGING if (ECOFF_DEBUGGING && mips_debug != 0 && ! ecoff_debugging_seen) flag_keep_locals = 1; #endif } /* Sort any unmatched HI16 and GOT16 relocs so that they immediately precede the corresponding LO16 reloc. This is called before md_apply_fix and tc_gen_reloc. Unmatched relocs can only be generated by use of explicit relocation operators. For our purposes, a %lo() expression matches a %got() or %hi() expression if: (a) it refers to the same symbol; and (b) the offset applied in the %lo() expression is no lower than the offset applied in the %got() or %hi(). (b) allows us to cope with code like: lui $4,%hi(foo) lh $4,%lo(foo+2)($4) ...which is legal on RELA targets, and has a well-defined behaviour if the user knows that adding 2 to "foo" will not induce a carry to the high 16 bits. When several %lo()s match a particular %got() or %hi(), we use the following rules to distinguish them: (1) %lo()s with smaller offsets are a better match than %lo()s with higher offsets. (2) %lo()s with no matching %got() or %hi() are better than those that already have a matching %got() or %hi(). (3) later %lo()s are better than earlier %lo()s. These rules are applied in order. (1) means, among other things, that %lo()s with identical offsets are chosen if they exist. (2) means that we won't associate several high-part relocations with the same low-part relocation unless there's no alternative. Having several high parts for the same low part is a GNU extension; this rule allows careful users to avoid it. (3) is purely cosmetic. mips_hi_fixup_list is is in reverse order, with the last high-part relocation being at the front of the list. It therefore makes sense to choose the last matching low-part relocation, all other things being equal. It's also easier to code that way. */ void mips_frob_file (void) { struct mips_hi_fixup *l; for (l = mips_hi_fixup_list; l != NULL; l = l->next) { segment_info_type *seginfo; bfd_boolean matched_lo_p; fixS **hi_pos, **lo_pos, **pos; assert (reloc_needs_lo_p (l->fixp->fx_r_type)); /* If a GOT16 relocation turns out to be against a global symbol, there isn't supposed to be a matching LO. */ if (l->fixp->fx_r_type == BFD_RELOC_MIPS_GOT16 && !pic_need_relax (l->fixp->fx_addsy, l->seg)) continue; /* Check quickly whether the next fixup happens to be a matching %lo. */ if (fixup_has_matching_lo_p (l->fixp)) continue; seginfo = seg_info (l->seg); /* Set HI_POS to the position of this relocation in the chain. Set LO_POS to the position of the chosen low-part relocation. MATCHED_LO_P is true on entry to the loop if *POS is a low-part relocation that matches an immediately-preceding high-part relocation. */ hi_pos = NULL; lo_pos = NULL; matched_lo_p = FALSE; for (pos = &seginfo->fix_root; *pos != NULL; pos = &(*pos)->fx_next) { if (*pos == l->fixp) hi_pos = pos; if (((*pos)->fx_r_type == BFD_RELOC_LO16 || (*pos)->fx_r_type == BFD_RELOC_MIPS16_LO16) && (*pos)->fx_addsy == l->fixp->fx_addsy && (*pos)->fx_offset >= l->fixp->fx_offset && (lo_pos == NULL || (*pos)->fx_offset < (*lo_pos)->fx_offset || (!matched_lo_p && (*pos)->fx_offset == (*lo_pos)->fx_offset))) lo_pos = pos; matched_lo_p = (reloc_needs_lo_p ((*pos)->fx_r_type) && fixup_has_matching_lo_p (*pos)); } /* If we found a match, remove the high-part relocation from its current position and insert it before the low-part relocation. Make the offsets match so that fixup_has_matching_lo_p() will return true. We don't warn about unmatched high-part relocations since some versions of gcc have been known to emit dead "lui ...%hi(...)" instructions. */ if (lo_pos != NULL) { l->fixp->fx_offset = (*lo_pos)->fx_offset; if (l->fixp->fx_next != *lo_pos) { *hi_pos = l->fixp->fx_next; l->fixp->fx_next = *lo_pos; *lo_pos = l->fixp; } } } } /* We may have combined relocations without symbols in the N32/N64 ABI. We have to prevent gas from dropping them. */ int mips_force_relocation (fixS *fixp) { if (generic_force_reloc (fixp)) return 1; if (HAVE_NEWABI && S_GET_SEGMENT (fixp->fx_addsy) == bfd_abs_section_ptr && (fixp->fx_r_type == BFD_RELOC_MIPS_SUB || fixp->fx_r_type == BFD_RELOC_HI16_S || fixp->fx_r_type == BFD_RELOC_LO16)) return 1; return 0; } /* Apply a fixup to the object file. */ void md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) { bfd_byte *buf; long insn; reloc_howto_type *howto; /* We ignore generic BFD relocations we don't know about. */ howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type); if (! howto) return; assert (fixP->fx_size == 4 || fixP->fx_r_type == BFD_RELOC_16 || fixP->fx_r_type == BFD_RELOC_64 || fixP->fx_r_type == BFD_RELOC_CTOR || fixP->fx_r_type == BFD_RELOC_MIPS_SUB || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY || fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64); buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where); assert (!fixP->fx_pcrel || fixP->fx_r_type == BFD_RELOC_16_PCREL_S2); /* Don't treat parts of a composite relocation as done. There are two reasons for this: (1) The second and third parts will be against 0 (RSS_UNDEF) but should nevertheless be emitted if the first part is. (2) In normal usage, composite relocations are never assembly-time constants. The easiest way of dealing with the pathological exceptions is to generate a relocation against STN_UNDEF and leave everything up to the linker. */ if (fixP->fx_addsy == NULL && !fixP->fx_pcrel && fixP->fx_tcbit == 0) fixP->fx_done = 1; switch (fixP->fx_r_type) { case BFD_RELOC_MIPS_TLS_GD: case BFD_RELOC_MIPS_TLS_LDM: case BFD_RELOC_MIPS_TLS_DTPREL32: case BFD_RELOC_MIPS_TLS_DTPREL64: case BFD_RELOC_MIPS_TLS_DTPREL_HI16: case BFD_RELOC_MIPS_TLS_DTPREL_LO16: case BFD_RELOC_MIPS_TLS_GOTTPREL: case BFD_RELOC_MIPS_TLS_TPREL_HI16: case BFD_RELOC_MIPS_TLS_TPREL_LO16: S_SET_THREAD_LOCAL (fixP->fx_addsy); /* fall through */ case BFD_RELOC_MIPS_JMP: case BFD_RELOC_MIPS_SHIFT5: case BFD_RELOC_MIPS_SHIFT6: case BFD_RELOC_MIPS_GOT_DISP: case BFD_RELOC_MIPS_GOT_PAGE: case BFD_RELOC_MIPS_GOT_OFST: case BFD_RELOC_MIPS_SUB: case BFD_RELOC_MIPS_INSERT_A: case BFD_RELOC_MIPS_INSERT_B: case BFD_RELOC_MIPS_DELETE: case BFD_RELOC_MIPS_HIGHEST: case BFD_RELOC_MIPS_HIGHER: case BFD_RELOC_MIPS_SCN_DISP: case BFD_RELOC_MIPS_REL16: case BFD_RELOC_MIPS_RELGOT: case BFD_RELOC_MIPS_JALR: case BFD_RELOC_HI16: case BFD_RELOC_HI16_S: case BFD_RELOC_GPREL16: case BFD_RELOC_MIPS_LITERAL: case BFD_RELOC_MIPS_CALL16: case BFD_RELOC_MIPS_GOT16: case BFD_RELOC_GPREL32: case BFD_RELOC_MIPS_GOT_HI16: case BFD_RELOC_MIPS_GOT_LO16: case BFD_RELOC_MIPS_CALL_HI16: case BFD_RELOC_MIPS_CALL_LO16: case BFD_RELOC_MIPS16_GPREL: case BFD_RELOC_MIPS16_HI16: case BFD_RELOC_MIPS16_HI16_S: case BFD_RELOC_MIPS16_JMP: /* Nothing needed to do. The value comes from the reloc entry. */ break; case BFD_RELOC_64: /* This is handled like BFD_RELOC_32, but we output a sign extended value if we are only 32 bits. */ if (fixP->fx_done) { if (8 <= sizeof (valueT)) md_number_to_chars ((char *) buf, *valP, 8); else { valueT hiv; if ((*valP & 0x80000000) != 0) hiv = 0xffffffff; else hiv = 0; md_number_to_chars ((char *)(buf + (target_big_endian ? 4 : 0)), *valP, 4); md_number_to_chars ((char *)(buf + (target_big_endian ? 0 : 4)), hiv, 4); } } break; case BFD_RELOC_RVA: case BFD_RELOC_32: case BFD_RELOC_16: /* If we are deleting this reloc entry, we must fill in the value now. This can happen if we have a .word which is not resolved when it appears but is later defined. */ if (fixP->fx_done) md_number_to_chars ((char *) buf, *valP, fixP->fx_size); break; case BFD_RELOC_LO16: case BFD_RELOC_MIPS16_LO16: /* FIXME: Now that embedded-PIC is gone, some of this code/comment may be safe to remove, but if so it's not obvious. */ /* When handling an embedded PIC switch statement, we can wind up deleting a LO16 reloc. See the 'o' case in mips_ip. */ if (fixP->fx_done) { if (*valP + 0x8000 > 0xffff) as_bad_where (fixP->fx_file, fixP->fx_line, _("relocation overflow")); if (target_big_endian) buf += 2; md_number_to_chars ((char *) buf, *valP, 2); } break; case BFD_RELOC_16_PCREL_S2: if ((*valP & 0x3) != 0) as_bad_where (fixP->fx_file, fixP->fx_line, _("Branch to misaligned address (%lx)"), (long) *valP); /* We need to save the bits in the instruction since fixup_segment() might be deleting the relocation entry (i.e., a branch within the current segment). */ if (! fixP->fx_done) break; /* Update old instruction data. */ if (target_big_endian) insn = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; else insn = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; if (*valP + 0x20000 <= 0x3ffff) { insn |= (*valP >> 2) & 0xffff; md_number_to_chars ((char *) buf, insn, 4); } else if (mips_pic == NO_PIC && fixP->fx_done && fixP->fx_frag->fr_address >= text_section->vma && (fixP->fx_frag->fr_address < text_section->vma + bfd_get_section_size (text_section)) && ((insn & 0xffff0000) == 0x10000000 /* beq $0,$0 */ || (insn & 0xffff0000) == 0x04010000 /* bgez $0 */ || (insn & 0xffff0000) == 0x04110000)) /* bgezal $0 */ { /* The branch offset is too large. If this is an unconditional branch, and we are not generating PIC code, we can convert it to an absolute jump instruction. */ if ((insn & 0xffff0000) == 0x04110000) /* bgezal $0 */ insn = 0x0c000000; /* jal */ else insn = 0x08000000; /* j */ fixP->fx_r_type = BFD_RELOC_MIPS_JMP; fixP->fx_done = 0; fixP->fx_addsy = section_symbol (text_section); *valP += md_pcrel_from (fixP); md_number_to_chars ((char *) buf, insn, 4); } else { /* If we got here, we have branch-relaxation disabled, and there's nothing we can do to fix this instruction without turning it into a longer sequence. */ as_bad_where (fixP->fx_file, fixP->fx_line, _("Branch out of range")); } break; case BFD_RELOC_VTABLE_INHERIT: fixP->fx_done = 0; if (fixP->fx_addsy && !S_IS_DEFINED (fixP->fx_addsy) && !S_IS_WEAK (fixP->fx_addsy)) S_SET_WEAK (fixP->fx_addsy); break; case BFD_RELOC_VTABLE_ENTRY: fixP->fx_done = 0; break; default: internalError (); } /* Remember value for tc_gen_reloc. */ fixP->fx_addnumber = *valP; } static symbolS * get_symbol (void) { int c; char *name; symbolS *p; name = input_line_pointer; c = get_symbol_end (); p = (symbolS *) symbol_find_or_make (name); *input_line_pointer = c; return p; } /* Align the current frag to a given power of two. The MIPS assembler also automatically adjusts any preceding label. */ static void mips_align (int to, int fill, symbolS *label) { mips_emit_delays (); frag_align (to, fill, 0); record_alignment (now_seg, to); if (label != NULL) { assert (S_GET_SEGMENT (label) == now_seg); symbol_set_frag (label, frag_now); S_SET_VALUE (label, (valueT) frag_now_fix ()); } } /* Align to a given power of two. .align 0 turns off the automatic alignment used by the data creating pseudo-ops. */ static void s_align (int x ATTRIBUTE_UNUSED) { int temp; long temp_fill; long max_alignment = 15; /* o Note that the assembler pulls down any immediately preceding label to the aligned address. o It's not documented but auto alignment is reinstated by a .align pseudo instruction. o Note also that after auto alignment is turned off the mips assembler issues an error on attempt to assemble an improperly aligned data item. We don't. */ temp = get_absolute_expression (); if (temp > max_alignment) as_bad (_("Alignment too large: %d. assumed."), temp = max_alignment); else if (temp < 0) { as_warn (_("Alignment negative: 0 assumed.")); temp = 0; } if (*input_line_pointer == ',') { ++input_line_pointer; temp_fill = get_absolute_expression (); } else temp_fill = 0; if (temp) { segment_info_type *si = seg_info (now_seg); struct insn_label_list *l = si->label_list; /* Auto alignment should be switched on by next section change. */ auto_align = 1; mips_align (temp, (int) temp_fill, l != NULL ? l->label : NULL); } else { auto_align = 0; } demand_empty_rest_of_line (); } static void s_change_sec (int sec) { segT seg; #ifdef OBJ_ELF /* The ELF backend needs to know that we are changing sections, so that .previous works correctly. We could do something like check for an obj_section_change_hook macro, but that might be confusing as it would not be appropriate to use it in the section changing functions in read.c, since obj-elf.c intercepts those. FIXME: This should be cleaner, somehow. */ if (IS_ELF) obj_elf_section_change_hook (); #endif mips_emit_delays (); switch (sec) { case 't': s_text (0); break; case 'd': s_data (0); break; case 'b': subseg_set (bss_section, (subsegT) get_absolute_expression ()); demand_empty_rest_of_line (); break; case 'r': seg = subseg_new (RDATA_SECTION_NAME, (subsegT) get_absolute_expression ()); if (IS_ELF) { bfd_set_section_flags (stdoutput, seg, (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_RELOC | SEC_DATA)); if (strcmp (TARGET_OS, "elf") != 0) record_alignment (seg, 4); } demand_empty_rest_of_line (); break; case 's': seg = subseg_new (".sdata", (subsegT) get_absolute_expression ()); if (IS_ELF) { bfd_set_section_flags (stdoutput, seg, SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA); if (strcmp (TARGET_OS, "elf") != 0) record_alignment (seg, 4); } demand_empty_rest_of_line (); break; } auto_align = 1; } void s_change_section (int ignore ATTRIBUTE_UNUSED) { #ifdef OBJ_ELF char *section_name; char c; char next_c = 0; int section_type; int section_flag; int section_entry_size; int section_alignment; if (!IS_ELF) return; section_name = input_line_pointer; c = get_symbol_end (); if (c) next_c = *(input_line_pointer + 1); /* Do we have .section Name<,"flags">? */ if (c != ',' || (c == ',' && next_c == '"')) { /* just after name is now '\0'. */ *input_line_pointer = c; input_line_pointer = section_name; obj_elf_section (ignore); return; } input_line_pointer++; /* Do we have .section Name<,type><,flag><,entry_size><,alignment> */ if (c == ',') section_type = get_absolute_expression (); else section_type = 0; if (*input_line_pointer++ == ',') section_flag = get_absolute_expression (); else section_flag = 0; if (*input_line_pointer++ == ',') section_entry_size = get_absolute_expression (); else section_entry_size = 0; if (*input_line_pointer++ == ',') section_alignment = get_absolute_expression (); else section_alignment = 0; section_name = xstrdup (section_name); /* When using the generic form of .section (as implemented by obj-elf.c), there's no way to set the section type to SHT_MIPS_DWARF. Users have traditionally had to fall back on the more common @progbits instead. There's nothing really harmful in this, since bfd will correct SHT_PROGBITS to SHT_MIPS_DWARF before writing out the file. But it means that, for backwards compatibility, the special_section entries for dwarf sections must use SHT_PROGBITS rather than SHT_MIPS_DWARF. Even so, we shouldn't force users of the MIPS .section syntax to incorrectly label the sections as SHT_PROGBITS. The best compromise seems to be to map SHT_MIPS_DWARF to SHT_PROGBITS before calling the generic type-checking code. */ if (section_type == SHT_MIPS_DWARF) section_type = SHT_PROGBITS; obj_elf_change_section (section_name, section_type, section_flag, section_entry_size, 0, 0, 0); if (now_seg->name != section_name) free (section_name); #endif /* OBJ_ELF */ } void mips_enable_auto_align (void) { auto_align = 1; } static void s_cons (int log_size) { segment_info_type *si = seg_info (now_seg); struct insn_label_list *l = si->label_list; symbolS *label; label = l != NULL ? l->label : NULL; mips_emit_delays (); if (log_size > 0 && auto_align) mips_align (log_size, 0, label); mips_clear_insn_labels (); cons (1 << log_size); } static void s_float_cons (int type) { segment_info_type *si = seg_info (now_seg); struct insn_label_list *l = si->label_list; symbolS *label; label = l != NULL ? l->label : NULL; mips_emit_delays (); if (auto_align) { if (type == 'd') mips_align (3, 0, label); else mips_align (2, 0, label); } mips_clear_insn_labels (); float_cons (type); } /* Handle .globl. We need to override it because on Irix 5 you are permitted to say .globl foo .text where foo is an undefined symbol, to mean that foo should be considered to be the address of a function. */ static void s_mips_globl (int x ATTRIBUTE_UNUSED) { char *name; int c; symbolS *symbolP; flagword flag; do { name = input_line_pointer; c = get_symbol_end (); symbolP = symbol_find_or_make (name); S_SET_EXTERNAL (symbolP); *input_line_pointer = c; SKIP_WHITESPACE (); /* On Irix 5, every global symbol that is not explicitly labelled as being a function is apparently labelled as being an object. */ flag = BSF_OBJECT; if (!is_end_of_line[(unsigned char) *input_line_pointer] && (*input_line_pointer != ',')) { char *secname; asection *sec; secname = input_line_pointer; c = get_symbol_end (); sec = bfd_get_section_by_name (stdoutput, secname); if (sec == NULL) as_bad (_("%s: no such section"), secname); *input_line_pointer = c; if (sec != NULL && (sec->flags & SEC_CODE) != 0) flag = BSF_FUNCTION; } symbol_get_bfdsym (symbolP)->flags |= flag; c = *input_line_pointer; if (c == ',') { input_line_pointer++; SKIP_WHITESPACE (); if (is_end_of_line[(unsigned char) *input_line_pointer]) c = '\n'; } } while (c == ','); demand_empty_rest_of_line (); } static void s_option (int x ATTRIBUTE_UNUSED) { char *opt; char c; opt = input_line_pointer; c = get_symbol_end (); if (*opt == 'O') { /* FIXME: What does this mean? */ } else if (strncmp (opt, "pic", 3) == 0) { int i; i = atoi (opt + 3); if (i == 0) mips_pic = NO_PIC; else if (i == 2) { mips_pic = SVR4_PIC; mips_abicalls = TRUE; } else as_bad (_(".option pic%d not supported"), i); if (mips_pic == SVR4_PIC) { if (g_switch_seen && g_switch_value != 0) as_warn (_("-G may not be used with SVR4 PIC code")); g_switch_value = 0; bfd_set_gp_size (stdoutput, 0); } } else as_warn (_("Unrecognized option \"%s\""), opt); *input_line_pointer = c; demand_empty_rest_of_line (); } /* This structure is used to hold a stack of .set values. */ struct mips_option_stack { struct mips_option_stack *next; struct mips_set_options options; }; static struct mips_option_stack *mips_opts_stack; /* Handle the .set pseudo-op. */ static void s_mipsset (int x ATTRIBUTE_UNUSED) { char *name = input_line_pointer, ch; while (!is_end_of_line[(unsigned char) *input_line_pointer]) ++input_line_pointer; ch = *input_line_pointer; *input_line_pointer = '\0'; if (strcmp (name, "reorder") == 0) { if (mips_opts.noreorder) end_noreorder (); } else if (strcmp (name, "noreorder") == 0) { if (!mips_opts.noreorder) start_noreorder (); } else if (strcmp (name, "at") == 0) { mips_opts.noat = 0; } else if (strcmp (name, "noat") == 0) { mips_opts.noat = 1; } else if (strcmp (name, "macro") == 0) { mips_opts.warn_about_macros = 0; } else if (strcmp (name, "nomacro") == 0) { if (mips_opts.noreorder == 0) as_bad (_("`noreorder' must be set before `nomacro'")); mips_opts.warn_about_macros = 1; } else if (strcmp (name, "move") == 0 || strcmp (name, "novolatile") == 0) { mips_opts.nomove = 0; } else if (strcmp (name, "nomove") == 0 || strcmp (name, "volatile") == 0) { mips_opts.nomove = 1; } else if (strcmp (name, "bopt") == 0) { mips_opts.nobopt = 0; } else if (strcmp (name, "nobopt") == 0) { mips_opts.nobopt = 1; } else if (strcmp (name, "gp=default") == 0) mips_opts.gp32 = file_mips_gp32; else if (strcmp (name, "gp=32") == 0) mips_opts.gp32 = 1; else if (strcmp (name, "gp=64") == 0) { if (!ISA_HAS_64BIT_REGS (mips_opts.isa)) as_warn ("%s isa does not support 64-bit registers", mips_cpu_info_from_isa (mips_opts.isa)->name); mips_opts.gp32 = 0; } else if (strcmp (name, "fp=default") == 0) mips_opts.fp32 = file_mips_fp32; else if (strcmp (name, "fp=32") == 0) mips_opts.fp32 = 1; else if (strcmp (name, "fp=64") == 0) { if (!ISA_HAS_64BIT_FPRS (mips_opts.isa)) as_warn ("%s isa does not support 64-bit floating point registers", mips_cpu_info_from_isa (mips_opts.isa)->name); mips_opts.fp32 = 0; } else if (strcmp (name, "mips16") == 0 || strcmp (name, "MIPS-16") == 0) mips_opts.mips16 = 1; else if (strcmp (name, "nomips16") == 0 || strcmp (name, "noMIPS-16") == 0) mips_opts.mips16 = 0; else if (strcmp (name, "smartmips") == 0) { if (!ISA_SUPPORTS_SMARTMIPS) as_warn ("%s ISA does not support SmartMIPS ASE", mips_cpu_info_from_isa (mips_opts.isa)->name); mips_opts.ase_smartmips = 1; } else if (strcmp (name, "nosmartmips") == 0) mips_opts.ase_smartmips = 0; else if (strcmp (name, "mips3d") == 0) mips_opts.ase_mips3d = 1; else if (strcmp (name, "nomips3d") == 0) mips_opts.ase_mips3d = 0; else if (strcmp (name, "mdmx") == 0) mips_opts.ase_mdmx = 1; else if (strcmp (name, "nomdmx") == 0) mips_opts.ase_mdmx = 0; else if (strcmp (name, "dsp") == 0) { if (!ISA_SUPPORTS_DSP_ASE) as_warn ("%s ISA does not support DSP ASE", mips_cpu_info_from_isa (mips_opts.isa)->name); mips_opts.ase_dsp = 1; mips_opts.ase_dspr2 = 0; } else if (strcmp (name, "nodsp") == 0) { mips_opts.ase_dsp = 0; mips_opts.ase_dspr2 = 0; } else if (strcmp (name, "dspr2") == 0) { if (!ISA_SUPPORTS_DSPR2_ASE) as_warn ("%s ISA does not support DSP R2 ASE", mips_cpu_info_from_isa (mips_opts.isa)->name); mips_opts.ase_dspr2 = 1; mips_opts.ase_dsp = 1; } else if (strcmp (name, "nodspr2") == 0) { mips_opts.ase_dspr2 = 0; mips_opts.ase_dsp = 0; } else if (strcmp (name, "mt") == 0) { if (!ISA_SUPPORTS_MT_ASE) as_warn ("%s ISA does not support MT ASE", mips_cpu_info_from_isa (mips_opts.isa)->name); mips_opts.ase_mt = 1; } else if (strcmp (name, "nomt") == 0) mips_opts.ase_mt = 0; else if (strncmp (name, "mips", 4) == 0 || strncmp (name, "arch=", 5) == 0) { int reset = 0; /* Permit the user to change the ISA and architecture on the fly. Needless to say, misuse can cause serious problems. */ if (strcmp (name, "mips0") == 0 || strcmp (name, "arch=default") == 0) { reset = 1; mips_opts.isa = file_mips_isa; mips_opts.arch = file_mips_arch; } else if (strncmp (name, "arch=", 5) == 0) { const struct mips_cpu_info *p; p = mips_parse_cpu("internal use", name + 5); if (!p) as_bad (_("unknown architecture %s"), name + 5); else { mips_opts.arch = p->cpu; mips_opts.isa = p->isa; } } else if (strncmp (name, "mips", 4) == 0) { const struct mips_cpu_info *p; p = mips_parse_cpu("internal use", name); if (!p) as_bad (_("unknown ISA level %s"), name + 4); else { mips_opts.arch = p->cpu; mips_opts.isa = p->isa; } } else as_bad (_("unknown ISA or architecture %s"), name); switch (mips_opts.isa) { case 0: break; case ISA_MIPS1: case ISA_MIPS2: case ISA_MIPS32: case ISA_MIPS32R2: mips_opts.gp32 = 1; mips_opts.fp32 = 1; break; case ISA_MIPS3: case ISA_MIPS4: case ISA_MIPS5: case ISA_MIPS64: case ISA_MIPS64R2: mips_opts.gp32 = 0; mips_opts.fp32 = 0; break; default: as_bad (_("unknown ISA level %s"), name + 4); break; } if (reset) { mips_opts.gp32 = file_mips_gp32; mips_opts.fp32 = file_mips_fp32; } } else if (strcmp (name, "autoextend") == 0) mips_opts.noautoextend = 0; else if (strcmp (name, "noautoextend") == 0) mips_opts.noautoextend = 1; else if (strcmp (name, "push") == 0) { struct mips_option_stack *s; s = (struct mips_option_stack *) xmalloc (sizeof *s); s->next = mips_opts_stack; s->options = mips_opts; mips_opts_stack = s; } else if (strcmp (name, "pop") == 0) { struct mips_option_stack *s; s = mips_opts_stack; if (s == NULL) as_bad (_(".set pop with no .set push")); else { /* If we're changing the reorder mode we need to handle delay slots correctly. */ if (s->options.noreorder && ! mips_opts.noreorder) start_noreorder (); else if (! s->options.noreorder && mips_opts.noreorder) end_noreorder (); mips_opts = s->options; mips_opts_stack = s->next; free (s); } } else if (strcmp (name, "sym32") == 0) mips_opts.sym32 = TRUE; else if (strcmp (name, "nosym32") == 0) mips_opts.sym32 = FALSE; else if (strchr (name, ',')) { /* Generic ".set" directive; use the generic handler. */ *input_line_pointer = ch; input_line_pointer = name; s_set (0); return; } else { as_warn (_("Tried to set unrecognized symbol: %s\n"), name); } *input_line_pointer = ch; demand_empty_rest_of_line (); } /* Handle the .abicalls pseudo-op. I believe this is equivalent to .option pic2. It means to generate SVR4 PIC calls. */ static void s_abicalls (int ignore ATTRIBUTE_UNUSED) { mips_pic = SVR4_PIC; mips_abicalls = TRUE; if (g_switch_seen && g_switch_value != 0) as_warn (_("-G may not be used with SVR4 PIC code")); g_switch_value = 0; bfd_set_gp_size (stdoutput, 0); demand_empty_rest_of_line (); } /* Handle the .cpload pseudo-op. This is used when generating SVR4 PIC code. It sets the $gp register for the function based on the function address, which is in the register named in the argument. This uses a relocation against _gp_disp, which is handled specially by the linker. The result is: lui $gp,%hi(_gp_disp) addiu $gp,$gp,%lo(_gp_disp) addu $gp,$gp,.cpload argument The .cpload argument is normally $25 == $t9. The -mno-shared option changes this to: lui $gp,%hi(__gnu_local_gp) addiu $gp,$gp,%lo(__gnu_local_gp) and the argument is ignored. This saves an instruction, but the resulting code is not position independent; it uses an absolute address for __gnu_local_gp. Thus code assembled with -mno-shared can go into an ordinary executable, but not into a shared library. */ static void s_cpload (int ignore ATTRIBUTE_UNUSED) { expressionS ex; int reg; int in_shared; /* If we are not generating SVR4 PIC code, or if this is NewABI code, .cpload is ignored. */ if (mips_pic != SVR4_PIC || HAVE_NEWABI) { s_ignore (0); return; } /* .cpload should be in a .set noreorder section. */ if (mips_opts.noreorder == 0) as_warn (_(".cpload not in noreorder section")); reg = tc_get_register (0); /* If we need to produce a 64-bit address, we are better off using the default instruction sequence. */ in_shared = mips_in_shared || HAVE_64BIT_SYMBOLS; ex.X_op = O_symbol; ex.X_add_symbol = symbol_find_or_make (in_shared ? "_gp_disp" : "__gnu_local_gp"); ex.X_op_symbol = NULL; ex.X_add_number = 0; /* In ELF, this symbol is implicitly an STT_OBJECT symbol. */ symbol_get_bfdsym (ex.X_add_symbol)->flags |= BSF_OBJECT; macro_start (); macro_build_lui (&ex, mips_gp_register); macro_build (&ex, "addiu", "t,r,j", mips_gp_register, mips_gp_register, BFD_RELOC_LO16); if (in_shared) macro_build (NULL, "addu", "d,v,t", mips_gp_register, mips_gp_register, reg); macro_end (); demand_empty_rest_of_line (); } /* Handle the .cpsetup pseudo-op defined for NewABI PIC code. The syntax is: .cpsetup $reg1, offset|$reg2, label If offset is given, this results in: sd $gp, offset($sp) lui $gp, %hi(%neg(%gp_rel(label))) addiu $gp, $gp, %lo(%neg(%gp_rel(label))) daddu $gp, $gp, $reg1 If $reg2 is given, this results in: daddu $reg2, $gp, $0 lui $gp, %hi(%neg(%gp_rel(label))) addiu $gp, $gp, %lo(%neg(%gp_rel(label))) daddu $gp, $gp, $reg1 $reg1 is normally $25 == $t9. The -mno-shared option replaces the last three instructions with lui $gp,%hi(_gp) addiu $gp,$gp,%lo(_gp) */ static void s_cpsetup (int ignore ATTRIBUTE_UNUSED) { expressionS ex_off; expressionS ex_sym; int reg1; /* If we are not generating SVR4 PIC code, .cpsetup is ignored. We also need NewABI support. */ if (mips_pic != SVR4_PIC || ! HAVE_NEWABI) { s_ignore (0); return; } reg1 = tc_get_register (0); SKIP_WHITESPACE (); if (*input_line_pointer != ',') { as_bad (_("missing argument separator ',' for .cpsetup")); return; } else ++input_line_pointer; SKIP_WHITESPACE (); if (*input_line_pointer == '$') { mips_cpreturn_register = tc_get_register (0); mips_cpreturn_offset = -1; } else { mips_cpreturn_offset = get_absolute_expression (); mips_cpreturn_register = -1; } SKIP_WHITESPACE (); if (*input_line_pointer != ',') { as_bad (_("missing argument separator ',' for .cpsetup")); return; } else ++input_line_pointer; SKIP_WHITESPACE (); expression (&ex_sym); macro_start (); if (mips_cpreturn_register == -1) { ex_off.X_op = O_constant; ex_off.X_add_symbol = NULL; ex_off.X_op_symbol = NULL; ex_off.X_add_number = mips_cpreturn_offset; macro_build (&ex_off, "sd", "t,o(b)", mips_gp_register, BFD_RELOC_LO16, SP); } else macro_build (NULL, "daddu", "d,v,t", mips_cpreturn_register, mips_gp_register, 0); if (mips_in_shared || HAVE_64BIT_SYMBOLS) { macro_build (&ex_sym, "lui", "t,u", mips_gp_register, -1, BFD_RELOC_GPREL16, BFD_RELOC_MIPS_SUB, BFD_RELOC_HI16_S); macro_build (&ex_sym, "addiu", "t,r,j", mips_gp_register, mips_gp_register, -1, BFD_RELOC_GPREL16, BFD_RELOC_MIPS_SUB, BFD_RELOC_LO16); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", mips_gp_register, mips_gp_register, reg1); } else { expressionS ex; ex.X_op = O_symbol; ex.X_add_symbol = symbol_find_or_make ("__gnu_local_gp"); ex.X_op_symbol = NULL; ex.X_add_number = 0; /* In ELF, this symbol is implicitly an STT_OBJECT symbol. */ symbol_get_bfdsym (ex.X_add_symbol)->flags |= BSF_OBJECT; macro_build_lui (&ex, mips_gp_register); macro_build (&ex, "addiu", "t,r,j", mips_gp_register, mips_gp_register, BFD_RELOC_LO16); } macro_end (); demand_empty_rest_of_line (); } static void s_cplocal (int ignore ATTRIBUTE_UNUSED) { /* If we are not generating SVR4 PIC code, or if this is not NewABI code, .cplocal is ignored. */ if (mips_pic != SVR4_PIC || ! HAVE_NEWABI) { s_ignore (0); return; } mips_gp_register = tc_get_register (0); demand_empty_rest_of_line (); } /* Handle the .cprestore pseudo-op. This stores $gp into a given offset from $sp. The offset is remembered, and after making a PIC call $gp is restored from that location. */ static void s_cprestore (int ignore ATTRIBUTE_UNUSED) { expressionS ex; /* If we are not generating SVR4 PIC code, or if this is NewABI code, .cprestore is ignored. */ if (mips_pic != SVR4_PIC || HAVE_NEWABI) { s_ignore (0); return; } mips_cprestore_offset = get_absolute_expression (); mips_cprestore_valid = 1; ex.X_op = O_constant; ex.X_add_symbol = NULL; ex.X_op_symbol = NULL; ex.X_add_number = mips_cprestore_offset; macro_start (); macro_build_ldst_constoffset (&ex, ADDRESS_STORE_INSN, mips_gp_register, SP, HAVE_64BIT_ADDRESSES); macro_end (); demand_empty_rest_of_line (); } /* Handle the .cpreturn pseudo-op defined for NewABI PIC code. If an offset was given in the preceding .cpsetup, it results in: ld $gp, offset($sp) If a register $reg2 was given there, it results in: daddu $gp, $reg2, $0 */ static void s_cpreturn (int ignore ATTRIBUTE_UNUSED) { expressionS ex; /* If we are not generating SVR4 PIC code, .cpreturn is ignored. We also need NewABI support. */ if (mips_pic != SVR4_PIC || ! HAVE_NEWABI) { s_ignore (0); return; } macro_start (); if (mips_cpreturn_register == -1) { ex.X_op = O_constant; ex.X_add_symbol = NULL; ex.X_op_symbol = NULL; ex.X_add_number = mips_cpreturn_offset; macro_build (&ex, "ld", "t,o(b)", mips_gp_register, BFD_RELOC_LO16, SP); } else macro_build (NULL, "daddu", "d,v,t", mips_gp_register, mips_cpreturn_register, 0); macro_end (); demand_empty_rest_of_line (); } /* Handle the .dtprelword and .dtpreldword pseudo-ops. They generate a 32-bit or 64-bit DTP-relative relocation (BYTES says which) for use in DWARF debug information. */ static void s_dtprel_internal (size_t bytes) { expressionS ex; char *p; expression (&ex); if (ex.X_op != O_symbol) { as_bad (_("Unsupported use of %s"), (bytes == 8 ? ".dtpreldword" : ".dtprelword")); ignore_rest_of_line (); } p = frag_more (bytes); md_number_to_chars (p, 0, bytes); fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, FALSE, (bytes == 8 ? BFD_RELOC_MIPS_TLS_DTPREL64 : BFD_RELOC_MIPS_TLS_DTPREL32)); demand_empty_rest_of_line (); } /* Handle .dtprelword. */ static void s_dtprelword (int ignore ATTRIBUTE_UNUSED) { s_dtprel_internal (4); } /* Handle .dtpreldword. */ static void s_dtpreldword (int ignore ATTRIBUTE_UNUSED) { s_dtprel_internal (8); } /* Handle the .gpvalue pseudo-op. This is used when generating NewABI PIC code. It sets the offset to use in gp_rel relocations. */ static void s_gpvalue (int ignore ATTRIBUTE_UNUSED) { /* If we are not generating SVR4 PIC code, .gpvalue is ignored. We also need NewABI support. */ if (mips_pic != SVR4_PIC || ! HAVE_NEWABI) { s_ignore (0); return; } mips_gprel_offset = get_absolute_expression (); demand_empty_rest_of_line (); } /* Handle the .gpword pseudo-op. This is used when generating PIC code. It generates a 32 bit GP relative reloc. */ static void s_gpword (int ignore ATTRIBUTE_UNUSED) { segment_info_type *si; struct insn_label_list *l; symbolS *label; expressionS ex; char *p; /* When not generating PIC code, this is treated as .word. */ if (mips_pic != SVR4_PIC) { s_cons (2); return; } si = seg_info (now_seg); l = si->label_list; label = l != NULL ? l->label : NULL; mips_emit_delays (); if (auto_align) mips_align (2, 0, label); mips_clear_insn_labels (); expression (&ex); if (ex.X_op != O_symbol || ex.X_add_number != 0) { as_bad (_("Unsupported use of .gpword")); ignore_rest_of_line (); } p = frag_more (4); md_number_to_chars (p, 0, 4); fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE, BFD_RELOC_GPREL32); demand_empty_rest_of_line (); } static void s_gpdword (int ignore ATTRIBUTE_UNUSED) { segment_info_type *si; struct insn_label_list *l; symbolS *label; expressionS ex; char *p; /* When not generating PIC code, this is treated as .dword. */ if (mips_pic != SVR4_PIC) { s_cons (3); return; } si = seg_info (now_seg); l = si->label_list; label = l != NULL ? l->label : NULL; mips_emit_delays (); if (auto_align) mips_align (3, 0, label); mips_clear_insn_labels (); expression (&ex); if (ex.X_op != O_symbol || ex.X_add_number != 0) { as_bad (_("Unsupported use of .gpdword")); ignore_rest_of_line (); } p = frag_more (8); md_number_to_chars (p, 0, 8); fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE, BFD_RELOC_GPREL32)->fx_tcbit = 1; /* GPREL32 composed with 64 gives a 64-bit GP offset. */ fix_new (frag_now, p - frag_now->fr_literal, 8, NULL, 0, FALSE, BFD_RELOC_64)->fx_tcbit = 1; demand_empty_rest_of_line (); } /* Handle the .cpadd pseudo-op. This is used when dealing with switch tables in SVR4 PIC code. */ static void s_cpadd (int ignore ATTRIBUTE_UNUSED) { int reg; /* This is ignored when not generating SVR4 PIC code. */ if (mips_pic != SVR4_PIC) { s_ignore (0); return; } /* Add $gp to the register named as an argument. */ macro_start (); reg = tc_get_register (0); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", reg, reg, mips_gp_register); macro_end (); demand_empty_rest_of_line (); } /* Handle the .insn pseudo-op. This marks instruction labels in mips16 mode. This permits the linker to handle them specially, such as generating jalx instructions when needed. We also make them odd for the duration of the assembly, in order to generate the right sort of code. We will make them even in the adjust_symtab routine, while leaving them marked. This is convenient for the debugger and the disassembler. The linker knows to make them odd again. */ static void s_insn (int ignore ATTRIBUTE_UNUSED) { mips16_mark_labels (); demand_empty_rest_of_line (); } /* Handle a .stabn directive. We need these in order to mark a label as being a mips16 text label correctly. Sometimes the compiler will emit a label, followed by a .stabn, and then switch sections. If the label and .stabn are in mips16 mode, then the label is really a mips16 text label. */ static void s_mips_stab (int type) { if (type == 'n') mips16_mark_labels (); s_stab (type); } /* Handle the .weakext pseudo-op as defined in Kane and Heinrich. */ static void s_mips_weakext (int ignore ATTRIBUTE_UNUSED) { char *name; int c; symbolS *symbolP; expressionS exp; name = input_line_pointer; c = get_symbol_end (); symbolP = symbol_find_or_make (name); S_SET_WEAK (symbolP); *input_line_pointer = c; SKIP_WHITESPACE (); if (! is_end_of_line[(unsigned char) *input_line_pointer]) { if (S_IS_DEFINED (symbolP)) { as_bad ("ignoring attempt to redefine symbol %s", S_GET_NAME (symbolP)); ignore_rest_of_line (); return; } if (*input_line_pointer == ',') { ++input_line_pointer; SKIP_WHITESPACE (); } expression (&exp); if (exp.X_op != O_symbol) { as_bad ("bad .weakext directive"); ignore_rest_of_line (); return; } symbol_set_value_expression (symbolP, &exp); } demand_empty_rest_of_line (); } /* Parse a register string into a number. Called from the ECOFF code to parse .frame. The argument is non-zero if this is the frame register, so that we can record it in mips_frame_reg. */ int tc_get_register (int frame) { unsigned int reg; SKIP_WHITESPACE (); if (! reg_lookup (&input_line_pointer, RWARN | RTYPE_NUM | RTYPE_GP, ®)) reg = 0; if (frame) { mips_frame_reg = reg != 0 ? reg : SP; mips_frame_reg_valid = 1; mips_cprestore_valid = 0; } return reg; } valueT md_section_align (asection *seg, valueT addr) { int align = bfd_get_section_alignment (stdoutput, seg); if (IS_ELF) { /* We don't need to align ELF sections to the full alignment. However, Irix 5 may prefer that we align them at least to a 16 byte boundary. We don't bother to align the sections if we are targeted for an embedded system. */ if (strcmp (TARGET_OS, "elf") == 0) return addr; if (align > 4) align = 4; } return ((addr + (1 << align) - 1) & (-1 << align)); } /* Utility routine, called from above as well. If called while the input file is still being read, it's only an approximation. (For example, a symbol may later become defined which appeared to be undefined earlier.) */ static int nopic_need_relax (symbolS *sym, int before_relaxing) { if (sym == 0) return 0; if (g_switch_value > 0) { const char *symname; int change; /* Find out whether this symbol can be referenced off the $gp register. It can be if it is smaller than the -G size or if it is in the .sdata or .sbss section. Certain symbols can not be referenced off the $gp, although it appears as though they can. */ symname = S_GET_NAME (sym); if (symname != (const char *) NULL && (strcmp (symname, "eprol") == 0 || strcmp (symname, "etext") == 0 || strcmp (symname, "_gp") == 0 || strcmp (symname, "edata") == 0 || strcmp (symname, "_fbss") == 0 || strcmp (symname, "_fdata") == 0 || strcmp (symname, "_ftext") == 0 || strcmp (symname, "end") == 0 || strcmp (symname, "_gp_disp") == 0)) change = 1; else if ((! S_IS_DEFINED (sym) || S_IS_COMMON (sym)) && (0 #ifndef NO_ECOFF_DEBUGGING || (symbol_get_obj (sym)->ecoff_extern_size != 0 && (symbol_get_obj (sym)->ecoff_extern_size <= g_switch_value)) #endif /* We must defer this decision until after the whole file has been read, since there might be a .extern after the first use of this symbol. */ || (before_relaxing #ifndef NO_ECOFF_DEBUGGING && symbol_get_obj (sym)->ecoff_extern_size == 0 #endif && S_GET_VALUE (sym) == 0) || (S_GET_VALUE (sym) != 0 && S_GET_VALUE (sym) <= g_switch_value))) change = 0; else { const char *segname; segname = segment_name (S_GET_SEGMENT (sym)); assert (strcmp (segname, ".lit8") != 0 && strcmp (segname, ".lit4") != 0); change = (strcmp (segname, ".sdata") != 0 && strcmp (segname, ".sbss") != 0 && strncmp (segname, ".sdata.", 7) != 0 && strncmp (segname, ".sbss.", 6) != 0 && strncmp (segname, ".gnu.linkonce.sb.", 17) != 0 && strncmp (segname, ".gnu.linkonce.s.", 16) != 0); } return change; } else /* We are not optimizing for the $gp register. */ return 1; } /* Return true if the given symbol should be considered local for SVR4 PIC. */ static bfd_boolean pic_need_relax (symbolS *sym, asection *segtype) { asection *symsec; /* Handle the case of a symbol equated to another symbol. */ while (symbol_equated_reloc_p (sym)) { symbolS *n; /* It's possible to get a loop here in a badly written program. */ n = symbol_get_value_expression (sym)->X_add_symbol; if (n == sym) break; sym = n; } if (symbol_section_p (sym)) return TRUE; symsec = S_GET_SEGMENT (sym); /* This must duplicate the test in adjust_reloc_syms. */ return (symsec != &bfd_und_section && symsec != &bfd_abs_section && !bfd_is_com_section (symsec) && !s_is_linkonce (sym, segtype) #ifdef OBJ_ELF /* A global or weak symbol is treated as external. */ && (!IS_ELF || (! S_IS_WEAK (sym) && ! S_IS_EXTERNAL (sym))) #endif ); } /* Given a mips16 variant frag FRAGP, return non-zero if it needs an extended opcode. SEC is the section the frag is in. */ static int mips16_extended_frag (fragS *fragp, asection *sec, long stretch) { int type; const struct mips16_immed_operand *op; offsetT val; int mintiny, maxtiny; segT symsec; fragS *sym_frag; if (RELAX_MIPS16_USER_SMALL (fragp->fr_subtype)) return 0; if (RELAX_MIPS16_USER_EXT (fragp->fr_subtype)) return 1; type = RELAX_MIPS16_TYPE (fragp->fr_subtype); op = mips16_immed_operands; while (op->type != type) { ++op; assert (op < mips16_immed_operands + MIPS16_NUM_IMMED); } if (op->unsp) { if (type == '<' || type == '>' || type == '[' || type == ']') { mintiny = 1; maxtiny = 1 << op->nbits; } else { mintiny = 0; maxtiny = (1 << op->nbits) - 1; } } else { mintiny = - (1 << (op->nbits - 1)); maxtiny = (1 << (op->nbits - 1)) - 1; } sym_frag = symbol_get_frag (fragp->fr_symbol); val = S_GET_VALUE (fragp->fr_symbol); symsec = S_GET_SEGMENT (fragp->fr_symbol); if (op->pcrel) { addressT addr; /* We won't have the section when we are called from mips_relax_frag. However, we will always have been called from md_estimate_size_before_relax first. If this is a branch to a different section, we mark it as such. If SEC is NULL, and the frag is not marked, then it must be a branch to the same section. */ if (sec == NULL) { if (RELAX_MIPS16_LONG_BRANCH (fragp->fr_subtype)) return 1; } else { /* Must have been called from md_estimate_size_before_relax. */ if (symsec != sec) { fragp->fr_subtype = RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype); /* FIXME: We should support this, and let the linker catch branches and loads that are out of range. */ as_bad_where (fragp->fr_file, fragp->fr_line, _("unsupported PC relative reference to different section")); return 1; } if (fragp != sym_frag && sym_frag->fr_address == 0) /* Assume non-extended on the first relaxation pass. The address we have calculated will be bogus if this is a forward branch to another frag, as the forward frag will have fr_address == 0. */ return 0; } /* In this case, we know for sure that the symbol fragment is in the same section. If the relax_marker of the symbol fragment differs from the relax_marker of this fragment, we have not yet adjusted the symbol fragment fr_address. We want to add in STRETCH in order to get a better estimate of the address. This particularly matters because of the shift bits. */ if (stretch != 0 && sym_frag->relax_marker != fragp->relax_marker) { fragS *f; /* Adjust stretch for any alignment frag. Note that if have been expanding the earlier code, the symbol may be defined in what appears to be an earlier frag. FIXME: This doesn't handle the fr_subtype field, which specifies a maximum number of bytes to skip when doing an alignment. */ for (f = fragp; f != NULL && f != sym_frag; f = f->fr_next) { if (f->fr_type == rs_align || f->fr_type == rs_align_code) { if (stretch < 0) stretch = - ((- stretch) & ~ ((1 << (int) f->fr_offset) - 1)); else stretch &= ~ ((1 << (int) f->fr_offset) - 1); if (stretch == 0) break; } } if (f != NULL) val += stretch; } addr = fragp->fr_address + fragp->fr_fix; /* The base address rules are complicated. The base address of a branch is the following instruction. The base address of a PC relative load or add is the instruction itself, but if it is in a delay slot (in which case it can not be extended) use the address of the instruction whose delay slot it is in. */ if (type == 'p' || type == 'q') { addr += 2; /* If we are currently assuming that this frag should be extended, then, the current address is two bytes higher. */ if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype)) addr += 2; /* Ignore the low bit in the target, since it will be set for a text label. */ if ((val & 1) != 0) --val; } else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype)) addr -= 4; else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype)) addr -= 2; val -= addr & ~ ((1 << op->shift) - 1); /* Branch offsets have an implicit 0 in the lowest bit. */ if (type == 'p' || type == 'q') val /= 2; /* If any of the shifted bits are set, we must use an extended opcode. If the address depends on the size of this instruction, this can lead to a loop, so we arrange to always use an extended opcode. We only check this when we are in the main relaxation loop, when SEC is NULL. */ if ((val & ((1 << op->shift) - 1)) != 0 && sec == NULL) { fragp->fr_subtype = RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype); return 1; } /* If we are about to mark a frag as extended because the value is precisely maxtiny + 1, then there is a chance of an infinite loop as in the following code: la $4,foo .skip 1020 .align 2 foo: In this case when the la is extended, foo is 0x3fc bytes away, so the la can be shrunk, but then foo is 0x400 away, so the la must be extended. To avoid this loop, we mark the frag as extended if it was small, and is about to become extended with a value of maxtiny + 1. */ if (val == ((maxtiny + 1) << op->shift) && ! RELAX_MIPS16_EXTENDED (fragp->fr_subtype) && sec == NULL) { fragp->fr_subtype = RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype); return 1; } } else if (symsec != absolute_section && sec != NULL) as_bad_where (fragp->fr_file, fragp->fr_line, _("unsupported relocation")); if ((val & ((1 << op->shift) - 1)) != 0 || val < (mintiny << op->shift) || val > (maxtiny << op->shift)) return 1; else return 0; } /* Compute the length of a branch sequence, and adjust the RELAX_BRANCH_TOOFAR bit accordingly. If FRAGP is NULL, the worst-case length is computed, with UPDATE being used to indicate whether an unconditional (-1), branch-likely (+1) or regular (0) branch is to be computed. */ static int relaxed_branch_length (fragS *fragp, asection *sec, int update) { bfd_boolean toofar; int length; if (fragp && S_IS_DEFINED (fragp->fr_symbol) && sec == S_GET_SEGMENT (fragp->fr_symbol)) { addressT addr; offsetT val; val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset; addr = fragp->fr_address + fragp->fr_fix + 4; val -= addr; toofar = val < - (0x8000 << 2) || val >= (0x8000 << 2); } else if (fragp) /* If the symbol is not defined or it's in a different segment, assume the user knows what's going on and emit a short branch. */ toofar = FALSE; else toofar = TRUE; if (fragp && update && toofar != RELAX_BRANCH_TOOFAR (fragp->fr_subtype)) fragp->fr_subtype = RELAX_BRANCH_ENCODE (RELAX_BRANCH_UNCOND (fragp->fr_subtype), RELAX_BRANCH_LIKELY (fragp->fr_subtype), RELAX_BRANCH_LINK (fragp->fr_subtype), toofar); length = 4; if (toofar) { if (fragp ? RELAX_BRANCH_LIKELY (fragp->fr_subtype) : (update > 0)) length += 8; if (mips_pic != NO_PIC) { /* Additional space for PIC loading of target address. */ length += 8; if (mips_opts.isa == ISA_MIPS1) /* Additional space for $at-stabilizing nop. */ length += 4; } /* If branch is conditional. */ if (fragp ? !RELAX_BRANCH_UNCOND (fragp->fr_subtype) : (update >= 0)) length += 8; } return length; } /* Estimate the size of a frag before relaxing. Unless this is the mips16, we are not really relaxing here, and the final size is encoded in the subtype information. For the mips16, we have to decide whether we are using an extended opcode or not. */ int md_estimate_size_before_relax (fragS *fragp, asection *segtype) { int change; if (RELAX_BRANCH_P (fragp->fr_subtype)) { fragp->fr_var = relaxed_branch_length (fragp, segtype, FALSE); return fragp->fr_var; } if (RELAX_MIPS16_P (fragp->fr_subtype)) /* We don't want to modify the EXTENDED bit here; it might get us into infinite loops. We change it only in mips_relax_frag(). */ return (RELAX_MIPS16_EXTENDED (fragp->fr_subtype) ? 4 : 2); if (mips_pic == NO_PIC) change = nopic_need_relax (fragp->fr_symbol, 0); else if (mips_pic == SVR4_PIC) change = pic_need_relax (fragp->fr_symbol, segtype); else if (mips_pic == VXWORKS_PIC) /* For vxworks, GOT16 relocations never have a corresponding LO16. */ change = 0; else abort (); if (change) { fragp->fr_subtype |= RELAX_USE_SECOND; return -RELAX_FIRST (fragp->fr_subtype); } else return -RELAX_SECOND (fragp->fr_subtype); } /* This is called to see whether a reloc against a defined symbol should be converted into a reloc against a section. */ int mips_fix_adjustable (fixS *fixp) { if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) return 0; if (fixp->fx_addsy == NULL) return 1; /* If symbol SYM is in a mergeable section, relocations of the form SYM + 0 can usually be made section-relative. The mergeable data is then identified by the section offset rather than by the symbol. However, if we're generating REL LO16 relocations, the offset is split between the LO16 and parterning high part relocation. The linker will need to recalculate the complete offset in order to correctly identify the merge data. The linker has traditionally not looked for the parterning high part relocation, and has thus allowed orphaned R_MIPS_LO16 relocations to be placed anywhere. Rather than break backwards compatibility by changing this, it seems better not to force the issue, and instead keep the original symbol. This will work with either linker behavior. */ if ((fixp->fx_r_type == BFD_RELOC_LO16 || fixp->fx_r_type == BFD_RELOC_MIPS16_LO16 || reloc_needs_lo_p (fixp->fx_r_type)) && HAVE_IN_PLACE_ADDENDS && (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_MERGE) != 0) return 0; #ifdef OBJ_ELF /* R_MIPS16_26 relocations against non-MIPS16 functions might resolve to a floating-point stub. The same is true for non-R_MIPS16_26 relocations against MIPS16 functions; in this case, the stub becomes the function's canonical address. Floating-point stubs are stored in unique .mips16.call.* or .mips16.fn.* sections. If a stub T for function F is in section S, the first relocation in section S must be against F; this is how the linker determines the target function. All relocations that might resolve to T must also be against F. We therefore have the following restrictions, which are given in an intentionally-redundant way: 1. We cannot reduce R_MIPS16_26 relocations against non-MIPS16 symbols. 2. We cannot reduce a stub's relocations against non-MIPS16 symbols if that stub might be used. 3. We cannot reduce non-R_MIPS16_26 relocations against MIPS16 symbols. 4. We cannot reduce a stub's relocations against MIPS16 symbols if that stub might be used. There is a further restriction: 5. We cannot reduce R_MIPS16_26 relocations against MIPS16 symbols on targets with in-place addends; the relocation field cannot encode the low bit. For simplicity, we deal with (3)-(5) by not reducing _any_ relocation against a MIPS16 symbol. We deal with (1)-(2) by saying that, if there's a R_MIPS16_26 relocation against some symbol R, no relocation against R may be reduced. (Note that this deals with (2) as well as (1) because relocations against global symbols will never be reduced on ELF targets.) This approach is a little simpler than trying to detect stub sections, and gives the "all or nothing" per-symbol consistency that we have for MIPS16 symbols. */ if (IS_ELF && fixp->fx_subsy == NULL && (S_GET_OTHER (fixp->fx_addsy) == STO_MIPS16 || *symbol_get_tc (fixp->fx_addsy))) return 0; #endif return 1; } /* Translate internal representation of relocation info to BFD target format. */ arelent ** tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp) { static arelent *retval[4]; arelent *reloc; bfd_reloc_code_real_type code; memset (retval, 0, sizeof(retval)); reloc = retval[0] = (arelent *) xcalloc (1, sizeof (arelent)); reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; if (fixp->fx_pcrel) { assert (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2); /* At this point, fx_addnumber is "symbol offset - pcrel address". Relocations want only the symbol offset. */ reloc->addend = fixp->fx_addnumber + reloc->address; if (!IS_ELF) { /* A gruesome hack which is a result of the gruesome gas reloc handling. What's worse, for COFF (as opposed to ECOFF), we might need yet another copy of reloc->address. See bfd_install_relocation. */ reloc->addend += reloc->address; } } else reloc->addend = fixp->fx_addnumber; /* Since the old MIPS ELF ABI uses Rel instead of Rela, encode the vtable entry to be used in the relocation's section offset. */ if (! HAVE_NEWABI && fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) { reloc->address = reloc->addend; reloc->addend = 0; } code = fixp->fx_r_type; reloc->howto = bfd_reloc_type_lookup (stdoutput, code); if (reloc->howto == NULL) { as_bad_where (fixp->fx_file, fixp->fx_line, _("Can not represent %s relocation in this object file format"), bfd_get_reloc_code_name (code)); retval[0] = NULL; } return retval; } /* Relax a machine dependent frag. This returns the amount by which the current size of the frag should change. */ int mips_relax_frag (asection *sec, fragS *fragp, long stretch) { if (RELAX_BRANCH_P (fragp->fr_subtype)) { offsetT old_var = fragp->fr_var; fragp->fr_var = relaxed_branch_length (fragp, sec, TRUE); return fragp->fr_var - old_var; } if (! RELAX_MIPS16_P (fragp->fr_subtype)) return 0; if (mips16_extended_frag (fragp, NULL, stretch)) { if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype)) return 0; fragp->fr_subtype = RELAX_MIPS16_MARK_EXTENDED (fragp->fr_subtype); return 2; } else { if (! RELAX_MIPS16_EXTENDED (fragp->fr_subtype)) return 0; fragp->fr_subtype = RELAX_MIPS16_CLEAR_EXTENDED (fragp->fr_subtype); return -2; } return 0; } /* Convert a machine dependent frag. */ void md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp) { if (RELAX_BRANCH_P (fragp->fr_subtype)) { bfd_byte *buf; unsigned long insn; expressionS exp; fixS *fixp; buf = (bfd_byte *)fragp->fr_literal + fragp->fr_fix; if (target_big_endian) insn = bfd_getb32 (buf); else insn = bfd_getl32 (buf); if (!RELAX_BRANCH_TOOFAR (fragp->fr_subtype)) { /* We generate a fixup instead of applying it right now because, if there are linker relaxations, we're going to need the relocations. */ exp.X_op = O_symbol; exp.X_add_symbol = fragp->fr_symbol; exp.X_add_number = fragp->fr_offset; fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal, 4, &exp, TRUE, BFD_RELOC_16_PCREL_S2); fixp->fx_file = fragp->fr_file; fixp->fx_line = fragp->fr_line; md_number_to_chars ((char *) buf, insn, 4); buf += 4; } else { int i; as_warn_where (fragp->fr_file, fragp->fr_line, _("relaxed out-of-range branch into a jump")); if (RELAX_BRANCH_UNCOND (fragp->fr_subtype)) goto uncond; if (!RELAX_BRANCH_LIKELY (fragp->fr_subtype)) { /* Reverse the branch. */ switch ((insn >> 28) & 0xf) { case 4: /* bc[0-3][tf]l? and bc1any[24][ft] instructions can have the condition reversed by tweaking a single bit, and their opcodes all have 0x4???????. */ assert ((insn & 0xf1000000) == 0x41000000); insn ^= 0x00010000; break; case 0: /* bltz 0x04000000 bgez 0x04010000 bltzal 0x04100000 bgezal 0x04110000 */ assert ((insn & 0xfc0e0000) == 0x04000000); insn ^= 0x00010000; break; case 1: /* beq 0x10000000 bne 0x14000000 blez 0x18000000 bgtz 0x1c000000 */ insn ^= 0x04000000; break; default: abort (); } } if (RELAX_BRANCH_LINK (fragp->fr_subtype)) { /* Clear the and-link bit. */ assert ((insn & 0xfc1c0000) == 0x04100000); /* bltzal 0x04100000 bgezal 0x04110000 bltzall 0x04120000 bgezall 0x04130000 */ insn &= ~0x00100000; } /* Branch over the branch (if the branch was likely) or the full jump (not likely case). Compute the offset from the current instruction to branch to. */ if (RELAX_BRANCH_LIKELY (fragp->fr_subtype)) i = 16; else { /* How many bytes in instructions we've already emitted? */ i = buf - (bfd_byte *)fragp->fr_literal - fragp->fr_fix; /* How many bytes in instructions from here to the end? */ i = fragp->fr_var - i; } /* Convert to instruction count. */ i >>= 2; /* Branch counts from the next instruction. */ i--; insn |= i; /* Branch over the jump. */ md_number_to_chars ((char *) buf, insn, 4); buf += 4; /* nop */ md_number_to_chars ((char *) buf, 0, 4); buf += 4; if (RELAX_BRANCH_LIKELY (fragp->fr_subtype)) { /* beql $0, $0, 2f */ insn = 0x50000000; /* Compute the PC offset from the current instruction to the end of the variable frag. */ /* How many bytes in instructions we've already emitted? */ i = buf - (bfd_byte *)fragp->fr_literal - fragp->fr_fix; /* How many bytes in instructions from here to the end? */ i = fragp->fr_var - i; /* Convert to instruction count. */ i >>= 2; /* Don't decrement i, because we want to branch over the delay slot. */ insn |= i; md_number_to_chars ((char *) buf, insn, 4); buf += 4; md_number_to_chars ((char *) buf, 0, 4); buf += 4; } uncond: if (mips_pic == NO_PIC) { /* j or jal. */ insn = (RELAX_BRANCH_LINK (fragp->fr_subtype) ? 0x0c000000 : 0x08000000); exp.X_op = O_symbol; exp.X_add_symbol = fragp->fr_symbol; exp.X_add_number = fragp->fr_offset; fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal, 4, &exp, FALSE, BFD_RELOC_MIPS_JMP); fixp->fx_file = fragp->fr_file; fixp->fx_line = fragp->fr_line; md_number_to_chars ((char *) buf, insn, 4); buf += 4; } else { /* lw/ld $at, ($gp) R_MIPS_GOT16 */ insn = HAVE_64BIT_ADDRESSES ? 0xdf810000 : 0x8f810000; exp.X_op = O_symbol; exp.X_add_symbol = fragp->fr_symbol; exp.X_add_number = fragp->fr_offset; if (fragp->fr_offset) { exp.X_add_symbol = make_expr_symbol (&exp); exp.X_add_number = 0; } fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal, 4, &exp, FALSE, BFD_RELOC_MIPS_GOT16); fixp->fx_file = fragp->fr_file; fixp->fx_line = fragp->fr_line; md_number_to_chars ((char *) buf, insn, 4); buf += 4; if (mips_opts.isa == ISA_MIPS1) { /* nop */ md_number_to_chars ((char *) buf, 0, 4); buf += 4; } /* d/addiu $at, $at, R_MIPS_LO16 */ insn = HAVE_64BIT_ADDRESSES ? 0x64210000 : 0x24210000; fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal, 4, &exp, FALSE, BFD_RELOC_LO16); fixp->fx_file = fragp->fr_file; fixp->fx_line = fragp->fr_line; md_number_to_chars ((char *) buf, insn, 4); buf += 4; /* j(al)r $at. */ if (RELAX_BRANCH_LINK (fragp->fr_subtype)) insn = 0x0020f809; else insn = 0x00200008; md_number_to_chars ((char *) buf, insn, 4); buf += 4; } } assert (buf == (bfd_byte *)fragp->fr_literal + fragp->fr_fix + fragp->fr_var); fragp->fr_fix += fragp->fr_var; return; } if (RELAX_MIPS16_P (fragp->fr_subtype)) { int type; const struct mips16_immed_operand *op; bfd_boolean small, ext; offsetT val; bfd_byte *buf; unsigned long insn; bfd_boolean use_extend; unsigned short extend; type = RELAX_MIPS16_TYPE (fragp->fr_subtype); op = mips16_immed_operands; while (op->type != type) ++op; if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype)) { small = FALSE; ext = TRUE; } else { small = TRUE; ext = FALSE; } resolve_symbol_value (fragp->fr_symbol); val = S_GET_VALUE (fragp->fr_symbol); if (op->pcrel) { addressT addr; addr = fragp->fr_address + fragp->fr_fix; /* The rules for the base address of a PC relative reloc are complicated; see mips16_extended_frag. */ if (type == 'p' || type == 'q') { addr += 2; if (ext) addr += 2; /* Ignore the low bit in the target, since it will be set for a text label. */ if ((val & 1) != 0) --val; } else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype)) addr -= 4; else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype)) addr -= 2; addr &= ~ (addressT) ((1 << op->shift) - 1); val -= addr; /* Make sure the section winds up with the alignment we have assumed. */ if (op->shift > 0) record_alignment (asec, op->shift); } if (ext && (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype) || RELAX_MIPS16_DSLOT (fragp->fr_subtype))) as_warn_where (fragp->fr_file, fragp->fr_line, _("extended instruction in delay slot")); buf = (bfd_byte *) (fragp->fr_literal + fragp->fr_fix); if (target_big_endian) insn = bfd_getb16 (buf); else insn = bfd_getl16 (buf); mips16_immed (fragp->fr_file, fragp->fr_line, type, val, RELAX_MIPS16_USER_EXT (fragp->fr_subtype), small, ext, &insn, &use_extend, &extend); if (use_extend) { md_number_to_chars ((char *) buf, 0xf000 | extend, 2); fragp->fr_fix += 2; buf += 2; } md_number_to_chars ((char *) buf, insn, 2); fragp->fr_fix += 2; buf += 2; } else { int first, second; fixS *fixp; first = RELAX_FIRST (fragp->fr_subtype); second = RELAX_SECOND (fragp->fr_subtype); fixp = (fixS *) fragp->fr_opcode; /* Possibly emit a warning if we've chosen the longer option. */ if (((fragp->fr_subtype & RELAX_USE_SECOND) != 0) == ((fragp->fr_subtype & RELAX_SECOND_LONGER) != 0)) { const char *msg = macro_warning (fragp->fr_subtype); if (msg != 0) as_warn_where (fragp->fr_file, fragp->fr_line, msg); } /* Go through all the fixups for the first sequence. Disable them (by marking them as done) if we're going to use the second sequence instead. */ while (fixp && fixp->fx_frag == fragp && fixp->fx_where < fragp->fr_fix - second) { if (fragp->fr_subtype & RELAX_USE_SECOND) fixp->fx_done = 1; fixp = fixp->fx_next; } /* Go through the fixups for the second sequence. Disable them if we're going to use the first sequence, otherwise adjust their addresses to account for the relaxation. */ while (fixp && fixp->fx_frag == fragp) { if (fragp->fr_subtype & RELAX_USE_SECOND) fixp->fx_where -= first; else fixp->fx_done = 1; fixp = fixp->fx_next; } /* Now modify the frag contents. */ if (fragp->fr_subtype & RELAX_USE_SECOND) { char *start; start = fragp->fr_literal + fragp->fr_fix - first - second; memmove (start, start + first, second); fragp->fr_fix -= first; } else fragp->fr_fix -= second; } } #ifdef OBJ_ELF /* This function is called after the relocs have been generated. We've been storing mips16 text labels as odd. Here we convert them back to even for the convenience of the debugger. */ void mips_frob_file_after_relocs (void) { asymbol **syms; unsigned int count, i; if (!IS_ELF) return; syms = bfd_get_outsymbols (stdoutput); count = bfd_get_symcount (stdoutput); for (i = 0; i < count; i++, syms++) { if (elf_symbol (*syms)->internal_elf_sym.st_other == STO_MIPS16 && ((*syms)->value & 1) != 0) { (*syms)->value &= ~1; /* If the symbol has an odd size, it was probably computed incorrectly, so adjust that as well. */ if ((elf_symbol (*syms)->internal_elf_sym.st_size & 1) != 0) ++elf_symbol (*syms)->internal_elf_sym.st_size; } } } #endif /* This function is called whenever a label is defined. It is used when handling branch delays; if a branch has a label, we assume we can not move it. */ void mips_define_label (symbolS *sym) { segment_info_type *si = seg_info (now_seg); struct insn_label_list *l; if (free_insn_labels == NULL) l = (struct insn_label_list *) xmalloc (sizeof *l); else { l = free_insn_labels; free_insn_labels = l->next; } l->label = sym; l->next = si->label_list; si->label_list = l; #ifdef OBJ_ELF dwarf2_emit_label (sym); #endif } #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) /* Some special processing for a MIPS ELF file. */ void mips_elf_final_processing (void) { /* Write out the register information. */ if (mips_abi != N64_ABI) { Elf32_RegInfo s; s.ri_gprmask = mips_gprmask; s.ri_cprmask[0] = mips_cprmask[0]; s.ri_cprmask[1] = mips_cprmask[1]; s.ri_cprmask[2] = mips_cprmask[2]; s.ri_cprmask[3] = mips_cprmask[3]; /* The gp_value field is set by the MIPS ELF backend. */ bfd_mips_elf32_swap_reginfo_out (stdoutput, &s, ((Elf32_External_RegInfo *) mips_regmask_frag)); } else { Elf64_Internal_RegInfo s; s.ri_gprmask = mips_gprmask; s.ri_pad = 0; s.ri_cprmask[0] = mips_cprmask[0]; s.ri_cprmask[1] = mips_cprmask[1]; s.ri_cprmask[2] = mips_cprmask[2]; s.ri_cprmask[3] = mips_cprmask[3]; /* The gp_value field is set by the MIPS ELF backend. */ bfd_mips_elf64_swap_reginfo_out (stdoutput, &s, ((Elf64_External_RegInfo *) mips_regmask_frag)); } /* Set the MIPS ELF flag bits. FIXME: There should probably be some sort of BFD interface for this. */ if (mips_any_noreorder) elf_elfheader (stdoutput)->e_flags |= EF_MIPS_NOREORDER; if (mips_pic != NO_PIC) { elf_elfheader (stdoutput)->e_flags |= EF_MIPS_PIC; elf_elfheader (stdoutput)->e_flags |= EF_MIPS_CPIC; } if (mips_abicalls) elf_elfheader (stdoutput)->e_flags |= EF_MIPS_CPIC; /* Set MIPS ELF flags for ASEs. */ /* We may need to define a new flag for DSP ASE, and set this flag when file_ase_dsp is true. */ /* Same for DSP R2. */ /* We may need to define a new flag for MT ASE, and set this flag when file_ase_mt is true. */ if (file_ase_mips16) elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_M16; #if 0 /* XXX FIXME */ if (file_ase_mips3d) elf_elfheader (stdoutput)->e_flags |= ???; #endif if (file_ase_mdmx) elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_MDMX; /* Set the MIPS ELF ABI flags. */ if (mips_abi == O32_ABI && USE_E_MIPS_ABI_O32) elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O32; else if (mips_abi == O64_ABI) elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O64; else if (mips_abi == EABI_ABI) { if (!file_mips_gp32) elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI64; else elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI32; } else if (mips_abi == N32_ABI) elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ABI2; /* Nothing to do for N64_ABI. */ if (mips_32bitmode) elf_elfheader (stdoutput)->e_flags |= EF_MIPS_32BITMODE; #if 0 /* XXX FIXME */ /* 32 bit code with 64 bit FP registers. */ if (!file_mips_fp32 && ABI_NEEDS_32BIT_REGS (mips_abi)) elf_elfheader (stdoutput)->e_flags |= ???; #endif } #endif /* OBJ_ELF || OBJ_MAYBE_ELF */ typedef struct proc { symbolS *func_sym; symbolS *func_end_sym; unsigned long reg_mask; unsigned long reg_offset; unsigned long fpreg_mask; unsigned long fpreg_offset; unsigned long frame_offset; unsigned long frame_reg; unsigned long pc_reg; } procS; static procS cur_proc; static procS *cur_proc_ptr; static int numprocs; /* Fill in an rs_align_code fragment. */ void mips_handle_align (fragS *fragp) { if (fragp->fr_type != rs_align_code) return; if (mips_opts.mips16) { static const unsigned char be_nop[] = { 0x65, 0x00 }; static const unsigned char le_nop[] = { 0x00, 0x65 }; int bytes; char *p; bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix; p = fragp->fr_literal + fragp->fr_fix; if (bytes & 1) { *p++ = 0; fragp->fr_fix++; } memcpy (p, (target_big_endian ? be_nop : le_nop), 2); fragp->fr_var = 2; } /* For mips32, a nop is a zero, which we trivially get by doing nothing. */ } static void md_obj_begin (void) { } static void md_obj_end (void) { /* Check for premature end, nesting errors, etc. */ if (cur_proc_ptr) as_warn (_("missing .end at end of assembly")); } static long get_number (void) { int negative = 0; long val = 0; if (*input_line_pointer == '-') { ++input_line_pointer; negative = 1; } if (!ISDIGIT (*input_line_pointer)) as_bad (_("expected simple number")); if (input_line_pointer[0] == '0') { if (input_line_pointer[1] == 'x') { input_line_pointer += 2; while (ISXDIGIT (*input_line_pointer)) { val <<= 4; val |= hex_value (*input_line_pointer++); } return negative ? -val : val; } else { ++input_line_pointer; while (ISDIGIT (*input_line_pointer)) { val <<= 3; val |= *input_line_pointer++ - '0'; } return negative ? -val : val; } } if (!ISDIGIT (*input_line_pointer)) { printf (_(" *input_line_pointer == '%c' 0x%02x\n"), *input_line_pointer, *input_line_pointer); as_warn (_("invalid number")); return -1; } while (ISDIGIT (*input_line_pointer)) { val *= 10; val += *input_line_pointer++ - '0'; } return negative ? -val : val; } /* The .file directive; just like the usual .file directive, but there is an initial number which is the ECOFF file index. In the non-ECOFF case .file implies DWARF-2. */ static void s_mips_file (int x ATTRIBUTE_UNUSED) { static int first_file_directive = 0; if (ECOFF_DEBUGGING) { get_number (); s_app_file (0); } else { char *filename; filename = dwarf2_directive_file (0); /* Versions of GCC up to 3.1 start files with a ".file" directive even for stabs output. Make sure that this ".file" is handled. Note that you need a version of GCC after 3.1 in order to support DWARF-2 on MIPS. */ if (filename != NULL && ! first_file_directive) { (void) new_logical_line (filename, -1); s_app_file_string (filename, 0); } first_file_directive = 1; } } /* The .loc directive, implying DWARF-2. */ static void s_mips_loc (int x ATTRIBUTE_UNUSED) { if (!ECOFF_DEBUGGING) dwarf2_directive_loc (0); } /* The .end directive. */ static void s_mips_end (int x ATTRIBUTE_UNUSED) { symbolS *p; /* Following functions need their own .frame and .cprestore directives. */ mips_frame_reg_valid = 0; mips_cprestore_valid = 0; if (!is_end_of_line[(unsigned char) *input_line_pointer]) { p = get_symbol (); demand_empty_rest_of_line (); } else p = NULL; if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) == 0) as_warn (_(".end not in text section")); if (!cur_proc_ptr) { as_warn (_(".end directive without a preceding .ent directive.")); demand_empty_rest_of_line (); return; } if (p != NULL) { assert (S_GET_NAME (p)); if (strcmp (S_GET_NAME (p), S_GET_NAME (cur_proc_ptr->func_sym))) as_warn (_(".end symbol does not match .ent symbol.")); if (debug_type == DEBUG_STABS) stabs_generate_asm_endfunc (S_GET_NAME (p), S_GET_NAME (p)); } else as_warn (_(".end directive missing or unknown symbol")); #ifdef OBJ_ELF /* Create an expression to calculate the size of the function. */ if (p && cur_proc_ptr) { OBJ_SYMFIELD_TYPE *obj = symbol_get_obj (p); expressionS *exp = xmalloc (sizeof (expressionS)); obj->size = exp; exp->X_op = O_subtract; exp->X_add_symbol = symbol_temp_new_now (); exp->X_op_symbol = p; exp->X_add_number = 0; cur_proc_ptr->func_end_sym = exp->X_add_symbol; } /* Generate a .pdr section. */ if (IS_ELF && !ECOFF_DEBUGGING && mips_flag_pdr) { segT saved_seg = now_seg; subsegT saved_subseg = now_subseg; valueT dot; expressionS exp; char *fragp; dot = frag_now_fix (); #ifdef md_flush_pending_output md_flush_pending_output (); #endif assert (pdr_seg); subseg_set (pdr_seg, 0); /* Write the symbol. */ exp.X_op = O_symbol; exp.X_add_symbol = p; exp.X_add_number = 0; emit_expr (&exp, 4); fragp = frag_more (7 * 4); md_number_to_chars (fragp, cur_proc_ptr->reg_mask, 4); md_number_to_chars (fragp + 4, cur_proc_ptr->reg_offset, 4); md_number_to_chars (fragp + 8, cur_proc_ptr->fpreg_mask, 4); md_number_to_chars (fragp + 12, cur_proc_ptr->fpreg_offset, 4); md_number_to_chars (fragp + 16, cur_proc_ptr->frame_offset, 4); md_number_to_chars (fragp + 20, cur_proc_ptr->frame_reg, 4); md_number_to_chars (fragp + 24, cur_proc_ptr->pc_reg, 4); subseg_set (saved_seg, saved_subseg); } #endif /* OBJ_ELF */ cur_proc_ptr = NULL; } /* The .aent and .ent directives. */ static void s_mips_ent (int aent) { symbolS *symbolP; symbolP = get_symbol (); if (*input_line_pointer == ',') ++input_line_pointer; SKIP_WHITESPACE (); if (ISDIGIT (*input_line_pointer) || *input_line_pointer == '-') get_number (); if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) == 0) as_warn (_(".ent or .aent not in text section.")); if (!aent && cur_proc_ptr) as_warn (_("missing .end")); if (!aent) { /* This function needs its own .frame and .cprestore directives. */ mips_frame_reg_valid = 0; mips_cprestore_valid = 0; cur_proc_ptr = &cur_proc; memset (cur_proc_ptr, '\0', sizeof (procS)); cur_proc_ptr->func_sym = symbolP; symbol_get_bfdsym (symbolP)->flags |= BSF_FUNCTION; ++numprocs; if (debug_type == DEBUG_STABS) stabs_generate_asm_func (S_GET_NAME (symbolP), S_GET_NAME (symbolP)); } demand_empty_rest_of_line (); } /* The .frame directive. If the mdebug section is present (IRIX 5 native) then ecoff.c (ecoff_directive_frame) is used. For embedded targets, s_mips_frame is used so that we can set the PDR information correctly. We can't use the ecoff routines because they make reference to the ecoff symbol table (in the mdebug section). */ static void s_mips_frame (int ignore ATTRIBUTE_UNUSED) { #ifdef OBJ_ELF if (IS_ELF && !ECOFF_DEBUGGING) { long val; if (cur_proc_ptr == (procS *) NULL) { as_warn (_(".frame outside of .ent")); demand_empty_rest_of_line (); return; } cur_proc_ptr->frame_reg = tc_get_register (1); SKIP_WHITESPACE (); if (*input_line_pointer++ != ',' || get_absolute_expression_and_terminator (&val) != ',') { as_warn (_("Bad .frame directive")); --input_line_pointer; demand_empty_rest_of_line (); return; } cur_proc_ptr->frame_offset = val; cur_proc_ptr->pc_reg = tc_get_register (0); demand_empty_rest_of_line (); } else #endif /* OBJ_ELF */ s_ignore (ignore); } /* The .fmask and .mask directives. If the mdebug section is present (IRIX 5 native) then ecoff.c (ecoff_directive_mask) is used. For embedded targets, s_mips_mask is used so that we can set the PDR information correctly. We can't use the ecoff routines because they make reference to the ecoff symbol table (in the mdebug section). */ static void s_mips_mask (int reg_type) { #ifdef OBJ_ELF if (IS_ELF && !ECOFF_DEBUGGING) { long mask, off; if (cur_proc_ptr == (procS *) NULL) { as_warn (_(".mask/.fmask outside of .ent")); demand_empty_rest_of_line (); return; } if (get_absolute_expression_and_terminator (&mask) != ',') { as_warn (_("Bad .mask/.fmask directive")); --input_line_pointer; demand_empty_rest_of_line (); return; } off = get_absolute_expression (); if (reg_type == 'F') { cur_proc_ptr->fpreg_mask = mask; cur_proc_ptr->fpreg_offset = off; } else { cur_proc_ptr->reg_mask = mask; cur_proc_ptr->reg_offset = off; } demand_empty_rest_of_line (); } else #endif /* OBJ_ELF */ s_ignore (reg_type); } /* A table describing all the processors gas knows about. Names are matched in the order listed. To ease comparison, please keep this table in the same order as gcc's mips_cpu_info_table[]. */ static const struct mips_cpu_info mips_cpu_info_table[] = { /* Entries for generic ISAs */ { "mips1", MIPS_CPU_IS_ISA, ISA_MIPS1, CPU_R3000 }, { "mips2", MIPS_CPU_IS_ISA, ISA_MIPS2, CPU_R6000 }, { "mips3", MIPS_CPU_IS_ISA, ISA_MIPS3, CPU_R4000 }, { "mips4", MIPS_CPU_IS_ISA, ISA_MIPS4, CPU_R8000 }, { "mips5", MIPS_CPU_IS_ISA, ISA_MIPS5, CPU_MIPS5 }, { "mips32", MIPS_CPU_IS_ISA, ISA_MIPS32, CPU_MIPS32 }, { "mips32r2", MIPS_CPU_IS_ISA, ISA_MIPS32R2, CPU_MIPS32R2 }, { "mips64", MIPS_CPU_IS_ISA, ISA_MIPS64, CPU_MIPS64 }, { "mips64r2", MIPS_CPU_IS_ISA, ISA_MIPS64R2, CPU_MIPS64R2 }, /* MIPS I */ { "r3000", 0, ISA_MIPS1, CPU_R3000 }, { "r2000", 0, ISA_MIPS1, CPU_R3000 }, { "r3900", 0, ISA_MIPS1, CPU_R3900 }, /* MIPS II */ { "r6000", 0, ISA_MIPS2, CPU_R6000 }, /* MIPS III */ { "r4000", 0, ISA_MIPS3, CPU_R4000 }, { "r4010", 0, ISA_MIPS2, CPU_R4010 }, { "vr4100", 0, ISA_MIPS3, CPU_VR4100 }, { "vr4111", 0, ISA_MIPS3, CPU_R4111 }, { "vr4120", 0, ISA_MIPS3, CPU_VR4120 }, { "vr4130", 0, ISA_MIPS3, CPU_VR4120 }, { "vr4181", 0, ISA_MIPS3, CPU_R4111 }, { "vr4300", 0, ISA_MIPS3, CPU_R4300 }, { "r4400", 0, ISA_MIPS3, CPU_R4400 }, { "r4600", 0, ISA_MIPS3, CPU_R4600 }, { "orion", 0, ISA_MIPS3, CPU_R4600 }, { "r4650", 0, ISA_MIPS3, CPU_R4650 }, /* MIPS IV */ { "r8000", 0, ISA_MIPS4, CPU_R8000 }, { "r10000", 0, ISA_MIPS4, CPU_R10000 }, { "r12000", 0, ISA_MIPS4, CPU_R12000 }, { "vr5000", 0, ISA_MIPS4, CPU_R5000 }, { "vr5400", 0, ISA_MIPS4, CPU_VR5400 }, { "vr5500", 0, ISA_MIPS4, CPU_VR5500 }, { "rm5200", 0, ISA_MIPS4, CPU_R5000 }, { "rm5230", 0, ISA_MIPS4, CPU_R5000 }, { "rm5231", 0, ISA_MIPS4, CPU_R5000 }, { "rm5261", 0, ISA_MIPS4, CPU_R5000 }, { "rm5721", 0, ISA_MIPS4, CPU_R5000 }, { "rm7000", 0, ISA_MIPS4, CPU_RM7000 }, { "rm9000", 0, ISA_MIPS4, CPU_RM9000 }, /* MIPS 32 */ { "4kc", 0, ISA_MIPS32, CPU_MIPS32 }, { "4km", 0, ISA_MIPS32, CPU_MIPS32 }, { "4kp", 0, ISA_MIPS32, CPU_MIPS32 }, { "4ksc", MIPS_CPU_ASE_SMARTMIPS, ISA_MIPS32, CPU_MIPS32 }, /* MIPS 32 Release 2 */ { "4kec", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, { "4kem", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, { "4kep", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, { "4ksd", MIPS_CPU_ASE_SMARTMIPS, ISA_MIPS32R2, CPU_MIPS32R2 }, { "m4k", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, { "m4kp", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, { "24kc", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, { "24kf", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, { "24kx", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, /* 24KE is a 24K with DSP ASE, other ASEs are optional. */ { "24kec", MIPS_CPU_ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 }, { "24kef", MIPS_CPU_ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 }, { "24kex", MIPS_CPU_ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 }, /* 34K is a 24K with DSP and MT ASE, other ASEs are optional. */ { "34kc", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 }, { "34kf", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 }, { "34kx", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 }, /* 74K with DSP and DSPR2 ASE, other ASEs are optional. */ { "74kc", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 }, { "74kf", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 }, { "74kx", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 }, /* MIPS 64 */ { "5kc", 0, ISA_MIPS64, CPU_MIPS64 }, { "5kf", 0, ISA_MIPS64, CPU_MIPS64 }, { "20kc", MIPS_CPU_ASE_MIPS3D, ISA_MIPS64, CPU_MIPS64 }, { "25kf", MIPS_CPU_ASE_MIPS3D, ISA_MIPS64, CPU_MIPS64 }, /* MIPS 64 Release 2 */ /* Broadcom SB-1 CPU core */ { "sb1", MIPS_CPU_ASE_MIPS3D | MIPS_CPU_ASE_MDMX, ISA_MIPS64, CPU_SB1 }, /* Broadcom SB-1A CPU core */ { "sb1a", MIPS_CPU_ASE_MIPS3D | MIPS_CPU_ASE_MDMX, ISA_MIPS64, CPU_SB1 }, /* Cavium Networks Octeon CPU core */ { "octeon", 0, ISA_MIPS64R2, CPU_OCTEON }, + { "octeon+", 0, ISA_MIPS64R2, CPU_OCTEON }, /* End marker */ { NULL, 0, 0, 0 } }; /* Return true if GIVEN is the same as CANONICAL, or if it is CANONICAL with a final "000" replaced by "k". Ignore case. Note: this function is shared between GCC and GAS. */ static bfd_boolean mips_strict_matching_cpu_name_p (const char *canonical, const char *given) { while (*given != 0 && TOLOWER (*given) == TOLOWER (*canonical)) given++, canonical++; return ((*given == 0 && *canonical == 0) || (strcmp (canonical, "000") == 0 && strcasecmp (given, "k") == 0)); } /* Return true if GIVEN matches CANONICAL, where GIVEN is a user-supplied CPU name. We've traditionally allowed a lot of variation here. Note: this function is shared between GCC and GAS. */ static bfd_boolean mips_matching_cpu_name_p (const char *canonical, const char *given) { /* First see if the name matches exactly, or with a final "000" turned into "k". */ if (mips_strict_matching_cpu_name_p (canonical, given)) return TRUE; /* If not, try comparing based on numerical designation alone. See if GIVEN is an unadorned number, or 'r' followed by a number. */ if (TOLOWER (*given) == 'r') given++; if (!ISDIGIT (*given)) return FALSE; /* Skip over some well-known prefixes in the canonical name, hoping to find a number there too. */ 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); } /* Parse an option that takes the name of a processor as its argument. OPTION is the name of the option and CPU_STRING is the argument. Return the corresponding processor enumeration if the CPU_STRING is recognized, otherwise report an error and return null. A similar function exists in GCC. */ static const struct mips_cpu_info * mips_parse_cpu (const char *option, const char *cpu_string) { const struct mips_cpu_info *p; /* 'from-abi' selects the most compatible architecture for the given ABI: MIPS I for 32-bit ABIs and MIPS III for 64-bit ABIs. For the EABIs, we have to decide whether we're using the 32-bit or 64-bit version. Look first at the -mgp options, if given, otherwise base the choice on MIPS_DEFAULT_64BIT. Treat NO_ABI like the EABIs. One reason to do this is that the plain 'mips' and 'mips64' configs have 'from-abi' as their default architecture. This code picks MIPS I for 'mips' and MIPS III for 'mips64', just as we did in the days before 'from-abi'. */ if (strcasecmp (cpu_string, "from-abi") == 0) { if (ABI_NEEDS_32BIT_REGS (mips_abi)) return mips_cpu_info_from_isa (ISA_MIPS1); if (ABI_NEEDS_64BIT_REGS (mips_abi)) return mips_cpu_info_from_isa (ISA_MIPS3); if (file_mips_gp32 >= 0) return mips_cpu_info_from_isa (file_mips_gp32 ? ISA_MIPS1 : ISA_MIPS3); return mips_cpu_info_from_isa (MIPS_DEFAULT_64BIT ? ISA_MIPS3 : ISA_MIPS1); } /* 'default' has traditionally been a no-op. Probably not very useful. */ 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; as_bad ("Bad value (%s) for %s", cpu_string, option); return 0; } /* Return the canonical processor information for ISA (a member of the ISA_MIPS* enumeration). */ static const struct mips_cpu_info * mips_cpu_info_from_isa (int isa) { int i; for (i = 0; mips_cpu_info_table[i].name != NULL; i++) if ((mips_cpu_info_table[i].flags & MIPS_CPU_IS_ISA) && isa == mips_cpu_info_table[i].isa) return (&mips_cpu_info_table[i]); return NULL; } static const struct mips_cpu_info * mips_cpu_info_from_arch (int arch) { int i; for (i = 0; mips_cpu_info_table[i].name != NULL; i++) if (arch == mips_cpu_info_table[i].cpu) return (&mips_cpu_info_table[i]); return NULL; } static void show (FILE *stream, const char *string, int *col_p, int *first_p) { if (*first_p) { fprintf (stream, "%24s", ""); *col_p = 24; } else { fprintf (stream, ", "); *col_p += 2; } if (*col_p + strlen (string) > 72) { fprintf (stream, "\n%24s", ""); *col_p = 24; } fprintf (stream, "%s", string); *col_p += strlen (string); *first_p = 0; } void md_show_usage (FILE *stream) { int column, first; size_t i; fprintf (stream, _("\ MIPS options:\n\ -EB generate big endian output\n\ -EL generate little endian output\n\ -g, -g2 do not remove unneeded NOPs or swap branches\n\ -G NUM allow referencing objects up to NUM bytes\n\ implicitly with the gp register [default 8]\n")); fprintf (stream, _("\ -mips1 generate MIPS ISA I instructions\n\ -mips2 generate MIPS ISA II instructions\n\ -mips3 generate MIPS ISA III instructions\n\ -mips4 generate MIPS ISA IV instructions\n\ -mips5 generate MIPS ISA V instructions\n\ -mips32 generate MIPS32 ISA instructions\n\ -mips32r2 generate MIPS32 release 2 ISA instructions\n\ -mips64 generate MIPS64 ISA instructions\n\ -mips64r2 generate MIPS64 release 2 ISA instructions\n\ -march=CPU/-mtune=CPU generate code/schedule for CPU, where CPU is one of:\n")); first = 1; for (i = 0; mips_cpu_info_table[i].name != NULL; i++) show (stream, mips_cpu_info_table[i].name, &column, &first); show (stream, "from-abi", &column, &first); fputc ('\n', stream); fprintf (stream, _("\ -mCPU equivalent to -march=CPU -mtune=CPU. Deprecated.\n\ -no-mCPU don't generate code specific to CPU.\n\ For -mCPU and -no-mCPU, CPU must be one of:\n")); first = 1; show (stream, "3900", &column, &first); show (stream, "4010", &column, &first); show (stream, "4100", &column, &first); show (stream, "4650", &column, &first); fputc ('\n', stream); fprintf (stream, _("\ -mips16 generate mips16 instructions\n\ -no-mips16 do not generate mips16 instructions\n")); fprintf (stream, _("\ -msmartmips generate smartmips instructions\n\ -mno-smartmips do not generate smartmips instructions\n")); fprintf (stream, _("\ -mdsp generate DSP instructions\n\ -mno-dsp do not generate DSP instructions\n")); fprintf (stream, _("\ -mdspr2 generate DSP R2 instructions\n\ -mno-dspr2 do not generate DSP R2 instructions\n")); fprintf (stream, _("\ -mmt generate MT instructions\n\ -mno-mt do not generate MT instructions\n")); fprintf (stream, _("\ -mfix-vr4120 work around certain VR4120 errata\n\ -mfix-vr4130 work around VR4130 mflo/mfhi errata\n\ -mgp32 use 32-bit GPRs, regardless of the chosen ISA\n\ -mfp32 use 32-bit FPRs, regardless of the chosen ISA\n\ -msym32 assume all symbols have 32-bit values\n\ -O0 remove unneeded NOPs, do not swap branches\n\ -O remove unneeded NOPs and swap branches\n\ --[no-]construct-floats [dis]allow floating point values to be constructed\n\ --trap, --no-break trap exception on div by 0 and mult overflow\n\ --break, --no-trap break exception on div by 0 and mult overflow\n")); #ifdef OBJ_ELF fprintf (stream, _("\ -KPIC, -call_shared generate SVR4 position independent code\n\ -mvxworks-pic generate VxWorks position independent code\n\ -non_shared do not generate position independent code\n\ -xgot assume a 32 bit GOT\n\ -mpdr, -mno-pdr enable/disable creation of .pdr sections\n\ -mshared, -mno-shared disable/enable .cpload optimization for\n\ position dependent (non shared) code\n\ -mabi=ABI create ABI conformant object file for:\n")); first = 1; show (stream, "32", &column, &first); show (stream, "o64", &column, &first); show (stream, "n32", &column, &first); show (stream, "64", &column, &first); show (stream, "eabi", &column, &first); fputc ('\n', stream); fprintf (stream, _("\ -32 create o32 ABI object file (default)\n\ -n32 create n32 ABI object file\n\ -64 create 64 ABI object file\n")); #endif fprintf (stream, _("\ -mocteon-unsupported error on unsupported Octeon instructions\n\ -mno-octeon-unsupported do not error on unsupported Octeon instructions\n")); fprintf (stream, _("\ -mocteon-useun generate Octeon unaligned load/store instructions\n\ -mno-octeon-useun generate MIPS unaligned load/store instructions\n")); } int mips_dwarf2_addr_size (void) { if (HAVE_64BIT_SYMBOLS) return 8; else return 4; } /* Standard calling conventions leave the CFA at SP on entry. */ void mips_cfi_frame_initial_instructions (void) { cfi_add_CFA_def_cfa_register (SP); } int tc_mips_regname_to_dw2regnum (char *regname) { unsigned int regnum = -1; unsigned int reg; if (reg_lookup (®name, RTYPE_GP | RTYPE_NUM, ®)) regnum = reg; return regnum; } Index: head/contrib/gcc/config/mips/mips.c =================================================================== --- head/contrib/gcc/config/mips/mips.c (revision 312898) +++ head/contrib/gcc/config/mips/mips.c (revision 312899) @@ -1,10869 +1,10870 @@ /* Subroutines used for MIPS code generation. Copyright (C) 1989, 1990, 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. Contributed by A. Lichnewsky, lich@inria.inria.fr. Changes by Michael Meissner, meissner@osf.org. 64 bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and Brendan Eich, brendan@microunity.com. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include #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 "optabs.h" #include "flags.h" #include "reload.h" #include "tm_p.h" #include "ggc.h" #include "gstab.h" #include "hashtab.h" #include "debug.h" #include "target.h" #include "target-def.h" #include "integrate.h" #include "langhooks.h" #include "cfglayout.h" #include "sched-int.h" #include "tree-gimple.h" #include "bitmap.h" /* True if X is an unspec wrapper around a SYMBOL_REF or LABEL_REF. */ #define UNSPEC_ADDRESS_P(X) \ (GET_CODE (X) == UNSPEC \ && XINT (X, 1) >= UNSPEC_ADDRESS_FIRST \ && XINT (X, 1) < UNSPEC_ADDRESS_FIRST + NUM_SYMBOL_TYPES) /* Extract the symbol or label from UNSPEC wrapper X. */ #define UNSPEC_ADDRESS(X) \ XVECEXP (X, 0, 0) /* Extract the symbol type from UNSPEC wrapper X. */ #define UNSPEC_ADDRESS_TYPE(X) \ ((enum mips_symbol_type) (XINT (X, 1) - UNSPEC_ADDRESS_FIRST)) /* The maximum distance between the top of the stack frame and the value $sp has when we save & restore registers. Use a maximum gap of 0x100 in the mips16 case. We can then use unextended instructions to save and restore registers, and to allocate and deallocate the top part of the frame. The value in the !mips16 case must be a SMALL_OPERAND and must preserve the maximum stack alignment. */ #define MIPS_MAX_FIRST_STACK_STEP (TARGET_MIPS16 ? 0x100 : 0x7ff0) /* True if INSN is a mips.md pattern or asm statement. */ #define USEFUL_INSN_P(INSN) \ (INSN_P (INSN) \ && GET_CODE (PATTERN (INSN)) != USE \ && GET_CODE (PATTERN (INSN)) != CLOBBER \ && GET_CODE (PATTERN (INSN)) != ADDR_VEC \ && GET_CODE (PATTERN (INSN)) != ADDR_DIFF_VEC) /* If INSN is a delayed branch sequence, return the first instruction in the sequence, otherwise return INSN itself. */ #define SEQ_BEGIN(INSN) \ (INSN_P (INSN) && GET_CODE (PATTERN (INSN)) == SEQUENCE \ ? XVECEXP (PATTERN (INSN), 0, 0) \ : (INSN)) /* Likewise for the last instruction in a delayed branch sequence. */ #define SEQ_END(INSN) \ (INSN_P (INSN) && GET_CODE (PATTERN (INSN)) == SEQUENCE \ ? XVECEXP (PATTERN (INSN), 0, XVECLEN (PATTERN (INSN), 0) - 1) \ : (INSN)) /* Execute the following loop body with SUBINSN set to each instruction between SEQ_BEGIN (INSN) and SEQ_END (INSN) inclusive. */ #define FOR_EACH_SUBINSN(SUBINSN, INSN) \ for ((SUBINSN) = SEQ_BEGIN (INSN); \ (SUBINSN) != NEXT_INSN (SEQ_END (INSN)); \ (SUBINSN) = NEXT_INSN (SUBINSN)) /* Classifies an address. ADDRESS_REG A natural register + offset address. The register satisfies mips_valid_base_register_p and the offset is a const_arith_operand. ADDRESS_LO_SUM A LO_SUM rtx. The first operand is a valid base register and the second operand is a symbolic address. ADDRESS_CONST_INT A signed 16-bit constant address. ADDRESS_SYMBOLIC: A constant symbolic address (equivalent to CONSTANT_SYMBOLIC). */ enum mips_address_type { ADDRESS_REG, ADDRESS_LO_SUM, ADDRESS_CONST_INT, ADDRESS_SYMBOLIC }; /* Classifies the prototype of a builtin function. */ enum mips_function_type { MIPS_V2SF_FTYPE_V2SF, MIPS_V2SF_FTYPE_V2SF_V2SF, MIPS_V2SF_FTYPE_V2SF_V2SF_INT, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF, MIPS_V2SF_FTYPE_SF_SF, MIPS_INT_FTYPE_V2SF_V2SF, MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF, MIPS_INT_FTYPE_SF_SF, MIPS_INT_FTYPE_DF_DF, MIPS_SF_FTYPE_V2SF, MIPS_SF_FTYPE_SF, MIPS_SF_FTYPE_SF_SF, MIPS_DF_FTYPE_DF, MIPS_DF_FTYPE_DF_DF, /* For MIPS DSP ASE */ MIPS_DI_FTYPE_DI_SI, MIPS_DI_FTYPE_DI_SI_SI, MIPS_DI_FTYPE_DI_V2HI_V2HI, MIPS_DI_FTYPE_DI_V4QI_V4QI, MIPS_SI_FTYPE_DI_SI, MIPS_SI_FTYPE_PTR_SI, MIPS_SI_FTYPE_SI, MIPS_SI_FTYPE_SI_SI, MIPS_SI_FTYPE_V2HI, MIPS_SI_FTYPE_V2HI_V2HI, MIPS_SI_FTYPE_V4QI, MIPS_SI_FTYPE_V4QI_V4QI, MIPS_SI_FTYPE_VOID, MIPS_V2HI_FTYPE_SI, MIPS_V2HI_FTYPE_SI_SI, MIPS_V2HI_FTYPE_V2HI, MIPS_V2HI_FTYPE_V2HI_SI, MIPS_V2HI_FTYPE_V2HI_V2HI, MIPS_V2HI_FTYPE_V4QI, MIPS_V2HI_FTYPE_V4QI_V2HI, MIPS_V4QI_FTYPE_SI, MIPS_V4QI_FTYPE_V2HI_V2HI, MIPS_V4QI_FTYPE_V4QI_SI, MIPS_V4QI_FTYPE_V4QI_V4QI, MIPS_VOID_FTYPE_SI_SI, MIPS_VOID_FTYPE_V2HI_V2HI, MIPS_VOID_FTYPE_V4QI_V4QI, /* The last type. */ MIPS_MAX_FTYPE_MAX }; /* Specifies how a builtin function should be converted into rtl. */ enum mips_builtin_type { /* The builtin corresponds directly to an .md pattern. The return value is mapped to operand 0 and the arguments are mapped to operands 1 and above. */ MIPS_BUILTIN_DIRECT, /* The builtin corresponds directly to an .md pattern. There is no return value and the arguments are mapped to operands 0 and above. */ MIPS_BUILTIN_DIRECT_NO_TARGET, /* The builtin corresponds to a comparison instruction followed by a mips_cond_move_tf_ps pattern. The first two arguments are the values to compare and the second two arguments are the vector operands for the movt.ps or movf.ps instruction (in assembly order). */ MIPS_BUILTIN_MOVF, MIPS_BUILTIN_MOVT, /* The builtin corresponds to a V2SF comparison instruction. Operand 0 of this instruction is the result of the comparison, which has mode CCV2 or CCV4. The function arguments are mapped to operands 1 and above. The function's return value is an SImode boolean that is true under the following conditions: MIPS_BUILTIN_CMP_ANY: one of the registers is true MIPS_BUILTIN_CMP_ALL: all of the registers are true MIPS_BUILTIN_CMP_LOWER: the first register is true MIPS_BUILTIN_CMP_UPPER: the second register is true. */ MIPS_BUILTIN_CMP_ANY, MIPS_BUILTIN_CMP_ALL, MIPS_BUILTIN_CMP_UPPER, MIPS_BUILTIN_CMP_LOWER, /* As above, but the instruction only sets a single $fcc register. */ MIPS_BUILTIN_CMP_SINGLE, /* For generating bposge32 branch instructions in MIPS32 DSP ASE. */ MIPS_BUILTIN_BPOSGE32 }; /* Invokes MACRO (COND) for each c.cond.fmt condition. */ #define MIPS_FP_CONDITIONS(MACRO) \ MACRO (f), \ MACRO (un), \ MACRO (eq), \ MACRO (ueq), \ MACRO (olt), \ MACRO (ult), \ MACRO (ole), \ MACRO (ule), \ MACRO (sf), \ MACRO (ngle), \ MACRO (seq), \ MACRO (ngl), \ MACRO (lt), \ MACRO (nge), \ MACRO (le), \ MACRO (ngt) /* Enumerates the codes above as MIPS_FP_COND_. */ #define DECLARE_MIPS_COND(X) MIPS_FP_COND_ ## X enum mips_fp_condition { MIPS_FP_CONDITIONS (DECLARE_MIPS_COND) }; /* Index X provides the string representation of MIPS_FP_COND_. */ #define STRINGIFY(X) #X static const char *const mips_fp_conditions[] = { MIPS_FP_CONDITIONS (STRINGIFY) }; /* A function to save or store a register. The first argument is the register and the second is the stack slot. */ typedef void (*mips_save_restore_fn) (rtx, rtx); struct mips16_constant; struct mips_arg_info; struct mips_address_info; struct mips_integer_op; struct mips_sim; static enum mips_symbol_type mips_classify_symbol (rtx); static void mips_split_const (rtx, rtx *, HOST_WIDE_INT *); static bool mips_offset_within_object_p (rtx, HOST_WIDE_INT); static bool mips_valid_base_register_p (rtx, enum machine_mode, int); static bool mips_symbolic_address_p (enum mips_symbol_type, enum machine_mode); static bool mips_classify_address (struct mips_address_info *, rtx, enum machine_mode, int); static bool mips_cannot_force_const_mem (rtx); static bool mips_use_blocks_for_constant_p (enum machine_mode, rtx); static int mips_symbol_insns (enum mips_symbol_type); static bool mips16_unextended_reference_p (enum machine_mode mode, rtx, rtx); static rtx mips_force_temporary (rtx, rtx); static rtx mips_unspec_offset_high (rtx, rtx, rtx, enum mips_symbol_type); static rtx mips_add_offset (rtx, rtx, HOST_WIDE_INT); static unsigned int mips_build_shift (struct mips_integer_op *, HOST_WIDE_INT); static unsigned int mips_build_lower (struct mips_integer_op *, unsigned HOST_WIDE_INT); static unsigned int mips_build_integer (struct mips_integer_op *, unsigned HOST_WIDE_INT); static void mips_legitimize_const_move (enum machine_mode, rtx, rtx); static int m16_check_op (rtx, int, int, int); static bool mips_rtx_costs (rtx, int, int, int *); static int mips_address_cost (rtx); static void mips_emit_compare (enum rtx_code *, rtx *, rtx *, bool); static void mips_load_call_address (rtx, rtx, int); static bool mips_function_ok_for_sibcall (tree, tree); static void mips_block_move_straight (rtx, rtx, HOST_WIDE_INT); static void mips_adjust_block_mem (rtx, HOST_WIDE_INT, rtx *, rtx *); static void mips_block_move_loop (rtx, rtx, HOST_WIDE_INT); static void mips_arg_info (const CUMULATIVE_ARGS *, enum machine_mode, tree, int, struct mips_arg_info *); static bool mips_get_unaligned_mem (rtx *, unsigned int, int, rtx *, rtx *); static void mips_set_architecture (const struct mips_cpu_info *); static void mips_set_tune (const struct mips_cpu_info *); static bool mips_handle_option (size_t, const char *, int); static struct machine_function *mips_init_machine_status (void); static void print_operand_reloc (FILE *, rtx, const char **); #if TARGET_IRIX static void irix_output_external_libcall (rtx); #endif static void mips_file_start (void); static void mips_file_end (void); static bool mips_rewrite_small_data_p (rtx); static int mips_small_data_pattern_1 (rtx *, void *); static int mips_rewrite_small_data_1 (rtx *, void *); static bool mips_function_has_gp_insn (void); static unsigned int mips_global_pointer (void); static bool mips_save_reg_p (unsigned int); static void mips_save_restore_reg (enum machine_mode, int, HOST_WIDE_INT, mips_save_restore_fn); static void mips_for_each_saved_reg (HOST_WIDE_INT, mips_save_restore_fn); static void mips_output_cplocal (void); static void mips_emit_loadgp (void); static void mips_output_function_prologue (FILE *, HOST_WIDE_INT); static void mips_set_frame_expr (rtx); static rtx mips_frame_set (rtx, rtx); static void mips_save_reg (rtx, rtx); static void mips_output_function_epilogue (FILE *, HOST_WIDE_INT); static void mips_restore_reg (rtx, rtx); static void mips_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree); static int symbolic_expression_p (rtx); static section *mips_select_rtx_section (enum machine_mode, rtx, unsigned HOST_WIDE_INT); static section *mips_function_rodata_section (tree); static bool mips_in_small_data_p (tree); static bool mips_use_anchors_for_symbol_p (rtx); static int mips_fpr_return_fields (tree, tree *); static bool mips_return_in_msb (tree); static rtx mips_return_fpr_pair (enum machine_mode mode, enum machine_mode mode1, HOST_WIDE_INT, enum machine_mode mode2, HOST_WIDE_INT); static rtx mips16_gp_pseudo_reg (void); static void mips16_fp_args (FILE *, int, int); static void build_mips16_function_stub (FILE *); static rtx dump_constants_1 (enum machine_mode, rtx, rtx); static void dump_constants (struct mips16_constant *, rtx); static int mips16_insn_length (rtx); static int mips16_rewrite_pool_refs (rtx *, void *); static void mips16_lay_out_constants (void); static void mips_sim_reset (struct mips_sim *); static void mips_sim_init (struct mips_sim *, state_t); static void mips_sim_next_cycle (struct mips_sim *); static void mips_sim_wait_reg (struct mips_sim *, rtx, rtx); static int mips_sim_wait_regs_2 (rtx *, void *); static void mips_sim_wait_regs_1 (rtx *, void *); static void mips_sim_wait_regs (struct mips_sim *, rtx); static void mips_sim_wait_units (struct mips_sim *, rtx); static void mips_sim_wait_insn (struct mips_sim *, rtx); static void mips_sim_record_set (rtx, rtx, void *); static void mips_sim_issue_insn (struct mips_sim *, rtx); static void mips_sim_issue_nop (struct mips_sim *); static void mips_sim_finish_insn (struct mips_sim *, rtx); static void vr4130_avoid_branch_rt_conflict (rtx); static void vr4130_align_insns (void); static void mips_avoid_hazard (rtx, rtx, int *, rtx *, rtx); static void mips_avoid_hazards (void); static void mips_reorg (void); static bool mips_strict_matching_cpu_name_p (const char *, const char *); static bool mips_matching_cpu_name_p (const char *, const char *); static const struct mips_cpu_info *mips_parse_cpu (const char *); static const struct mips_cpu_info *mips_cpu_info_from_isa (int); static bool mips_return_in_memory (tree, tree); static bool mips_strict_argument_naming (CUMULATIVE_ARGS *); static void mips_macc_chains_record (rtx); static void mips_macc_chains_reorder (rtx *, int); static void vr4130_true_reg_dependence_p_1 (rtx, rtx, void *); static bool vr4130_true_reg_dependence_p (rtx); static bool vr4130_swap_insns_p (rtx, rtx); static void vr4130_reorder (rtx *, int); static void mips_promote_ready (rtx *, int, int); static int mips_sched_reorder (FILE *, int, rtx *, int *, int); static int mips_variable_issue (FILE *, int, rtx, int); static int mips_adjust_cost (rtx, rtx, rtx, int); static int mips_issue_rate (void); static int mips_multipass_dfa_lookahead (void); static void mips_init_libfuncs (void); static void mips_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode, tree, int *, int); static tree mips_build_builtin_va_list (void); static tree mips_gimplify_va_arg_expr (tree, tree, tree *, tree *); static bool mips_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode mode, tree, bool); static bool mips_callee_copies (CUMULATIVE_ARGS *, enum machine_mode mode, tree, bool); static int mips_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode mode, tree, bool); static bool mips_valid_pointer_mode (enum machine_mode); static bool mips_vector_mode_supported_p (enum machine_mode); static rtx mips_prepare_builtin_arg (enum insn_code, unsigned int, tree *); static rtx mips_prepare_builtin_target (enum insn_code, unsigned int, rtx); static rtx mips_expand_builtin (tree, rtx, rtx, enum machine_mode, int); static void mips_init_builtins (void); static rtx mips_expand_builtin_direct (enum insn_code, rtx, tree, bool); static rtx mips_expand_builtin_movtf (enum mips_builtin_type, enum insn_code, enum mips_fp_condition, rtx, tree); static rtx mips_expand_builtin_compare (enum mips_builtin_type, enum insn_code, enum mips_fp_condition, rtx, tree); static rtx mips_expand_builtin_bposge (enum mips_builtin_type, rtx); static void mips_encode_section_info (tree, rtx, int); static void mips_extra_live_on_entry (bitmap); static int mips_mode_rep_extended (enum machine_mode, enum machine_mode); /* Structure to be filled in by compute_frame_size with register save masks, and offsets for the current function. */ struct mips_frame_info GTY(()) { HOST_WIDE_INT total_size; /* # bytes that the entire frame takes up */ HOST_WIDE_INT var_size; /* # bytes that variables take up */ HOST_WIDE_INT args_size; /* # bytes that outgoing arguments take up */ HOST_WIDE_INT cprestore_size; /* # bytes that the .cprestore slot takes up */ HOST_WIDE_INT gp_reg_size; /* # bytes needed to store gp regs */ HOST_WIDE_INT fp_reg_size; /* # bytes needed to store fp regs */ unsigned int mask; /* mask of saved gp registers */ unsigned int fmask; /* mask of saved fp registers */ HOST_WIDE_INT gp_save_offset; /* offset from vfp to store gp registers */ HOST_WIDE_INT fp_save_offset; /* offset from vfp to store fp registers */ HOST_WIDE_INT gp_sp_offset; /* offset from new sp to store gp registers */ HOST_WIDE_INT fp_sp_offset; /* offset from new sp to store fp registers */ bool initialized; /* true if frame size already calculated */ int num_gp; /* number of gp registers saved */ int num_fp; /* number of fp registers saved */ }; struct machine_function GTY(()) { /* Pseudo-reg holding the value of $28 in a mips16 function which refers to GP relative global variables. */ rtx mips16_gp_pseudo_rtx; /* The number of extra stack bytes taken up by register varargs. This area is allocated by the callee at the very top of the frame. */ int varargs_size; /* Current frame information, calculated by compute_frame_size. */ struct mips_frame_info frame; /* The register to use as the global pointer within this function. */ unsigned int global_pointer; /* True if mips_adjust_insn_length should ignore an instruction's hazard attribute. */ bool ignore_hazard_length_p; /* True if the whole function is suitable for .set noreorder and .set nomacro. */ bool all_noreorder_p; /* True if the function is known to have an instruction that needs $gp. */ bool has_gp_insn_p; }; /* Information about a single argument. */ struct mips_arg_info { /* True if the argument is passed in a floating-point register, or would have been if we hadn't run out of registers. */ bool fpr_p; /* The number of words passed in registers, rounded up. */ unsigned int reg_words; /* For EABI, the offset of the first register from GP_ARG_FIRST or FP_ARG_FIRST. For other ABIs, the offset of the first register from the start of the ABI's argument structure (see the CUMULATIVE_ARGS comment for details). The value is MAX_ARGS_IN_REGISTERS if the argument is passed entirely on the stack. */ unsigned int reg_offset; /* The number of words that must be passed on the stack, rounded up. */ unsigned int stack_words; /* The offset from the start of the stack overflow area of the argument's first stack word. Only meaningful when STACK_WORDS is nonzero. */ unsigned int stack_offset; }; /* Information about an address described by mips_address_type. ADDRESS_CONST_INT No fields are used. ADDRESS_REG REG is the base register and OFFSET is the constant offset. ADDRESS_LO_SUM REG is the register that contains the high part of the address, OFFSET is the symbolic address being referenced and SYMBOL_TYPE is the type of OFFSET's symbol. ADDRESS_SYMBOLIC SYMBOL_TYPE is the type of symbol being referenced. */ struct mips_address_info { enum mips_address_type type; rtx reg; rtx offset; enum mips_symbol_type symbol_type; }; /* One stage in a constant building sequence. These sequences have the form: A = VALUE[0] A = A CODE[1] VALUE[1] A = A CODE[2] VALUE[2] ... where A is an accumulator, each CODE[i] is a binary rtl operation and each VALUE[i] is a constant integer. */ struct mips_integer_op { enum rtx_code code; unsigned HOST_WIDE_INT value; }; /* The largest number of operations needed to load an integer constant. The worst accepted case for 64-bit constants is LUI,ORI,SLL,ORI,SLL,ORI. When the lowest bit is clear, we can try, but reject a sequence with an extra SLL at the end. */ #define MIPS_MAX_INTEGER_OPS 7 /* Global variables for machine-dependent things. */ /* Threshold for data being put into the small data/bss area, instead of the normal data area. */ int mips_section_threshold = -1; /* Count the number of .file directives, so that .loc is up to date. */ int num_source_filenames = 0; /* Count the number of sdb related labels are generated (to find block start and end boundaries). */ int sdb_label_count = 0; /* Next label # for each statement for Silicon Graphics IRIS systems. */ int sym_lineno = 0; /* Linked list of all externals that are to be emitted when optimizing for the global pointer if they haven't been declared by the end of the program with an appropriate .comm or initialization. */ struct extern_list GTY (()) { struct extern_list *next; /* next external */ const char *name; /* name of the external */ int size; /* size in bytes */ }; static GTY (()) struct extern_list *extern_head = 0; /* Name of the file containing the current function. */ const char *current_function_file = ""; /* Number of nested .set noreorder, noat, nomacro, and volatile requests. */ int set_noreorder; int set_noat; int set_nomacro; int set_volatile; /* The next branch instruction is a branch likely, not branch normal. */ int mips_branch_likely; /* The operands passed to the last cmpMM expander. */ rtx cmp_operands[2]; /* The target cpu for code generation. */ enum processor_type mips_arch; const struct mips_cpu_info *mips_arch_info; /* The target cpu for optimization and scheduling. */ enum processor_type mips_tune; const struct mips_cpu_info *mips_tune_info; /* Which instruction set architecture to use. */ int mips_isa; /* Which ABI to use. */ int mips_abi = MIPS_ABI_DEFAULT; /* Cost information to use. */ const struct mips_rtx_cost_data *mips_cost; /* Whether we are generating mips16 hard float code. In mips16 mode we always set TARGET_SOFT_FLOAT; this variable is nonzero if -msoft-float was not specified by the user, which means that we should arrange to call mips32 hard floating point code. */ int mips16_hard_float; /* The architecture selected by -mipsN. */ static const struct mips_cpu_info *mips_isa_info; /* If TRUE, we split addresses into their high and low parts in the RTL. */ int mips_split_addresses; /* Mode used for saving/restoring general purpose registers. */ static enum machine_mode gpr_mode; /* Array giving truth value on whether or not a given hard register can support a given mode. */ char mips_hard_regno_mode_ok[(int)MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER]; /* List of all MIPS punctuation characters used by print_operand. */ char mips_print_operand_punct[256]; /* Map GCC register number to debugger register number. */ int mips_dbx_regno[FIRST_PSEUDO_REGISTER]; /* A copy of the original flag_delayed_branch: see override_options. */ static int mips_flag_delayed_branch; static GTY (()) int mips_output_filename_first_time = 1; /* mips_split_p[X] is true if symbols of type X can be split by mips_split_symbol(). */ bool mips_split_p[NUM_SYMBOL_TYPES]; /* mips_lo_relocs[X] is the relocation to use when a symbol of type X appears in a LO_SUM. It can be null if such LO_SUMs aren't valid or if they are matched by a special .md file pattern. */ static const char *mips_lo_relocs[NUM_SYMBOL_TYPES]; /* Likewise for HIGHs. */ static const char *mips_hi_relocs[NUM_SYMBOL_TYPES]; /* Map hard register number to register class */ const enum reg_class mips_regno_to_class[] = { LEA_REGS, LEA_REGS, M16_NA_REGS, V1_REG, M16_REGS, M16_REGS, M16_REGS, M16_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, M16_NA_REGS, M16_NA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, T_REG, PIC_FN_ADDR_REG, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_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, NO_REGS, ST_REGS, ST_REGS, ST_REGS, ST_REGS, ST_REGS, ST_REGS, ST_REGS, ST_REGS, NO_REGS, NO_REGS, ALL_REGS, ALL_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, DSP_ACC_REGS, DSP_ACC_REGS, DSP_ACC_REGS, DSP_ACC_REGS, DSP_ACC_REGS, DSP_ACC_REGS, ALL_REGS, ALL_REGS, ALL_REGS, ALL_REGS, ALL_REGS, ALL_REGS }; /* Table of machine dependent attributes. */ const struct attribute_spec mips_attribute_table[] = { { "long_call", 0, 0, false, true, true, NULL }, { NULL, 0, 0, false, false, false, NULL } }; /* A table describing all the processors gcc knows about. Names are matched in the order listed. The first mention of an ISA level is taken as the canonical name for that ISA. To ease comparison, please keep this table in the same order as gas's mips_cpu_info_table[]. */ const struct mips_cpu_info mips_cpu_info_table[] = { /* Entries for generic ISAs */ { "mips1", PROCESSOR_R3000, 1 }, { "mips2", PROCESSOR_R6000, 2 }, { "mips3", PROCESSOR_R4000, 3 }, { "mips4", PROCESSOR_R8000, 4 }, { "mips32", PROCESSOR_4KC, 32 }, { "mips32r2", PROCESSOR_M4K, 33 }, { "mips64", PROCESSOR_5KC, 64 }, { "mips64r2", PROCESSOR_5KC, 65 }, /* MIPS I */ { "r3000", PROCESSOR_R3000, 1 }, { "r2000", PROCESSOR_R3000, 1 }, /* = r3000 */ { "r3900", PROCESSOR_R3900, 1 }, /* MIPS II */ { "r6000", PROCESSOR_R6000, 2 }, /* MIPS III */ { "r4000", PROCESSOR_R4000, 3 }, { "vr4100", PROCESSOR_R4100, 3 }, { "vr4111", PROCESSOR_R4111, 3 }, { "vr4120", PROCESSOR_R4120, 3 }, { "vr4130", PROCESSOR_R4130, 3 }, { "vr4300", PROCESSOR_R4300, 3 }, { "r4400", PROCESSOR_R4000, 3 }, /* = r4000 */ { "r4600", PROCESSOR_R4600, 3 }, { "orion", PROCESSOR_R4600, 3 }, /* = r4600 */ { "r4650", PROCESSOR_R4650, 3 }, /* MIPS IV */ { "r8000", PROCESSOR_R8000, 4 }, { "vr5000", PROCESSOR_R5000, 4 }, { "vr5400", PROCESSOR_R5400, 4 }, { "vr5500", PROCESSOR_R5500, 4 }, { "rm7000", PROCESSOR_R7000, 4 }, { "rm9000", PROCESSOR_R9000, 4 }, /* MIPS32 */ { "4kc", PROCESSOR_4KC, 32 }, { "4km", PROCESSOR_4KC, 32 }, /* = 4kc */ { "4kp", PROCESSOR_4KP, 32 }, /* MIPS32 Release 2 */ { "m4k", PROCESSOR_M4K, 33 }, { "24k", PROCESSOR_24K, 33 }, { "24kc", PROCESSOR_24K, 33 }, /* 24K no FPU */ { "24kf", PROCESSOR_24K, 33 }, /* 24K 1:2 FPU */ { "24kx", PROCESSOR_24KX, 33 }, /* 24K 1:1 FPU */ /* MIPS64 */ { "5kc", PROCESSOR_5KC, 64 }, { "5kf", PROCESSOR_5KF, 64 }, { "20kc", PROCESSOR_20KC, 64 }, { "sb1", PROCESSOR_SB1, 64 }, { "sb1a", PROCESSOR_SB1A, 64 }, { "sr71000", PROCESSOR_SR71000, 64 }, /* MIPS64R2 */ { "octeon", PROCESSOR_OCTEON, 65 }, + { "octeon+", PROCESSOR_OCTEON, 65 }, /* End marker */ { 0, 0, 0 } }; /* Default costs. If these are used for a processor we should look up the actual costs. */ #define DEFAULT_COSTS COSTS_N_INSNS (6), /* fp_add */ \ COSTS_N_INSNS (7), /* fp_mult_sf */ \ COSTS_N_INSNS (8), /* fp_mult_df */ \ COSTS_N_INSNS (23), /* fp_div_sf */ \ COSTS_N_INSNS (36), /* fp_div_df */ \ COSTS_N_INSNS (10), /* int_mult_si */ \ COSTS_N_INSNS (10), /* int_mult_di */ \ COSTS_N_INSNS (69), /* int_div_si */ \ COSTS_N_INSNS (69), /* int_div_di */ \ 2, /* branch_cost */ \ 4 /* memory_latency */ /* Need to replace these with the costs of calling the appropriate libgcc routine. */ #define SOFT_FP_COSTS COSTS_N_INSNS (256), /* fp_add */ \ COSTS_N_INSNS (256), /* fp_mult_sf */ \ COSTS_N_INSNS (256), /* fp_mult_df */ \ COSTS_N_INSNS (256), /* fp_div_sf */ \ COSTS_N_INSNS (256) /* fp_div_df */ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] = { { /* R3000 */ COSTS_N_INSNS (2), /* fp_add */ COSTS_N_INSNS (4), /* fp_mult_sf */ COSTS_N_INSNS (5), /* fp_mult_df */ COSTS_N_INSNS (12), /* fp_div_sf */ COSTS_N_INSNS (19), /* fp_div_df */ COSTS_N_INSNS (12), /* int_mult_si */ COSTS_N_INSNS (12), /* int_mult_di */ COSTS_N_INSNS (35), /* int_div_si */ COSTS_N_INSNS (35), /* int_div_di */ 1, /* branch_cost */ 4 /* memory_latency */ }, { /* 4KC */ SOFT_FP_COSTS, COSTS_N_INSNS (6), /* int_mult_si */ COSTS_N_INSNS (6), /* int_mult_di */ COSTS_N_INSNS (36), /* int_div_si */ COSTS_N_INSNS (36), /* int_div_di */ 1, /* branch_cost */ 4 /* memory_latency */ }, { /* 4KP */ SOFT_FP_COSTS, COSTS_N_INSNS (36), /* int_mult_si */ COSTS_N_INSNS (36), /* int_mult_di */ COSTS_N_INSNS (37), /* int_div_si */ COSTS_N_INSNS (37), /* int_div_di */ 1, /* branch_cost */ 4 /* memory_latency */ }, { /* 5KC */ SOFT_FP_COSTS, COSTS_N_INSNS (4), /* int_mult_si */ COSTS_N_INSNS (11), /* int_mult_di */ COSTS_N_INSNS (36), /* int_div_si */ COSTS_N_INSNS (68), /* int_div_di */ 1, /* branch_cost */ 4 /* memory_latency */ }, { /* 5KF */ COSTS_N_INSNS (4), /* fp_add */ COSTS_N_INSNS (4), /* fp_mult_sf */ COSTS_N_INSNS (5), /* fp_mult_df */ COSTS_N_INSNS (17), /* fp_div_sf */ COSTS_N_INSNS (32), /* fp_div_df */ COSTS_N_INSNS (4), /* int_mult_si */ COSTS_N_INSNS (11), /* int_mult_di */ COSTS_N_INSNS (36), /* int_div_si */ COSTS_N_INSNS (68), /* int_div_di */ 1, /* branch_cost */ 4 /* memory_latency */ }, { /* 20KC */ DEFAULT_COSTS }, { /* 24k */ COSTS_N_INSNS (8), /* fp_add */ COSTS_N_INSNS (8), /* fp_mult_sf */ COSTS_N_INSNS (10), /* fp_mult_df */ COSTS_N_INSNS (34), /* fp_div_sf */ COSTS_N_INSNS (64), /* fp_div_df */ COSTS_N_INSNS (5), /* int_mult_si */ COSTS_N_INSNS (5), /* int_mult_di */ COSTS_N_INSNS (41), /* int_div_si */ COSTS_N_INSNS (41), /* int_div_di */ 1, /* branch_cost */ 4 /* memory_latency */ }, { /* 24kx */ COSTS_N_INSNS (4), /* fp_add */ COSTS_N_INSNS (4), /* fp_mult_sf */ COSTS_N_INSNS (5), /* fp_mult_df */ COSTS_N_INSNS (17), /* fp_div_sf */ COSTS_N_INSNS (32), /* fp_div_df */ COSTS_N_INSNS (5), /* int_mult_si */ COSTS_N_INSNS (5), /* int_mult_di */ COSTS_N_INSNS (41), /* int_div_si */ COSTS_N_INSNS (41), /* int_div_di */ 1, /* branch_cost */ 4 /* memory_latency */ }, { /* M4k */ DEFAULT_COSTS }, { /* R3900 */ COSTS_N_INSNS (2), /* fp_add */ COSTS_N_INSNS (4), /* fp_mult_sf */ COSTS_N_INSNS (5), /* fp_mult_df */ COSTS_N_INSNS (12), /* fp_div_sf */ COSTS_N_INSNS (19), /* fp_div_df */ COSTS_N_INSNS (2), /* int_mult_si */ COSTS_N_INSNS (2), /* int_mult_di */ COSTS_N_INSNS (35), /* int_div_si */ COSTS_N_INSNS (35), /* int_div_di */ 1, /* branch_cost */ 4 /* memory_latency */ }, { /* R6000 */ COSTS_N_INSNS (3), /* fp_add */ COSTS_N_INSNS (5), /* fp_mult_sf */ COSTS_N_INSNS (6), /* fp_mult_df */ COSTS_N_INSNS (15), /* fp_div_sf */ COSTS_N_INSNS (16), /* fp_div_df */ COSTS_N_INSNS (17), /* int_mult_si */ COSTS_N_INSNS (17), /* int_mult_di */ COSTS_N_INSNS (38), /* int_div_si */ COSTS_N_INSNS (38), /* int_div_di */ 2, /* branch_cost */ 6 /* memory_latency */ }, { /* R4000 */ COSTS_N_INSNS (6), /* fp_add */ COSTS_N_INSNS (7), /* fp_mult_sf */ COSTS_N_INSNS (8), /* fp_mult_df */ COSTS_N_INSNS (23), /* fp_div_sf */ COSTS_N_INSNS (36), /* fp_div_df */ COSTS_N_INSNS (10), /* int_mult_si */ COSTS_N_INSNS (10), /* int_mult_di */ COSTS_N_INSNS (69), /* int_div_si */ COSTS_N_INSNS (69), /* int_div_di */ 2, /* branch_cost */ 6 /* memory_latency */ }, { /* R4100 */ DEFAULT_COSTS }, { /* R4111 */ DEFAULT_COSTS }, { /* R4120 */ DEFAULT_COSTS }, { /* R4130 */ /* The only costs that appear to be updated here are integer multiplication. */ SOFT_FP_COSTS, COSTS_N_INSNS (4), /* int_mult_si */ COSTS_N_INSNS (6), /* int_mult_di */ COSTS_N_INSNS (69), /* int_div_si */ COSTS_N_INSNS (69), /* int_div_di */ 1, /* branch_cost */ 4 /* memory_latency */ }, { /* R4300 */ DEFAULT_COSTS }, { /* R4600 */ DEFAULT_COSTS }, { /* R4650 */ DEFAULT_COSTS }, { /* R5000 */ COSTS_N_INSNS (6), /* fp_add */ COSTS_N_INSNS (4), /* fp_mult_sf */ COSTS_N_INSNS (5), /* fp_mult_df */ COSTS_N_INSNS (23), /* fp_div_sf */ COSTS_N_INSNS (36), /* fp_div_df */ COSTS_N_INSNS (5), /* int_mult_si */ COSTS_N_INSNS (5), /* int_mult_di */ COSTS_N_INSNS (36), /* int_div_si */ COSTS_N_INSNS (36), /* int_div_di */ 1, /* branch_cost */ 4 /* memory_latency */ }, { /* R5400 */ COSTS_N_INSNS (6), /* fp_add */ COSTS_N_INSNS (5), /* fp_mult_sf */ COSTS_N_INSNS (6), /* fp_mult_df */ COSTS_N_INSNS (30), /* fp_div_sf */ COSTS_N_INSNS (59), /* fp_div_df */ COSTS_N_INSNS (3), /* int_mult_si */ COSTS_N_INSNS (4), /* int_mult_di */ COSTS_N_INSNS (42), /* int_div_si */ COSTS_N_INSNS (74), /* int_div_di */ 1, /* branch_cost */ 4 /* memory_latency */ }, { /* R5500 */ COSTS_N_INSNS (6), /* fp_add */ COSTS_N_INSNS (5), /* fp_mult_sf */ COSTS_N_INSNS (6), /* fp_mult_df */ COSTS_N_INSNS (30), /* fp_div_sf */ COSTS_N_INSNS (59), /* fp_div_df */ COSTS_N_INSNS (5), /* int_mult_si */ COSTS_N_INSNS (9), /* int_mult_di */ COSTS_N_INSNS (42), /* int_div_si */ COSTS_N_INSNS (74), /* int_div_di */ 1, /* branch_cost */ 4 /* memory_latency */ }, { /* R7000 */ /* The only costs that are changed here are integer multiplication. */ COSTS_N_INSNS (6), /* fp_add */ COSTS_N_INSNS (7), /* fp_mult_sf */ COSTS_N_INSNS (8), /* fp_mult_df */ COSTS_N_INSNS (23), /* fp_div_sf */ COSTS_N_INSNS (36), /* fp_div_df */ COSTS_N_INSNS (5), /* int_mult_si */ COSTS_N_INSNS (9), /* int_mult_di */ COSTS_N_INSNS (69), /* int_div_si */ COSTS_N_INSNS (69), /* int_div_di */ 1, /* branch_cost */ 4 /* memory_latency */ }, { /* R8000 */ DEFAULT_COSTS }, { /* R9000 */ /* The only costs that are changed here are integer multiplication. */ COSTS_N_INSNS (6), /* fp_add */ COSTS_N_INSNS (7), /* fp_mult_sf */ COSTS_N_INSNS (8), /* fp_mult_df */ COSTS_N_INSNS (23), /* fp_div_sf */ COSTS_N_INSNS (36), /* fp_div_df */ COSTS_N_INSNS (3), /* int_mult_si */ COSTS_N_INSNS (8), /* int_mult_di */ COSTS_N_INSNS (69), /* int_div_si */ COSTS_N_INSNS (69), /* int_div_di */ 1, /* branch_cost */ 4 /* memory_latency */ }, { /* SB1 */ /* These costs are the same as the SB-1A below. */ COSTS_N_INSNS (4), /* fp_add */ COSTS_N_INSNS (4), /* fp_mult_sf */ COSTS_N_INSNS (4), /* fp_mult_df */ COSTS_N_INSNS (24), /* fp_div_sf */ COSTS_N_INSNS (32), /* fp_div_df */ COSTS_N_INSNS (3), /* int_mult_si */ COSTS_N_INSNS (4), /* int_mult_di */ COSTS_N_INSNS (36), /* int_div_si */ COSTS_N_INSNS (68), /* int_div_di */ 1, /* branch_cost */ 4 /* memory_latency */ }, { /* SB1-A */ /* These costs are the same as the SB-1 above. */ COSTS_N_INSNS (4), /* fp_add */ COSTS_N_INSNS (4), /* fp_mult_sf */ COSTS_N_INSNS (4), /* fp_mult_df */ COSTS_N_INSNS (24), /* fp_div_sf */ COSTS_N_INSNS (32), /* fp_div_df */ COSTS_N_INSNS (3), /* int_mult_si */ COSTS_N_INSNS (4), /* int_mult_di */ COSTS_N_INSNS (36), /* int_div_si */ COSTS_N_INSNS (68), /* int_div_di */ 1, /* branch_cost */ 4 /* memory_latency */ }, { /* SR71000 */ DEFAULT_COSTS }, }; /* Nonzero if -march should decide the default value of MASK_SOFT_FLOAT. */ #ifndef MIPS_MARCH_CONTROLS_SOFT_FLOAT #define MIPS_MARCH_CONTROLS_SOFT_FLOAT 0 #endif /* Initialize the GCC target structure. */ #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_ALIGNED_DI_OP #define TARGET_ASM_ALIGNED_DI_OP "\t.dword\t" #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_ASM_FUNCTION_RODATA_SECTION #define TARGET_ASM_FUNCTION_RODATA_SECTION mips_function_rodata_section #undef TARGET_SCHED_REORDER #define TARGET_SCHED_REORDER mips_sched_reorder #undef TARGET_SCHED_VARIABLE_ISSUE #define TARGET_SCHED_VARIABLE_ISSUE mips_variable_issue #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_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD #define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \ mips_multipass_dfa_lookahead #undef TARGET_DEFAULT_TARGET_FLAGS #define TARGET_DEFAULT_TARGET_FLAGS \ (TARGET_DEFAULT \ | TARGET_CPU_DEFAULT \ | TARGET_ENDIAN_DEFAULT \ | TARGET_FP_EXCEPTIONS_DEFAULT \ | MASK_CHECK_ZERO_DIV \ | MASK_FUSED_MADD) #undef TARGET_HANDLE_OPTION #define TARGET_HANDLE_OPTION mips_handle_option #undef TARGET_FUNCTION_OK_FOR_SIBCALL #define TARGET_FUNCTION_OK_FOR_SIBCALL mips_function_ok_for_sibcall #undef TARGET_VALID_POINTER_MODE #define TARGET_VALID_POINTER_MODE mips_valid_pointer_mode #undef TARGET_RTX_COSTS #define TARGET_RTX_COSTS mips_rtx_costs #undef TARGET_ADDRESS_COST #define TARGET_ADDRESS_COST mips_address_cost #undef TARGET_IN_SMALL_DATA_P #define TARGET_IN_SMALL_DATA_P mips_in_small_data_p #undef TARGET_MACHINE_DEPENDENT_REORG #define TARGET_MACHINE_DEPENDENT_REORG mips_reorg #undef TARGET_ASM_FILE_START #undef TARGET_ASM_FILE_END #define TARGET_ASM_FILE_START mips_file_start #define TARGET_ASM_FILE_END mips_file_end #undef TARGET_ASM_FILE_START_FILE_DIRECTIVE #define TARGET_ASM_FILE_START_FILE_DIRECTIVE true #undef TARGET_INIT_LIBFUNCS #define TARGET_INIT_LIBFUNCS mips_init_libfuncs #undef TARGET_BUILD_BUILTIN_VA_LIST #define TARGET_BUILD_BUILTIN_VA_LIST mips_build_builtin_va_list #undef TARGET_GIMPLIFY_VA_ARG_EXPR #define TARGET_GIMPLIFY_VA_ARG_EXPR mips_gimplify_va_arg_expr #undef TARGET_PROMOTE_FUNCTION_ARGS #define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true #undef TARGET_PROMOTE_FUNCTION_RETURN #define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true #undef TARGET_PROMOTE_PROTOTYPES #define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true #undef TARGET_RETURN_IN_MEMORY #define TARGET_RETURN_IN_MEMORY mips_return_in_memory #undef TARGET_RETURN_IN_MSB #define TARGET_RETURN_IN_MSB mips_return_in_msb #undef TARGET_ASM_OUTPUT_MI_THUNK #define TARGET_ASM_OUTPUT_MI_THUNK mips_output_mi_thunk #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK #define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true #undef TARGET_SETUP_INCOMING_VARARGS #define TARGET_SETUP_INCOMING_VARARGS mips_setup_incoming_varargs #undef TARGET_STRICT_ARGUMENT_NAMING #define TARGET_STRICT_ARGUMENT_NAMING mips_strict_argument_naming #undef TARGET_MUST_PASS_IN_STACK #define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size #undef TARGET_PASS_BY_REFERENCE #define TARGET_PASS_BY_REFERENCE mips_pass_by_reference #undef TARGET_CALLEE_COPIES #define TARGET_CALLEE_COPIES mips_callee_copies #undef TARGET_ARG_PARTIAL_BYTES #define TARGET_ARG_PARTIAL_BYTES mips_arg_partial_bytes #undef TARGET_MODE_REP_EXTENDED #define TARGET_MODE_REP_EXTENDED mips_mode_rep_extended #undef TARGET_VECTOR_MODE_SUPPORTED_P #define TARGET_VECTOR_MODE_SUPPORTED_P mips_vector_mode_supported_p #undef TARGET_INIT_BUILTINS #define TARGET_INIT_BUILTINS mips_init_builtins #undef TARGET_EXPAND_BUILTIN #define TARGET_EXPAND_BUILTIN mips_expand_builtin #undef TARGET_HAVE_TLS #define TARGET_HAVE_TLS HAVE_AS_TLS #undef TARGET_CANNOT_FORCE_CONST_MEM #define TARGET_CANNOT_FORCE_CONST_MEM mips_cannot_force_const_mem #undef TARGET_ENCODE_SECTION_INFO #define TARGET_ENCODE_SECTION_INFO mips_encode_section_info #undef TARGET_ATTRIBUTE_TABLE #define TARGET_ATTRIBUTE_TABLE mips_attribute_table #undef TARGET_EXTRA_LIVE_ON_ENTRY #define TARGET_EXTRA_LIVE_ON_ENTRY mips_extra_live_on_entry #undef TARGET_MIN_ANCHOR_OFFSET #define TARGET_MIN_ANCHOR_OFFSET -32768 #undef TARGET_MAX_ANCHOR_OFFSET #define TARGET_MAX_ANCHOR_OFFSET 32767 #undef TARGET_USE_BLOCKS_FOR_CONSTANT_P #define TARGET_USE_BLOCKS_FOR_CONSTANT_P mips_use_blocks_for_constant_p #undef TARGET_USE_ANCHORS_FOR_SYMBOL_P #define TARGET_USE_ANCHORS_FOR_SYMBOL_P mips_use_anchors_for_symbol_p struct gcc_target targetm = TARGET_INITIALIZER; /* Classify symbol X, which must be a SYMBOL_REF or a LABEL_REF. */ static enum mips_symbol_type mips_classify_symbol (rtx x) { if (GET_CODE (x) == LABEL_REF) { if (TARGET_MIPS16) return SYMBOL_CONSTANT_POOL; if (TARGET_ABICALLS && !TARGET_ABSOLUTE_ABICALLS) return SYMBOL_GOT_LOCAL; return SYMBOL_GENERAL; } gcc_assert (GET_CODE (x) == SYMBOL_REF); if (SYMBOL_REF_TLS_MODEL (x)) return SYMBOL_TLS; if (CONSTANT_POOL_ADDRESS_P (x)) { if (TARGET_MIPS16) return SYMBOL_CONSTANT_POOL; if (GET_MODE_SIZE (get_pool_mode (x)) <= mips_section_threshold) return SYMBOL_SMALL_DATA; } /* Do not use small-data accesses for weak symbols; they may end up being zero. */ if (SYMBOL_REF_SMALL_P (x) && !SYMBOL_REF_WEAK (x)) return SYMBOL_SMALL_DATA; if (TARGET_ABICALLS) { if (SYMBOL_REF_DECL (x) == 0) { if (!SYMBOL_REF_LOCAL_P (x)) return SYMBOL_GOT_GLOBAL; } else { /* Don't use GOT accesses for locally-binding symbols if TARGET_ABSOLUTE_ABICALLS. Otherwise, there are three cases to consider: - o32 PIC (either with or without explicit relocs) - n32/n64 PIC without explicit relocs - n32/n64 PIC with explicit relocs In the first case, both local and global accesses will use an R_MIPS_GOT16 relocation. We must correctly predict which of the two semantics (local or global) the assembler and linker will apply. The choice doesn't depend on the symbol's visibility, so we deliberately ignore decl_visibility and binds_local_p here. In the second case, the assembler will not use R_MIPS_GOT16 relocations, but it chooses between local and global accesses in the same way as for o32 PIC. In the third case we have more freedom since both forms of access will work for any kind of symbol. However, there seems little point in doing things differently. */ if (DECL_P (SYMBOL_REF_DECL (x)) && TREE_PUBLIC (SYMBOL_REF_DECL (x)) && !(TARGET_ABSOLUTE_ABICALLS && targetm.binds_local_p (SYMBOL_REF_DECL (x)))) return SYMBOL_GOT_GLOBAL; } if (!TARGET_ABSOLUTE_ABICALLS) return SYMBOL_GOT_LOCAL; } return SYMBOL_GENERAL; } /* Split X into a base and a constant offset, storing them in *BASE and *OFFSET respectively. */ static void mips_split_const (rtx x, rtx *base, HOST_WIDE_INT *offset) { *offset = 0; if (GET_CODE (x) == CONST) { x = XEXP (x, 0); if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT) { *offset += INTVAL (XEXP (x, 1)); x = XEXP (x, 0); } } *base = x; } /* Classify symbolic expression X, given that it appears in context CONTEXT. */ static enum mips_symbol_type mips_classify_symbolic_expression (rtx x) { HOST_WIDE_INT offset; mips_split_const (x, &x, &offset); if (UNSPEC_ADDRESS_P (x)) return UNSPEC_ADDRESS_TYPE (x); return mips_classify_symbol (x); } /* Return true if SYMBOL is a SYMBOL_REF and OFFSET + SYMBOL points to the same object as SYMBOL, or to the same object_block. */ static bool mips_offset_within_object_p (rtx symbol, HOST_WIDE_INT offset) { if (GET_CODE (symbol) != SYMBOL_REF) return false; if (CONSTANT_POOL_ADDRESS_P (symbol) && offset >= 0 && offset < (int) GET_MODE_SIZE (get_pool_mode (symbol))) return true; if (SYMBOL_REF_DECL (symbol) != 0 && offset >= 0 && offset < int_size_in_bytes (TREE_TYPE (SYMBOL_REF_DECL (symbol)))) return true; if (SYMBOL_REF_HAS_BLOCK_INFO_P (symbol) && SYMBOL_REF_BLOCK (symbol) && SYMBOL_REF_BLOCK_OFFSET (symbol) >= 0 && ((unsigned HOST_WIDE_INT) offset + SYMBOL_REF_BLOCK_OFFSET (symbol) < (unsigned HOST_WIDE_INT) SYMBOL_REF_BLOCK (symbol)->size)) return true; return false; } /* Return true if X is a symbolic constant that can be calculated in the same way as a bare symbol. If it is, store the type of the symbol in *SYMBOL_TYPE. */ bool mips_symbolic_constant_p (rtx x, enum mips_symbol_type *symbol_type) { HOST_WIDE_INT offset; mips_split_const (x, &x, &offset); if (UNSPEC_ADDRESS_P (x)) *symbol_type = UNSPEC_ADDRESS_TYPE (x); else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF) { *symbol_type = mips_classify_symbol (x); if (*symbol_type == SYMBOL_TLS) return false; } else return false; if (offset == 0) return true; /* Check whether a nonzero offset is valid for the underlying relocations. */ switch (*symbol_type) { case SYMBOL_GENERAL: case SYMBOL_64_HIGH: case SYMBOL_64_MID: case SYMBOL_64_LOW: /* If the target has 64-bit pointers and the object file only supports 32-bit symbols, the values of those symbols will be sign-extended. In this case we can't allow an arbitrary offset in case the 32-bit value X + OFFSET has a different sign from X. */ if (Pmode == DImode && !ABI_HAS_64BIT_SYMBOLS) return mips_offset_within_object_p (x, offset); /* In other cases the relocations can handle any offset. */ return true; case SYMBOL_CONSTANT_POOL: /* Allow constant pool references to be converted to LABEL+CONSTANT. In this case, we no longer have access to the underlying constant, but the original symbol-based access was known to be valid. */ if (GET_CODE (x) == LABEL_REF) return true; /* Fall through. */ case SYMBOL_SMALL_DATA: /* Make sure that the offset refers to something within the underlying object. This should guarantee that the final PC- or GP-relative offset is within the 16-bit limit. */ return mips_offset_within_object_p (x, offset); case SYMBOL_GOT_LOCAL: case SYMBOL_GOTOFF_PAGE: /* The linker should provide enough local GOT entries for a 16-bit offset. Larger offsets may lead to GOT overflow. */ return SMALL_OPERAND (offset); case SYMBOL_GOT_GLOBAL: case SYMBOL_GOTOFF_GLOBAL: case SYMBOL_GOTOFF_CALL: case SYMBOL_GOTOFF_LOADGP: case SYMBOL_TLSGD: case SYMBOL_TLSLDM: case SYMBOL_DTPREL: case SYMBOL_TPREL: case SYMBOL_GOTTPREL: case SYMBOL_TLS: return false; } gcc_unreachable (); } /* This function is used to implement REG_MODE_OK_FOR_BASE_P. */ int mips_regno_mode_ok_for_base_p (int regno, enum machine_mode mode, int strict) { if (regno >= FIRST_PSEUDO_REGISTER) { if (!strict) return true; regno = reg_renumber[regno]; } /* These fake registers will be eliminated to either the stack or hard frame pointer, both of which are usually valid base registers. Reload deals with the cases where the eliminated form isn't valid. */ if (regno == ARG_POINTER_REGNUM || regno == FRAME_POINTER_REGNUM) return true; /* In mips16 mode, the stack pointer can only address word and doubleword values, nothing smaller. There are two problems here: (a) Instantiating virtual registers can introduce new uses of the stack pointer. If these virtual registers are valid addresses, the stack pointer should be too. (b) Most uses of the stack pointer are not made explicit until FRAME_POINTER_REGNUM and ARG_POINTER_REGNUM have been eliminated. We don't know until that stage whether we'll be eliminating to the stack pointer (which needs the restriction) or the hard frame pointer (which doesn't). All in all, it seems more consistent to only enforce this restriction during and after reload. */ if (TARGET_MIPS16 && regno == STACK_POINTER_REGNUM) return !strict || GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8; return TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno); } /* Return true if X is a valid base register for the given mode. Allow only hard registers if STRICT. */ static bool mips_valid_base_register_p (rtx x, enum machine_mode mode, int strict) { if (!strict && GET_CODE (x) == SUBREG) x = SUBREG_REG (x); return (REG_P (x) && mips_regno_mode_ok_for_base_p (REGNO (x), mode, strict)); } /* Return true if symbols of type SYMBOL_TYPE can directly address a value with mode MODE. This is used for both symbolic and LO_SUM addresses. */ static bool mips_symbolic_address_p (enum mips_symbol_type symbol_type, enum machine_mode mode) { switch (symbol_type) { case SYMBOL_GENERAL: return !TARGET_MIPS16; case SYMBOL_SMALL_DATA: return true; case SYMBOL_CONSTANT_POOL: /* PC-relative addressing is only available for lw and ld. */ return GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8; case SYMBOL_GOT_LOCAL: return true; case SYMBOL_GOT_GLOBAL: /* The address will have to be loaded from the GOT first. */ return false; case SYMBOL_GOTOFF_PAGE: case SYMBOL_GOTOFF_GLOBAL: case SYMBOL_GOTOFF_CALL: case SYMBOL_GOTOFF_LOADGP: case SYMBOL_TLS: case SYMBOL_TLSGD: case SYMBOL_TLSLDM: case SYMBOL_DTPREL: case SYMBOL_GOTTPREL: case SYMBOL_TPREL: case SYMBOL_64_HIGH: case SYMBOL_64_MID: case SYMBOL_64_LOW: return true; } gcc_unreachable (); } /* Return true if X is a valid address for machine mode MODE. If it is, fill in INFO appropriately. STRICT is true if we should only accept hard base registers. */ static bool mips_classify_address (struct mips_address_info *info, rtx x, enum machine_mode mode, int strict) { switch (GET_CODE (x)) { case REG: case SUBREG: info->type = ADDRESS_REG; info->reg = x; info->offset = const0_rtx; return mips_valid_base_register_p (info->reg, mode, strict); case PLUS: info->type = ADDRESS_REG; info->reg = XEXP (x, 0); info->offset = XEXP (x, 1); return (mips_valid_base_register_p (info->reg, mode, strict) && const_arith_operand (info->offset, VOIDmode)); case LO_SUM: info->type = ADDRESS_LO_SUM; info->reg = XEXP (x, 0); info->offset = XEXP (x, 1); /* We have to trust the creator of the LO_SUM to do something vaguely sane. Target-independent code that creates a LO_SUM should also create and verify the matching HIGH. Target-independent code that adds an offset to a LO_SUM must prove that the offset will not induce a carry. Failure to do either of these things would be a bug, and we are not required to check for it here. The MIPS backend itself should only create LO_SUMs for valid symbolic constants, with the high part being either a HIGH or a copy of _gp. */ info->symbol_type = mips_classify_symbolic_expression (info->offset); return (mips_valid_base_register_p (info->reg, mode, strict) && mips_symbolic_address_p (info->symbol_type, mode) && mips_lo_relocs[info->symbol_type] != 0); case CONST_INT: /* Small-integer addresses don't occur very often, but they are legitimate if $0 is a valid base register. */ info->type = ADDRESS_CONST_INT; return !TARGET_MIPS16 && SMALL_INT (x); case CONST: case LABEL_REF: case SYMBOL_REF: info->type = ADDRESS_SYMBOLIC; return (mips_symbolic_constant_p (x, &info->symbol_type) && mips_symbolic_address_p (info->symbol_type, mode) && !mips_split_p[info->symbol_type]); default: return false; } } /* Return true if X is a thread-local symbol. */ static bool mips_tls_operand_p (rtx x) { return GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x) != 0; } /* Return true if X can not be forced into a constant pool. */ static int mips_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED) { return mips_tls_operand_p (*x); } /* Return true if X can not be forced into a constant pool. */ static bool mips_cannot_force_const_mem (rtx x) { rtx base; HOST_WIDE_INT offset; if (!TARGET_MIPS16) { /* As an optimization, reject constants that mips_legitimize_move can expand inline. Suppose we have a multi-instruction sequence that loads constant C into register R. If R does not get allocated a hard register, and R is used in an operand that allows both registers and memory references, reload will consider forcing C into memory and using one of the instruction's memory alternatives. Returning false here will force it to use an input reload instead. */ if (GET_CODE (x) == CONST_INT) return true; mips_split_const (x, &base, &offset); if (symbolic_operand (base, VOIDmode) && SMALL_OPERAND (offset)) return true; } if (TARGET_HAVE_TLS && for_each_rtx (&x, &mips_tls_symbol_ref_1, 0)) return true; return false; } /* Implement TARGET_USE_BLOCKS_FOR_CONSTANT_P. MIPS16 uses per-function constant pools, but normal-mode code doesn't need to. */ static bool mips_use_blocks_for_constant_p (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x ATTRIBUTE_UNUSED) { return !TARGET_MIPS16; } /* Return the number of instructions needed to load a symbol of the given type into a register. If valid in an address, the same number of instructions are needed for loads and stores. Treat extended mips16 instructions as two instructions. */ static int mips_symbol_insns (enum mips_symbol_type type) { switch (type) { case SYMBOL_GENERAL: /* In mips16 code, general symbols must be fetched from the constant pool. */ if (TARGET_MIPS16) return 0; /* When using 64-bit symbols, we need 5 preparatory instructions, such as: lui $at,%highest(symbol) daddiu $at,$at,%higher(symbol) dsll $at,$at,16 daddiu $at,$at,%hi(symbol) dsll $at,$at,16 The final address is then $at + %lo(symbol). With 32-bit symbols we just need a preparatory lui. */ return (ABI_HAS_64BIT_SYMBOLS ? 6 : 2); case SYMBOL_SMALL_DATA: return 1; case SYMBOL_CONSTANT_POOL: /* This case is for mips16 only. Assume we'll need an extended instruction. */ return 2; case SYMBOL_GOT_LOCAL: case SYMBOL_GOT_GLOBAL: /* Unless -funit-at-a-time is in effect, we can't be sure whether the local/global classification is accurate. See override_options for details. The worst cases are: (1) For local symbols when generating o32 or o64 code. The assembler will use: lw $at,%got(symbol) nop ...and the final address will be $at + %lo(symbol). (2) For global symbols when -mxgot. The assembler will use: lui $at,%got_hi(symbol) (d)addu $at,$at,$gp ...and the final address will be $at + %got_lo(symbol). */ return 3; case SYMBOL_GOTOFF_PAGE: case SYMBOL_GOTOFF_GLOBAL: case SYMBOL_GOTOFF_CALL: case SYMBOL_GOTOFF_LOADGP: case SYMBOL_64_HIGH: case SYMBOL_64_MID: case SYMBOL_64_LOW: case SYMBOL_TLSGD: case SYMBOL_TLSLDM: case SYMBOL_DTPREL: case SYMBOL_GOTTPREL: case SYMBOL_TPREL: /* Check whether the offset is a 16- or 32-bit value. */ return mips_split_p[type] ? 2 : 1; case SYMBOL_TLS: /* We don't treat a bare TLS symbol as a constant. */ return 0; } gcc_unreachable (); } /* Return true if X is a legitimate $sp-based address for mode MDOE. */ bool mips_stack_address_p (rtx x, enum machine_mode mode) { struct mips_address_info addr; return (mips_classify_address (&addr, x, mode, false) && addr.type == ADDRESS_REG && addr.reg == stack_pointer_rtx); } /* Return true if a value at OFFSET bytes from BASE can be accessed using an unextended mips16 instruction. MODE is the mode of the value. Usually the offset in an unextended instruction is a 5-bit field. The offset is unsigned and shifted left once for HIs, twice for SIs, and so on. An exception is SImode accesses off the stack pointer, which have an 8-bit immediate field. */ static bool mips16_unextended_reference_p (enum machine_mode mode, rtx base, rtx offset) { if (TARGET_MIPS16 && GET_CODE (offset) == CONST_INT && INTVAL (offset) >= 0 && (INTVAL (offset) & (GET_MODE_SIZE (mode) - 1)) == 0) { if (GET_MODE_SIZE (mode) == 4 && base == stack_pointer_rtx) return INTVAL (offset) < 256 * GET_MODE_SIZE (mode); return INTVAL (offset) < 32 * GET_MODE_SIZE (mode); } return false; } /* Return the number of instructions needed to load or store a value of mode MODE at X. Return 0 if X isn't valid for MODE. For mips16 code, count extended instructions as two instructions. */ int mips_address_insns (rtx x, enum machine_mode mode) { struct mips_address_info addr; int factor; if (mode == BLKmode) /* BLKmode is used for single unaligned loads and stores. */ factor = 1; else /* Each word of a multi-word value will be accessed individually. */ factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; if (mips_classify_address (&addr, x, mode, false)) switch (addr.type) { case ADDRESS_REG: if (TARGET_MIPS16 && !mips16_unextended_reference_p (mode, addr.reg, addr.offset)) return factor * 2; return factor; case ADDRESS_LO_SUM: return (TARGET_MIPS16 ? factor * 2 : factor); case ADDRESS_CONST_INT: return factor; case ADDRESS_SYMBOLIC: return factor * mips_symbol_insns (addr.symbol_type); } return 0; } /* Likewise for constant X. */ int mips_const_insns (rtx x) { struct mips_integer_op codes[MIPS_MAX_INTEGER_OPS]; enum mips_symbol_type symbol_type; HOST_WIDE_INT offset; switch (GET_CODE (x)) { case HIGH: if (TARGET_MIPS16 || !mips_symbolic_constant_p (XEXP (x, 0), &symbol_type) || !mips_split_p[symbol_type]) return 0; return 1; case CONST_INT: if (TARGET_MIPS16) /* Unsigned 8-bit constants can be loaded using an unextended LI instruction. Unsigned 16-bit constants can be loaded using an extended LI. Negative constants must be loaded using LI and then negated. */ return (INTVAL (x) >= 0 && INTVAL (x) < 256 ? 1 : SMALL_OPERAND_UNSIGNED (INTVAL (x)) ? 2 : INTVAL (x) > -256 && INTVAL (x) < 0 ? 2 : SMALL_OPERAND_UNSIGNED (-INTVAL (x)) ? 3 : 0); return mips_build_integer (codes, INTVAL (x)); case CONST_DOUBLE: case CONST_VECTOR: return (!TARGET_MIPS16 && x == CONST0_RTX (GET_MODE (x)) ? 1 : 0); case CONST: if (CONST_GP_P (x)) return 1; /* See if we can refer to X directly. */ if (mips_symbolic_constant_p (x, &symbol_type)) return mips_symbol_insns (symbol_type); /* Otherwise try splitting the constant into a base and offset. 16-bit offsets can be added using an extra addiu. Larger offsets must be calculated separately and then added to the base. */ mips_split_const (x, &x, &offset); if (offset != 0) { int n = mips_const_insns (x); if (n != 0) { if (SMALL_OPERAND (offset)) return n + 1; else return n + 1 + mips_build_integer (codes, offset); } } return 0; case SYMBOL_REF: case LABEL_REF: return mips_symbol_insns (mips_classify_symbol (x)); default: return 0; } } /* Return the number of instructions needed for memory reference X. Count extended mips16 instructions as two instructions. */ int mips_fetch_insns (rtx x) { gcc_assert (MEM_P (x)); return mips_address_insns (XEXP (x, 0), GET_MODE (x)); } /* Return the number of instructions needed for an integer division. */ int mips_idiv_insns (void) { int count; count = 1; if (TARGET_CHECK_ZERO_DIV) { if (GENERATE_DIVIDE_TRAPS) count++; else count += 2; } if (TARGET_FIX_R4000 || TARGET_FIX_R4400) count++; return count; } /* This function is used to implement GO_IF_LEGITIMATE_ADDRESS. It returns a nonzero value if X is a legitimate address for a memory operand of the indicated MODE. STRICT is nonzero if this function is called during reload. */ bool mips_legitimate_address_p (enum machine_mode mode, rtx x, int strict) { struct mips_address_info addr; return mips_classify_address (&addr, x, mode, strict); } /* Copy VALUE to a register and return that register. If new psuedos are allowed, copy it into a new register, otherwise use DEST. */ static rtx mips_force_temporary (rtx dest, rtx value) { if (!no_new_pseudos) return force_reg (Pmode, value); else { emit_move_insn (copy_rtx (dest), value); return dest; } } /* Return a LO_SUM expression for ADDR. TEMP is as for mips_force_temporary and is used to load the high part into a register. */ rtx mips_split_symbol (rtx temp, rtx addr) { rtx high; if (TARGET_MIPS16) high = mips16_gp_pseudo_reg (); else high = mips_force_temporary (temp, gen_rtx_HIGH (Pmode, copy_rtx (addr))); return gen_rtx_LO_SUM (Pmode, high, addr); } /* Return an UNSPEC address with underlying address ADDRESS and symbol type SYMBOL_TYPE. */ rtx mips_unspec_address (rtx address, enum mips_symbol_type symbol_type) { rtx base; HOST_WIDE_INT offset; mips_split_const (address, &base, &offset); base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base), UNSPEC_ADDRESS_FIRST + symbol_type); return plus_constant (gen_rtx_CONST (Pmode, base), offset); } /* If mips_unspec_address (ADDR, SYMBOL_TYPE) is a 32-bit value, add the high part to BASE and return the result. Just return BASE otherwise. TEMP is available as a temporary register if needed. The returned expression can be used as the first operand to a LO_SUM. */ static rtx mips_unspec_offset_high (rtx temp, rtx base, rtx addr, enum mips_symbol_type symbol_type) { if (mips_split_p[symbol_type]) { addr = gen_rtx_HIGH (Pmode, mips_unspec_address (addr, symbol_type)); addr = mips_force_temporary (temp, addr); return mips_force_temporary (temp, gen_rtx_PLUS (Pmode, addr, base)); } return base; } /* Return a legitimate address for REG + OFFSET. TEMP is as for mips_force_temporary; it is only needed when OFFSET is not a SMALL_OPERAND. */ static rtx mips_add_offset (rtx temp, rtx reg, HOST_WIDE_INT offset) { if (!SMALL_OPERAND (offset)) { rtx high; if (TARGET_MIPS16) { /* Load the full offset into a register so that we can use an unextended instruction for the address itself. */ high = GEN_INT (offset); offset = 0; } else { /* Leave OFFSET as a 16-bit offset and put the excess in HIGH. */ high = GEN_INT (CONST_HIGH_PART (offset)); offset = CONST_LOW_PART (offset); } high = mips_force_temporary (temp, high); reg = mips_force_temporary (temp, gen_rtx_PLUS (Pmode, high, reg)); } return plus_constant (reg, offset); } /* Emit a call to __tls_get_addr. SYM is the TLS symbol we are referencing, and TYPE is the symbol type to use (either global dynamic or local dynamic). V0 is an RTX for the return value location. The entire insn sequence is returned. */ static GTY(()) rtx mips_tls_symbol; static rtx mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0) { rtx insn, loc, tga, a0; a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST); if (!mips_tls_symbol) mips_tls_symbol = init_one_libfunc ("__tls_get_addr"); loc = mips_unspec_address (sym, type); start_sequence (); emit_insn (gen_rtx_SET (Pmode, a0, gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, loc))); tga = gen_rtx_MEM (Pmode, mips_tls_symbol); insn = emit_call_insn (gen_call_value (v0, tga, const0_rtx, const0_rtx)); CONST_OR_PURE_CALL_P (insn) = 1; use_reg (&CALL_INSN_FUNCTION_USAGE (insn), v0); use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0); insn = get_insns (); end_sequence (); return insn; } /* Generate the code to access LOC, a thread local SYMBOL_REF. The return value will be a valid address and move_operand (either a REG or a LO_SUM). */ static rtx mips_legitimize_tls_address (rtx loc) { rtx dest, insn, v0, v1, tmp1, tmp2, eqv; enum tls_model model; v0 = gen_rtx_REG (Pmode, GP_RETURN); v1 = gen_rtx_REG (Pmode, GP_RETURN + 1); model = SYMBOL_REF_TLS_MODEL (loc); /* Only TARGET_ABICALLS code can have more than one module; other code must be be static and should not use a GOT. All TLS models reduce to local exec in this situation. */ if (!TARGET_ABICALLS) model = TLS_MODEL_LOCAL_EXEC; switch (model) { case TLS_MODEL_GLOBAL_DYNAMIC: insn = mips_call_tls_get_addr (loc, SYMBOL_TLSGD, v0); dest = gen_reg_rtx (Pmode); emit_libcall_block (insn, dest, v0, loc); break; case TLS_MODEL_LOCAL_DYNAMIC: insn = mips_call_tls_get_addr (loc, SYMBOL_TLSLDM, v0); tmp1 = gen_reg_rtx (Pmode); /* Attach a unique REG_EQUIV, to allow the RTL optimizers to share the LDM result with other LD model accesses. */ eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLS_LDM); emit_libcall_block (insn, tmp1, v0, eqv); tmp2 = mips_unspec_offset_high (NULL, tmp1, loc, SYMBOL_DTPREL); dest = gen_rtx_LO_SUM (Pmode, tmp2, mips_unspec_address (loc, SYMBOL_DTPREL)); break; case TLS_MODEL_INITIAL_EXEC: tmp1 = gen_reg_rtx (Pmode); tmp2 = mips_unspec_address (loc, SYMBOL_GOTTPREL); if (Pmode == DImode) { emit_insn (gen_tls_get_tp_di (v1)); emit_insn (gen_load_gotdi (tmp1, pic_offset_table_rtx, tmp2)); } else { emit_insn (gen_tls_get_tp_si (v1)); emit_insn (gen_load_gotsi (tmp1, pic_offset_table_rtx, tmp2)); } dest = gen_reg_rtx (Pmode); emit_insn (gen_add3_insn (dest, tmp1, v1)); break; case TLS_MODEL_LOCAL_EXEC: if (Pmode == DImode) emit_insn (gen_tls_get_tp_di (v1)); else emit_insn (gen_tls_get_tp_si (v1)); tmp1 = mips_unspec_offset_high (NULL, v1, loc, SYMBOL_TPREL); dest = gen_rtx_LO_SUM (Pmode, tmp1, mips_unspec_address (loc, SYMBOL_TPREL)); break; default: gcc_unreachable (); } return dest; } /* This function is used to implement LEGITIMIZE_ADDRESS. If *XLOC can be legitimized in a way that the generic machinery might not expect, put the new address in *XLOC and return true. MODE is the mode of the memory being accessed. */ bool mips_legitimize_address (rtx *xloc, enum machine_mode mode) { enum mips_symbol_type symbol_type; if (mips_tls_operand_p (*xloc)) { *xloc = mips_legitimize_tls_address (*xloc); return true; } /* See if the address can split into a high part and a LO_SUM. */ if (mips_symbolic_constant_p (*xloc, &symbol_type) && mips_symbolic_address_p (symbol_type, mode) && mips_split_p[symbol_type]) { *xloc = mips_split_symbol (0, *xloc); return true; } if (GET_CODE (*xloc) == PLUS && GET_CODE (XEXP (*xloc, 1)) == CONST_INT) { /* Handle REG + CONSTANT using mips_add_offset. */ rtx reg; reg = XEXP (*xloc, 0); if (!mips_valid_base_register_p (reg, mode, 0)) reg = copy_to_mode_reg (Pmode, reg); *xloc = mips_add_offset (0, reg, INTVAL (XEXP (*xloc, 1))); return true; } return false; } /* Subroutine of mips_build_integer (with the same interface). Assume that the final action in the sequence should be a left shift. */ static unsigned int mips_build_shift (struct mips_integer_op *codes, HOST_WIDE_INT value) { unsigned int i, shift; /* Shift VALUE right until its lowest bit is set. Shift arithmetically since signed numbers are easier to load than unsigned ones. */ shift = 0; while ((value & 1) == 0) value /= 2, shift++; i = mips_build_integer (codes, value); codes[i].code = ASHIFT; codes[i].value = shift; return i + 1; } /* As for mips_build_shift, but assume that the final action will be an IOR or PLUS operation. */ static unsigned int mips_build_lower (struct mips_integer_op *codes, unsigned HOST_WIDE_INT value) { unsigned HOST_WIDE_INT high; unsigned int i; high = value & ~(unsigned HOST_WIDE_INT) 0xffff; if (!LUI_OPERAND (high) && (value & 0x18000) == 0x18000) { /* The constant is too complex to load with a simple lui/ori pair so our goal is to clear as many trailing zeros as possible. In this case, we know bit 16 is set and that the low 16 bits form a negative number. If we subtract that number from VALUE, we will clear at least the lowest 17 bits, maybe more. */ i = mips_build_integer (codes, CONST_HIGH_PART (value)); codes[i].code = PLUS; codes[i].value = CONST_LOW_PART (value); } else { i = mips_build_integer (codes, high); codes[i].code = IOR; codes[i].value = value & 0xffff; } return i + 1; } /* Fill CODES with a sequence of rtl operations to load VALUE. Return the number of operations needed. */ static unsigned int mips_build_integer (struct mips_integer_op *codes, unsigned HOST_WIDE_INT value) { if (SMALL_OPERAND (value) || SMALL_OPERAND_UNSIGNED (value) || LUI_OPERAND (value)) { /* The value can be loaded with a single instruction. */ codes[0].code = UNKNOWN; codes[0].value = value; return 1; } else if ((value & 1) != 0 || LUI_OPERAND (CONST_HIGH_PART (value))) { /* Either the constant is a simple LUI/ORI combination or its lowest bit is set. We don't want to shift in this case. */ return mips_build_lower (codes, value); } else if ((value & 0xffff) == 0) { /* The constant will need at least three actions. The lowest 16 bits are clear, so the final action will be a shift. */ return mips_build_shift (codes, value); } else { /* The final action could be a shift, add or inclusive OR. Rather than use a complex condition to select the best approach, try both mips_build_shift and mips_build_lower and pick the one that gives the shortest sequence. Note that this case is only used once per constant. */ struct mips_integer_op alt_codes[MIPS_MAX_INTEGER_OPS]; unsigned int cost, alt_cost; cost = mips_build_shift (codes, value); alt_cost = mips_build_lower (alt_codes, value); if (alt_cost < cost) { memcpy (codes, alt_codes, alt_cost * sizeof (codes[0])); cost = alt_cost; } return cost; } } /* Load VALUE into DEST, using TEMP as a temporary register if need be. */ void mips_move_integer (rtx dest, rtx temp, unsigned HOST_WIDE_INT value) { struct mips_integer_op codes[MIPS_MAX_INTEGER_OPS]; enum machine_mode mode; unsigned int i, cost; rtx x; mode = GET_MODE (dest); cost = mips_build_integer (codes, value); /* Apply each binary operation to X. Invariant: X is a legitimate source operand for a SET pattern. */ x = GEN_INT (codes[0].value); for (i = 1; i < cost; i++) { if (no_new_pseudos) { emit_insn (gen_rtx_SET (VOIDmode, temp, x)); x = temp; } else x = force_reg (mode, x); x = gen_rtx_fmt_ee (codes[i].code, mode, x, GEN_INT (codes[i].value)); } emit_insn (gen_rtx_SET (VOIDmode, dest, x)); } /* Subroutine of mips_legitimize_move. Move constant SRC into register DEST given that SRC satisfies immediate_operand but doesn't satisfy move_operand. */ static void mips_legitimize_const_move (enum machine_mode mode, rtx dest, rtx src) { rtx base; HOST_WIDE_INT offset; /* Split moves of big integers into smaller pieces. */ if (splittable_const_int_operand (src, mode)) { mips_move_integer (dest, dest, INTVAL (src)); return; } /* Split moves of symbolic constants into high/low pairs. */ if (splittable_symbolic_operand (src, mode)) { emit_insn (gen_rtx_SET (VOIDmode, dest, mips_split_symbol (dest, src))); return; } if (mips_tls_operand_p (src)) { emit_move_insn (dest, mips_legitimize_tls_address (src)); return; } /* If we have (const (plus symbol offset)), load the symbol first and then add in the offset. This is usually better than forcing the constant into memory, at least in non-mips16 code. */ mips_split_const (src, &base, &offset); if (!TARGET_MIPS16 && offset != 0 && (!no_new_pseudos || SMALL_OPERAND (offset))) { base = mips_force_temporary (dest, base); emit_move_insn (dest, mips_add_offset (0, base, offset)); return; } src = force_const_mem (mode, src); /* When using explicit relocs, constant pool references are sometimes not legitimate addresses. */ if (!memory_operand (src, VOIDmode)) src = replace_equiv_address (src, mips_split_symbol (dest, XEXP (src, 0))); emit_move_insn (dest, src); } /* If (set DEST SRC) is not a valid instruction, emit an equivalent sequence that is valid. */ bool mips_legitimize_move (enum machine_mode mode, rtx dest, rtx src) { if (!register_operand (dest, mode) && !reg_or_0_operand (src, mode)) { emit_move_insn (dest, force_reg (mode, src)); return true; } /* Check for individual, fully-reloaded mflo and mfhi instructions. */ if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD && REG_P (src) && MD_REG_P (REGNO (src)) && REG_P (dest) && GP_REG_P (REGNO (dest))) { int other_regno = REGNO (src) == HI_REGNUM ? LO_REGNUM : HI_REGNUM; if (GET_MODE_SIZE (mode) <= 4) emit_insn (gen_mfhilo_si (gen_rtx_REG (SImode, REGNO (dest)), gen_rtx_REG (SImode, REGNO (src)), gen_rtx_REG (SImode, other_regno))); else emit_insn (gen_mfhilo_di (gen_rtx_REG (DImode, REGNO (dest)), gen_rtx_REG (DImode, REGNO (src)), gen_rtx_REG (DImode, other_regno))); return true; } /* We need to deal with constants that would be legitimate immediate_operands but not legitimate move_operands. */ if (CONSTANT_P (src) && !move_operand (src, mode)) { mips_legitimize_const_move (mode, dest, src); set_unique_reg_note (get_last_insn (), REG_EQUAL, copy_rtx (src)); return true; } return false; } /* We need a lot of little routines to check constant values on the mips16. These are used to figure out how long the instruction will be. It would be much better to do this using constraints, but there aren't nearly enough letters available. */ static int m16_check_op (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 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return m16_check_op (op, 0x1, 0x8, 0); } int m16_simm4_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return m16_check_op (op, - 0x8, 0x7, 0); } int m16_nsimm4_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return m16_check_op (op, - 0x7, 0x8, 0); } int m16_simm5_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return m16_check_op (op, - 0x10, 0xf, 0); } int m16_nsimm5_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return m16_check_op (op, - 0xf, 0x10, 0); } int m16_uimm5_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return m16_check_op (op, (- 0x10) << 2, 0xf << 2, 3); } int m16_nuimm5_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return m16_check_op (op, (- 0xf) << 2, 0x10 << 2, 3); } int m16_simm8_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return m16_check_op (op, - 0x80, 0x7f, 0); } int m16_nsimm8_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return m16_check_op (op, - 0x7f, 0x80, 0); } int m16_uimm8_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return m16_check_op (op, 0x0, 0xff, 0); } int m16_nuimm8_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return m16_check_op (op, - 0xff, 0x0, 0); } int m16_uimm8_m1_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return m16_check_op (op, - 0x1, 0xfe, 0); } int m16_uimm8_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return m16_check_op (op, 0x0, 0xff << 2, 3); } int m16_nuimm8_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return m16_check_op (op, (- 0xff) << 2, 0x0, 3); } int m16_simm8_8 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return m16_check_op (op, (- 0x80) << 3, 0x7f << 3, 7); } int m16_nsimm8_8 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return m16_check_op (op, (- 0x7f) << 3, 0x80 << 3, 7); } static bool mips_rtx_costs (rtx x, int code, int outer_code, int *total) { enum machine_mode mode = GET_MODE (x); bool float_mode_p = FLOAT_MODE_P (mode); switch (code) { case CONST_INT: if (TARGET_MIPS16) { /* A number between 1 and 8 inclusive is efficient for a shift. Otherwise, we will need an extended instruction. */ if ((outer_code) == ASHIFT || (outer_code) == ASHIFTRT || (outer_code) == LSHIFTRT) { if (INTVAL (x) >= 1 && INTVAL (x) <= 8) *total = 0; else *total = COSTS_N_INSNS (1); return true; } /* We can use cmpi for an xor with an unsigned 16 bit value. */ if ((outer_code) == XOR && INTVAL (x) >= 0 && INTVAL (x) < 0x10000) { *total = 0; return true; } /* We may be able to use slt or sltu for a comparison with a signed 16 bit value. (The boundary conditions aren't quite right, but this is just a heuristic anyhow.) */ if (((outer_code) == LT || (outer_code) == LE || (outer_code) == GE || (outer_code) == GT || (outer_code) == LTU || (outer_code) == LEU || (outer_code) == GEU || (outer_code) == GTU) && INTVAL (x) >= -0x8000 && INTVAL (x) < 0x8000) { *total = 0; return true; } /* Equality comparisons with 0 are cheap. */ if (((outer_code) == EQ || (outer_code) == NE) && INTVAL (x) == 0) { *total = 0; return true; } /* Constants in the range 0...255 can be loaded with an unextended instruction. They are therefore as cheap as a register move. Given the choice between "li R1,0...255" and "move R1,R2" (where R2 is a known constant), it is usually better to use "li", since we do not want to unnecessarily extend the lifetime of R2. */ if (outer_code == SET && INTVAL (x) >= 0 && INTVAL (x) < 256) { *total = 0; return true; } } else { /* These can be used anywhere. */ *total = 0; return true; } /* Otherwise fall through to the handling below because we'll need to construct the constant. */ case CONST: case SYMBOL_REF: case LABEL_REF: case CONST_DOUBLE: if (LEGITIMATE_CONSTANT_P (x)) { *total = COSTS_N_INSNS (1); return true; } else { /* The value will need to be fetched from the constant pool. */ *total = CONSTANT_POOL_COST; return true; } case MEM: { /* If the address is legitimate, return the number of instructions it needs, otherwise use the default handling. */ int n = mips_address_insns (XEXP (x, 0), GET_MODE (x)); if (n > 0) { *total = COSTS_N_INSNS (n + 1); return true; } return false; } case FFS: *total = COSTS_N_INSNS (6); return true; case NOT: *total = COSTS_N_INSNS ((mode == DImode && !TARGET_64BIT) ? 2 : 1); return true; case AND: case IOR: case XOR: if (mode == DImode && !TARGET_64BIT) { *total = COSTS_N_INSNS (2); return true; } return false; case ASHIFT: case ASHIFTRT: case LSHIFTRT: if (mode == DImode && !TARGET_64BIT) { *total = COSTS_N_INSNS ((GET_CODE (XEXP (x, 1)) == CONST_INT) ? 4 : 12); return true; } return false; case ABS: if (float_mode_p) *total = COSTS_N_INSNS (1); else *total = COSTS_N_INSNS (4); return true; case LO_SUM: *total = COSTS_N_INSNS (1); return true; case PLUS: case MINUS: if (float_mode_p) { *total = mips_cost->fp_add; return true; } else if (mode == DImode && !TARGET_64BIT) { *total = COSTS_N_INSNS (4); return true; } return false; case NEG: if (mode == DImode && !TARGET_64BIT) { *total = COSTS_N_INSNS (4); return true; } return false; case MULT: if (mode == SFmode) *total = mips_cost->fp_mult_sf; else if (mode == DFmode) *total = mips_cost->fp_mult_df; else if (mode == SImode) *total = mips_cost->int_mult_si; else *total = mips_cost->int_mult_di; return true; case DIV: case MOD: if (float_mode_p) { if (mode == SFmode) *total = mips_cost->fp_div_sf; else *total = mips_cost->fp_div_df; return true; } /* Fall through. */ case UDIV: case UMOD: if (mode == DImode) *total = mips_cost->int_div_di; else *total = mips_cost->int_div_si; return true; case SIGN_EXTEND: /* A sign extend from SImode to DImode in 64 bit mode is often zero instructions, because the result can often be used directly by another instruction; we'll call it one. */ if (TARGET_64BIT && mode == DImode && GET_MODE (XEXP (x, 0)) == SImode) *total = COSTS_N_INSNS (1); else *total = COSTS_N_INSNS (2); return true; case ZERO_EXTEND: if (TARGET_64BIT && mode == DImode && GET_MODE (XEXP (x, 0)) == SImode) *total = COSTS_N_INSNS (2); else *total = COSTS_N_INSNS (1); return true; case FLOAT: case UNSIGNED_FLOAT: case FIX: case FLOAT_EXTEND: case FLOAT_TRUNCATE: case SQRT: *total = mips_cost->fp_add; return true; default: return false; } } /* Provide the costs of an addressing mode that contains ADDR. If ADDR is not a valid address, its cost is irrelevant. */ static int mips_address_cost (rtx addr) { return mips_address_insns (addr, SImode); } /* Return one word of double-word value OP, taking into account the fixed endianness of certain registers. HIGH_P is true to select the high part, false to select the low part. */ rtx mips_subword (rtx op, int high_p) { unsigned int byte; enum machine_mode mode; mode = GET_MODE (op); if (mode == VOIDmode) mode = DImode; if (TARGET_BIG_ENDIAN ? !high_p : high_p) byte = UNITS_PER_WORD; else byte = 0; if (REG_P (op)) { if (FP_REG_P (REGNO (op))) return gen_rtx_REG (word_mode, high_p ? REGNO (op) + 1 : REGNO (op)); if (ACC_HI_REG_P (REGNO (op))) return gen_rtx_REG (word_mode, high_p ? REGNO (op) : REGNO (op) + 1); } if (MEM_P (op)) return mips_rewrite_small_data (adjust_address (op, word_mode, byte)); return simplify_gen_subreg (word_mode, op, mode, byte); } /* Return true if a 64-bit move from SRC to DEST should be split into two. */ bool mips_split_64bit_move_p (rtx dest, rtx src) { if (TARGET_64BIT) return false; /* FP->FP moves can be done in a single instruction. */ if (FP_REG_RTX_P (src) && FP_REG_RTX_P (dest)) return false; /* Check for floating-point loads and stores. They can be done using ldc1 and sdc1 on MIPS II and above. */ if (mips_isa > 1) { if (FP_REG_RTX_P (dest) && MEM_P (src)) return false; if (FP_REG_RTX_P (src) && MEM_P (dest)) return false; } return true; } /* Split a 64-bit move from SRC to DEST assuming that mips_split_64bit_move_p holds. Moves into and out of FPRs cause some difficulty here. Such moves will always be DFmode, since paired FPRs are not allowed to store DImode values. The most natural representation would be two separate 32-bit moves, such as: (set (reg:SI $f0) (mem:SI ...)) (set (reg:SI $f1) (mem:SI ...)) However, the second insn is invalid because odd-numbered FPRs are not allowed to store independent values. Use the patterns load_df_low, load_df_high and store_df_high instead. */ void mips_split_64bit_move (rtx dest, rtx src) { if (FP_REG_RTX_P (dest)) { /* Loading an FPR from memory or from GPRs. */ emit_insn (gen_load_df_low (copy_rtx (dest), mips_subword (src, 0))); emit_insn (gen_load_df_high (dest, mips_subword (src, 1), copy_rtx (dest))); } else if (FP_REG_RTX_P (src)) { /* Storing an FPR into memory or GPRs. */ emit_move_insn (mips_subword (dest, 0), mips_subword (src, 0)); emit_insn (gen_store_df_high (mips_subword (dest, 1), src)); } else { /* The operation can be split into two normal moves. Decide in which order to do them. */ rtx low_dest; low_dest = mips_subword (dest, 0); if (REG_P (low_dest) && reg_overlap_mentioned_p (low_dest, src)) { emit_move_insn (mips_subword (dest, 1), mips_subword (src, 1)); emit_move_insn (low_dest, mips_subword (src, 0)); } else { emit_move_insn (low_dest, mips_subword (src, 0)); emit_move_insn (mips_subword (dest, 1), mips_subword (src, 1)); } } } /* Return the appropriate instructions to move SRC into DEST. Assume that SRC is operand 1 and DEST is operand 0. */ const char * mips_output_move (rtx dest, rtx src) { enum rtx_code dest_code, src_code; bool dbl_p; dest_code = GET_CODE (dest); src_code = GET_CODE (src); dbl_p = (GET_MODE_SIZE (GET_MODE (dest)) == 8); if (dbl_p && mips_split_64bit_move_p (dest, src)) return "#"; if ((src_code == REG && GP_REG_P (REGNO (src))) || (!TARGET_MIPS16 && src == CONST0_RTX (GET_MODE (dest)))) { if (dest_code == REG) { if (GP_REG_P (REGNO (dest))) return "move\t%0,%z1"; if (MD_REG_P (REGNO (dest))) return "mt%0\t%z1"; if (DSP_ACC_REG_P (REGNO (dest))) { static char retval[] = "mt__\t%z1,%q0"; retval[2] = reg_names[REGNO (dest)][4]; retval[3] = reg_names[REGNO (dest)][5]; return retval; } if (FP_REG_P (REGNO (dest))) return (dbl_p ? "dmtc1\t%z1,%0" : "mtc1\t%z1,%0"); if (ALL_COP_REG_P (REGNO (dest))) { static char retval[] = "dmtc_\t%z1,%0"; retval[4] = COPNUM_AS_CHAR_FROM_REGNUM (REGNO (dest)); return (dbl_p ? retval : retval + 1); } } if (dest_code == MEM) return (dbl_p ? "sd\t%z1,%0" : "sw\t%z1,%0"); } if (dest_code == REG && GP_REG_P (REGNO (dest))) { if (src_code == REG) { if (DSP_ACC_REG_P (REGNO (src))) { static char retval[] = "mf__\t%0,%q1"; retval[2] = reg_names[REGNO (src)][4]; retval[3] = reg_names[REGNO (src)][5]; return retval; } if (ST_REG_P (REGNO (src)) && ISA_HAS_8CC) return "lui\t%0,0x3f80\n\tmovf\t%0,%.,%1"; if (FP_REG_P (REGNO (src))) return (dbl_p ? "dmfc1\t%0,%1" : "mfc1\t%0,%1"); if (ALL_COP_REG_P (REGNO (src))) { static char retval[] = "dmfc_\t%0,%1"; retval[4] = COPNUM_AS_CHAR_FROM_REGNUM (REGNO (src)); return (dbl_p ? retval : retval + 1); } } if (src_code == MEM) return (dbl_p ? "ld\t%0,%1" : "lw\t%0,%1"); if (src_code == CONST_INT) { /* Don't use the X format, because that will give out of range numbers for 64 bit hosts and 32 bit targets. */ if (!TARGET_MIPS16) return "li\t%0,%1\t\t\t# %X1"; if (INTVAL (src) >= 0 && INTVAL (src) <= 0xffff) return "li\t%0,%1"; if (INTVAL (src) < 0 && INTVAL (src) >= -0xffff) return "#"; } if (src_code == HIGH) return "lui\t%0,%h1"; if (CONST_GP_P (src)) return "move\t%0,%1"; if (symbolic_operand (src, VOIDmode)) return (dbl_p ? "dla\t%0,%1" : "la\t%0,%1"); } if (src_code == REG && FP_REG_P (REGNO (src))) { if (dest_code == REG && FP_REG_P (REGNO (dest))) { if (GET_MODE (dest) == V2SFmode) return "mov.ps\t%0,%1"; else return (dbl_p ? "mov.d\t%0,%1" : "mov.s\t%0,%1"); } if (dest_code == MEM) return (dbl_p ? "sdc1\t%1,%0" : "swc1\t%1,%0"); } if (dest_code == REG && FP_REG_P (REGNO (dest))) { if (src_code == MEM) return (dbl_p ? "ldc1\t%0,%1" : "lwc1\t%0,%1"); } if (dest_code == REG && ALL_COP_REG_P (REGNO (dest)) && src_code == MEM) { static char retval[] = "l_c_\t%0,%1"; retval[1] = (dbl_p ? 'd' : 'w'); retval[3] = COPNUM_AS_CHAR_FROM_REGNUM (REGNO (dest)); return retval; } if (dest_code == MEM && src_code == REG && ALL_COP_REG_P (REGNO (src))) { static char retval[] = "s_c_\t%1,%0"; retval[1] = (dbl_p ? 'd' : 'w'); retval[3] = COPNUM_AS_CHAR_FROM_REGNUM (REGNO (src)); return retval; } gcc_unreachable (); } /* Restore $gp from its save slot. Valid only when using o32 or o64 abicalls. */ void mips_restore_gp (void) { rtx address, slot; gcc_assert (TARGET_ABICALLS && TARGET_OLDABI); address = mips_add_offset (pic_offset_table_rtx, frame_pointer_needed ? hard_frame_pointer_rtx : stack_pointer_rtx, current_function_outgoing_args_size); slot = gen_rtx_MEM (Pmode, address); emit_move_insn (pic_offset_table_rtx, slot); if (!TARGET_EXPLICIT_RELOCS) emit_insn (gen_blockage ()); } /* Emit an instruction of the form (set TARGET (CODE OP0 OP1)). */ static void mips_emit_binary (enum rtx_code code, rtx target, rtx op0, rtx op1) { emit_insn (gen_rtx_SET (VOIDmode, target, gen_rtx_fmt_ee (code, GET_MODE (target), op0, op1))); } /* Return true if CMP1 is a suitable second operand for relational operator CODE. See also the *sCC patterns in mips.md. */ static bool mips_relational_operand_ok_p (enum rtx_code code, rtx cmp1) { switch (code) { case GT: case GTU: return reg_or_0_operand (cmp1, VOIDmode); case GE: case GEU: return !TARGET_MIPS16 && cmp1 == const1_rtx; case LT: case LTU: return arith_operand (cmp1, VOIDmode); case LE: return sle_operand (cmp1, VOIDmode); case LEU: return sleu_operand (cmp1, VOIDmode); default: gcc_unreachable (); } } /* Canonicalize LE or LEU comparisons into LT comparisons when possible to avoid extra instructions or inverting the comparison. */ static bool mips_canonicalize_comparison (enum rtx_code *code, rtx *cmp1, enum machine_mode mode) { HOST_WIDE_INT original, plus_one; if (GET_CODE (*cmp1) != CONST_INT) return false; original = INTVAL (*cmp1); plus_one = trunc_int_for_mode ((unsigned HOST_WIDE_INT) original + 1, mode); switch (*code) { case LE: if (original < plus_one) { *code = LT; *cmp1 = force_reg (mode, GEN_INT (plus_one)); return true; } break; case LEU: if (plus_one != 0) { *code = LTU; *cmp1 = force_reg (mode, GEN_INT (plus_one)); return true; } break; default: return false; } return false; } /* Compare CMP0 and CMP1 using relational operator CODE and store the result in TARGET. CMP0 and TARGET are register_operands that have the same integer mode. If INVERT_PTR is nonnull, it's OK to set TARGET to the inverse of the result and flip *INVERT_PTR instead. */ static void mips_emit_int_relational (enum rtx_code code, bool *invert_ptr, rtx target, rtx cmp0, rtx cmp1) { /* First see if there is a MIPS instruction that can do this operation with CMP1 in its current form. If not, try to canonicalize the comparison to LT. If that fails, try doing the same for the inverse operation. If that also fails, force CMP1 into a register and try again. */ if (mips_relational_operand_ok_p (code, cmp1)) mips_emit_binary (code, target, cmp0, cmp1); else if (mips_canonicalize_comparison (&code, &cmp1, GET_MODE (target))) mips_emit_binary (code, target, cmp0, cmp1); else { enum rtx_code inv_code = reverse_condition (code); if (!mips_relational_operand_ok_p (inv_code, cmp1)) { cmp1 = force_reg (GET_MODE (cmp0), cmp1); mips_emit_int_relational (code, invert_ptr, target, cmp0, cmp1); } else if (invert_ptr == 0) { rtx inv_target = gen_reg_rtx (GET_MODE (target)); mips_emit_binary (inv_code, inv_target, cmp0, cmp1); mips_emit_binary (XOR, target, inv_target, const1_rtx); } else { *invert_ptr = !*invert_ptr; mips_emit_binary (inv_code, target, cmp0, cmp1); } } } /* Return a register that is zero iff CMP0 and CMP1 are equal. The register will have the same mode as CMP0. */ static rtx mips_zero_if_equal (rtx cmp0, rtx cmp1) { if (cmp1 == const0_rtx) return cmp0; if (uns_arith_operand (cmp1, VOIDmode)) return expand_binop (GET_MODE (cmp0), xor_optab, cmp0, cmp1, 0, 0, OPTAB_DIRECT); return expand_binop (GET_MODE (cmp0), sub_optab, cmp0, cmp1, 0, 0, OPTAB_DIRECT); } /* Convert *CODE into a code that can be used in a floating-point scc instruction (c..). Return true if the values of the condition code registers will be inverted, with 0 indicating that the condition holds. */ static bool mips_reverse_fp_cond_p (enum rtx_code *code) { switch (*code) { case NE: case LTGT: case ORDERED: *code = reverse_condition_maybe_unordered (*code); return true; default: return false; } } /* Convert a comparison into something that can be used in a branch or conditional move. cmp_operands[0] and cmp_operands[1] are the values being compared and *CODE is the code used to compare them. Update *CODE, *OP0 and *OP1 so that they describe the final comparison. If NEED_EQ_NE_P, then only EQ/NE comparisons against zero are possible, otherwise any standard branch condition can be used. The standard branch conditions are: - EQ/NE between two registers. - any comparison between a register and zero. */ static void mips_emit_compare (enum rtx_code *code, rtx *op0, rtx *op1, bool need_eq_ne_p) { if (GET_MODE_CLASS (GET_MODE (cmp_operands[0])) == MODE_INT) { if (!need_eq_ne_p && cmp_operands[1] == const0_rtx) { *op0 = cmp_operands[0]; *op1 = cmp_operands[1]; } else if (*code == EQ || *code == NE) { if (need_eq_ne_p) { *op0 = mips_zero_if_equal (cmp_operands[0], cmp_operands[1]); *op1 = const0_rtx; } else { *op0 = cmp_operands[0]; *op1 = force_reg (GET_MODE (*op0), cmp_operands[1]); } } else { /* The comparison needs a separate scc instruction. Store the result of the scc in *OP0 and compare it against zero. */ bool invert = false; *op0 = gen_reg_rtx (GET_MODE (cmp_operands[0])); *op1 = const0_rtx; mips_emit_int_relational (*code, &invert, *op0, cmp_operands[0], cmp_operands[1]); *code = (invert ? EQ : NE); } } else { enum rtx_code cmp_code; /* Floating-point tests use a separate c.cond.fmt comparison to set a condition code register. The branch or conditional move will then compare that register against zero. Set CMP_CODE to the code of the comparison instruction and *CODE to the code that the branch or move should use. */ cmp_code = *code; *code = mips_reverse_fp_cond_p (&cmp_code) ? EQ : NE; *op0 = (ISA_HAS_8CC ? gen_reg_rtx (CCmode) : gen_rtx_REG (CCmode, FPSW_REGNUM)); *op1 = const0_rtx; mips_emit_binary (cmp_code, *op0, cmp_operands[0], cmp_operands[1]); } } /* Try comparing cmp_operands[0] and cmp_operands[1] using rtl code CODE. Store the result in TARGET and return true if successful. On 64-bit targets, TARGET may be wider than cmp_operands[0]. */ bool mips_emit_scc (enum rtx_code code, rtx target) { if (GET_MODE_CLASS (GET_MODE (cmp_operands[0])) != MODE_INT) return false; target = gen_lowpart (GET_MODE (cmp_operands[0]), target); if (code == EQ || code == NE) { rtx zie = mips_zero_if_equal (cmp_operands[0], cmp_operands[1]); mips_emit_binary (code, target, zie, const0_rtx); } else mips_emit_int_relational (code, 0, target, cmp_operands[0], cmp_operands[1]); return true; } /* Emit the common code for doing conditional branches. operand[0] is the label to jump to. The comparison operands are saved away by cmp{si,di,sf,df}. */ void gen_conditional_branch (rtx *operands, enum rtx_code code) { rtx op0, op1, condition; mips_emit_compare (&code, &op0, &op1, TARGET_MIPS16); condition = gen_rtx_fmt_ee (code, VOIDmode, op0, op1); emit_jump_insn (gen_condjump (condition, operands[0])); } /* Implement: (set temp (COND:CCV2 CMP_OP0 CMP_OP1)) (set DEST (unspec [TRUE_SRC FALSE_SRC temp] UNSPEC_MOVE_TF_PS)) */ void mips_expand_vcondv2sf (rtx dest, rtx true_src, rtx false_src, enum rtx_code cond, rtx cmp_op0, rtx cmp_op1) { rtx cmp_result; bool reversed_p; reversed_p = mips_reverse_fp_cond_p (&cond); cmp_result = gen_reg_rtx (CCV2mode); emit_insn (gen_scc_ps (cmp_result, gen_rtx_fmt_ee (cond, VOIDmode, cmp_op0, cmp_op1))); if (reversed_p) emit_insn (gen_mips_cond_move_tf_ps (dest, false_src, true_src, cmp_result)); else emit_insn (gen_mips_cond_move_tf_ps (dest, true_src, false_src, cmp_result)); } /* Emit the common code for conditional moves. OPERANDS is the array of operands passed to the conditional move define_expand. */ void gen_conditional_move (rtx *operands) { enum rtx_code code; rtx op0, op1; code = GET_CODE (operands[1]); mips_emit_compare (&code, &op0, &op1, true); emit_insn (gen_rtx_SET (VOIDmode, operands[0], gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]), gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1), operands[2], operands[3]))); } /* Emit a conditional trap. OPERANDS is the array of operands passed to the conditional_trap expander. */ void mips_gen_conditional_trap (rtx *operands) { rtx op0, op1; enum rtx_code cmp_code = GET_CODE (operands[0]); enum machine_mode mode = GET_MODE (cmp_operands[0]); /* MIPS conditional trap machine instructions don't have GT or LE flavors, so we must invert the comparison and convert to LT and GE, respectively. */ 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 = cmp_operands[0]; op1 = cmp_operands[1]; } else { op0 = cmp_operands[1]; op1 = cmp_operands[0]; } op0 = force_reg (mode, op0); if (!arith_operand (op1, mode)) op1 = force_reg (mode, op1); emit_insn (gen_rtx_TRAP_IF (VOIDmode, gen_rtx_fmt_ee (cmp_code, mode, op0, op1), operands[1])); } /* Load function address ADDR into register DEST. SIBCALL_P is true if the address is needed for a sibling call. */ static void mips_load_call_address (rtx dest, rtx addr, int sibcall_p) { /* If we're generating PIC, and this call is to a global function, try to allow its address to be resolved lazily. This isn't possible for NewABI sibcalls since the value of $gp on entry to the stub would be our caller's gp, not ours. */ if (TARGET_EXPLICIT_RELOCS && !(sibcall_p && TARGET_NEWABI) && global_got_operand (addr, VOIDmode)) { rtx high, lo_sum_symbol; high = mips_unspec_offset_high (dest, pic_offset_table_rtx, addr, SYMBOL_GOTOFF_CALL); lo_sum_symbol = mips_unspec_address (addr, SYMBOL_GOTOFF_CALL); if (Pmode == SImode) emit_insn (gen_load_callsi (dest, high, lo_sum_symbol)); else emit_insn (gen_load_calldi (dest, high, lo_sum_symbol)); } else emit_move_insn (dest, addr); } /* Expand a call or call_value instruction. RESULT is where the result will go (null for calls), ADDR is the address of the function, ARGS_SIZE is the size of the arguments and AUX is the value passed to us by mips_function_arg. SIBCALL_P is true if we are expanding a sibling call, false if we're expanding a normal call. */ void mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p) { rtx orig_addr, pattern, insn; orig_addr = addr; if (!call_insn_operand (addr, VOIDmode)) { addr = gen_reg_rtx (Pmode); mips_load_call_address (addr, orig_addr, sibcall_p); } if (TARGET_MIPS16 && mips16_hard_float && build_mips16_call_stub (result, addr, args_size, aux == 0 ? 0 : (int) GET_MODE (aux))) return; if (result == 0) pattern = (sibcall_p ? gen_sibcall_internal (addr, args_size) : gen_call_internal (addr, args_size)); else if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 2) { rtx reg1, reg2; reg1 = XEXP (XVECEXP (result, 0, 0), 0); reg2 = XEXP (XVECEXP (result, 0, 1), 0); pattern = (sibcall_p ? gen_sibcall_value_multiple_internal (reg1, addr, args_size, reg2) : gen_call_value_multiple_internal (reg1, addr, args_size, reg2)); } else pattern = (sibcall_p ? gen_sibcall_value_internal (result, addr, args_size) : gen_call_value_internal (result, addr, args_size)); insn = emit_call_insn (pattern); /* Lazy-binding stubs require $gp to be valid on entry. */ if (global_got_operand (orig_addr, VOIDmode)) use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx); } /* We can handle any sibcall when TARGET_SIBCALLS is true. */ static bool mips_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED, tree exp ATTRIBUTE_UNUSED) { return TARGET_SIBCALLS; } /* Emit code to move general operand SRC into condition-code register DEST. SCRATCH is a scratch TFmode float register. The sequence is: FP1 = SRC FP2 = 0.0f DEST = FP2 < FP1 where FP1 and FP2 are single-precision float registers taken from SCRATCH. */ void mips_emit_fcc_reload (rtx dest, rtx src, rtx scratch) { rtx fp1, fp2; /* Change the source to SFmode. */ if (MEM_P (src)) src = adjust_address (src, SFmode, 0); else if (REG_P (src) || 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)); } /* Emit code to change the current function's return address to ADDRESS. SCRATCH is available as a scratch register, if needed. ADDRESS and SCRATCH are both word-mode GPRs. */ void mips_set_return_address (rtx address, rtx scratch) { rtx slot_address; compute_frame_size (get_frame_size ()); gcc_assert ((cfun->machine->frame.mask >> 31) & 1); slot_address = mips_add_offset (scratch, stack_pointer_rtx, cfun->machine->frame.gp_sp_offset); emit_move_insn (gen_rtx_MEM (GET_MODE (address), slot_address), address); } /* Emit straight-line code to move LENGTH bytes from SRC to DEST. Assume that the areas do not overlap. */ static void mips_block_move_straight (rtx dest, rtx src, HOST_WIDE_INT length) { HOST_WIDE_INT offset, delta; unsigned HOST_WIDE_INT bits; int i; enum machine_mode mode; rtx *regs; /* Work out how many bits to move at a time. If both operands have half-word alignment, it is usually better to move in half words. For instance, lh/lh/sh/sh is usually better than lwl/lwr/swl/swr and lw/lw/sw/sw is usually better than ldl/ldr/sdl/sdr. Otherwise move word-sized chunks. */ if (MEM_ALIGN (src) == BITS_PER_WORD / 2 && MEM_ALIGN (dest) == BITS_PER_WORD / 2) bits = BITS_PER_WORD / 2; else bits = BITS_PER_WORD; mode = mode_for_size (bits, MODE_INT, 0); delta = bits / BITS_PER_UNIT; /* Allocate a buffer for the temporary registers. */ regs = alloca (sizeof (rtx) * length / delta); /* Load as many BITS-sized chunks as possible. Use a normal load if the source has enough alignment, otherwise use left/right pairs. */ for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++) { regs[i] = gen_reg_rtx (mode); if (MEM_ALIGN (src) >= bits) emit_move_insn (regs[i], adjust_address (src, mode, offset)); else { rtx part = adjust_address (src, BLKmode, offset); if (!mips_expand_unaligned_load (regs[i], part, bits, 0)) gcc_unreachable (); } } /* Copy the chunks to the destination. */ for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++) if (MEM_ALIGN (dest) >= bits) emit_move_insn (adjust_address (dest, mode, offset), regs[i]); else { rtx part = adjust_address (dest, BLKmode, offset); if (!mips_expand_unaligned_store (part, regs[i], bits, 0)) gcc_unreachable (); } /* Mop up any left-over bytes. */ if (offset < length) { src = adjust_address (src, BLKmode, offset); dest = adjust_address (dest, BLKmode, offset); move_by_pieces (dest, src, length - offset, MIN (MEM_ALIGN (src), MEM_ALIGN (dest)), 0); } } #define MAX_MOVE_REGS 4 #define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD) /* Helper function for doing a loop-based block operation on memory reference MEM. Each iteration of the loop will operate on LENGTH bytes of MEM. Create a new base register for use within the loop and point it to the start of MEM. Create a new memory reference that uses this register. Store them in *LOOP_REG and *LOOP_MEM respectively. */ static void mips_adjust_block_mem (rtx mem, HOST_WIDE_INT length, rtx *loop_reg, rtx *loop_mem) { *loop_reg = copy_addr_to_reg (XEXP (mem, 0)); /* Although the new mem does not refer to a known location, it does keep up to LENGTH bytes of alignment. */ *loop_mem = change_address (mem, BLKmode, *loop_reg); set_mem_align (*loop_mem, MIN (MEM_ALIGN (mem), length * BITS_PER_UNIT)); } /* Move LENGTH bytes from SRC to DEST using a loop that moves MAX_MOVE_BYTES per iteration. LENGTH must be at least MAX_MOVE_BYTES. Assume that the memory regions do not overlap. */ static void mips_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length) { rtx label, src_reg, dest_reg, final_src; HOST_WIDE_INT leftover; leftover = length % MAX_MOVE_BYTES; length -= leftover; /* Create registers and memory references for use within the loop. */ mips_adjust_block_mem (src, MAX_MOVE_BYTES, &src_reg, &src); mips_adjust_block_mem (dest, MAX_MOVE_BYTES, &dest_reg, &dest); /* Calculate the value that SRC_REG should have after the last iteration of the loop. */ final_src = expand_simple_binop (Pmode, PLUS, src_reg, GEN_INT (length), 0, 0, OPTAB_WIDEN); /* Emit the start of the loop. */ label = gen_label_rtx (); emit_label (label); /* Emit the loop body. */ mips_block_move_straight (dest, src, MAX_MOVE_BYTES); /* Move on to the next block. */ emit_move_insn (src_reg, plus_constant (src_reg, MAX_MOVE_BYTES)); emit_move_insn (dest_reg, plus_constant (dest_reg, MAX_MOVE_BYTES)); /* Emit the loop condition. */ if (Pmode == DImode) emit_insn (gen_cmpdi (src_reg, final_src)); else emit_insn (gen_cmpsi (src_reg, final_src)); emit_jump_insn (gen_bne (label)); /* Mop up any left-over bytes. */ if (leftover) mips_block_move_straight (dest, src, leftover); } /* Expand a movmemsi instruction. */ bool mips_expand_block_move (rtx dest, rtx src, rtx length) { if (GET_CODE (length) == CONST_INT) { if (INTVAL (length) <= 2 * MAX_MOVE_BYTES) { mips_block_move_straight (dest, src, INTVAL (length)); return true; } else if (optimize) { mips_block_move_loop (dest, src, INTVAL (length)); return true; } } return false; } /* Argument support functions. */ /* Initialize CUMULATIVE_ARGS for a function. */ void init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, rtx libname ATTRIBUTE_UNUSED) { static CUMULATIVE_ARGS zero_cum; tree param, next_param; *cum = zero_cum; cum->prototype = (fntype && TYPE_ARG_TYPES (fntype)); /* Determine if this function has variable arguments. This is indicated by the last argument being 'void_type_mode' if there are no variable arguments. The standard MIPS calling sequence passes all arguments in the general purpose registers in this case. */ 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; } } /* Fill INFO with information about a single argument. CUM is the cumulative state for earlier arguments. MODE is the mode of this argument and TYPE is its type (if known). NAMED is true if this is a named (fixed) argument rather than a variable one. */ static void mips_arg_info (const CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, int named, struct mips_arg_info *info) { bool doubleword_aligned_p; unsigned int num_bytes, num_words, max_regs; /* Work out the size of the argument. */ num_bytes = type ? int_size_in_bytes (type) : GET_MODE_SIZE (mode); num_words = (num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; /* Decide whether it should go in a floating-point register, assuming one is free. Later code checks for availability. The checks against UNITS_PER_FPVALUE handle the soft-float and single-float cases. */ switch (mips_abi) { case ABI_EABI: /* The EABI conventions have traditionally been defined in terms of TYPE_MODE, regardless of the actual type. */ info->fpr_p = ((GET_MODE_CLASS (mode) == MODE_FLOAT || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT) && GET_MODE_SIZE (mode) <= UNITS_PER_FPVALUE); break; case ABI_32: case ABI_O64: /* Only leading floating-point scalars are passed in floating-point registers. We also handle vector floats the same say, which is OK because they are not covered by the standard ABI. */ info->fpr_p = (!cum->gp_reg_found && cum->arg_number < 2 && (type == 0 || SCALAR_FLOAT_TYPE_P (type) || VECTOR_FLOAT_TYPE_P (type)) && (GET_MODE_CLASS (mode) == MODE_FLOAT || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT) && GET_MODE_SIZE (mode) <= UNITS_PER_FPVALUE); break; case ABI_N32: case ABI_64: /* Scalar and complex floating-point types are passed in floating-point registers. */ info->fpr_p = (named && (type == 0 || FLOAT_TYPE_P (type)) && (GET_MODE_CLASS (mode) == MODE_FLOAT || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT) && GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_FPVALUE); /* ??? According to the ABI documentation, the real and imaginary parts of complex floats should be passed in individual registers. The real and imaginary parts of stack arguments are supposed to be contiguous and there should be an extra word of padding at the end. This has two problems. First, it makes it impossible to use a single "void *" va_list type, since register and stack arguments are passed differently. (At the time of writing, MIPSpro cannot handle complex float varargs correctly.) Second, it's unclear what should happen when there is only one register free. For now, we assume that named complex floats should go into FPRs if there are two FPRs free, otherwise they should be passed in the same way as a struct containing two floats. */ if (info->fpr_p && GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT && GET_MODE_UNIT_SIZE (mode) < UNITS_PER_FPVALUE) { if (cum->num_gprs >= MAX_ARGS_IN_REGISTERS - 1) info->fpr_p = false; else num_words = 2; } break; default: gcc_unreachable (); } /* See whether the argument has doubleword alignment. */ doubleword_aligned_p = FUNCTION_ARG_BOUNDARY (mode, type) > BITS_PER_WORD; /* Set REG_OFFSET to the register count we're interested in. The EABI allocates the floating-point registers separately, but the other ABIs allocate them like integer registers. */ info->reg_offset = (mips_abi == ABI_EABI && info->fpr_p ? cum->num_fprs : cum->num_gprs); /* Advance to an even register if the argument is doubleword-aligned. */ if (doubleword_aligned_p) info->reg_offset += info->reg_offset & 1; /* Work out the offset of a stack argument. */ info->stack_offset = cum->stack_words; if (doubleword_aligned_p) info->stack_offset += info->stack_offset & 1; max_regs = MAX_ARGS_IN_REGISTERS - info->reg_offset; /* Partition the argument between registers and stack. */ info->reg_words = MIN (num_words, max_regs); info->stack_words = num_words - info->reg_words; } /* Implement FUNCTION_ARG_ADVANCE. */ void function_arg_advance (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.fpr_p) cum->gp_reg_found = true; /* See the comment above the cumulative args structure in mips.h for an explanation of what this code does. It assumes the O32 ABI, which passes at most 2 arguments in float registers. */ 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++; } /* Implement FUNCTION_ARG. */ struct rtx_def * function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, int named) { struct mips_arg_info info; /* We will be called with a mode of VOIDmode after the last argument has been seen. Whatever we return will be passed to the call insn. If we need a mips16 fp_code, return a REG with the code stored as the mode. */ if (mode == VOIDmode) { 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); /* Return straight away if the whole argument is passed on the stack. */ if (info.reg_offset == MAX_ARGS_IN_REGISTERS) return 0; if (type != 0 && TREE_CODE (type) == RECORD_TYPE && TARGET_NEWABI && TYPE_SIZE_UNIT (type) && host_integerp (TYPE_SIZE_UNIT (type), 1) && named) { /* The Irix 6 n32/n64 ABIs say that if any 64 bit chunk of the structure contains a double in its entirety, then that 64 bit chunk is passed in a floating point register. */ tree field; /* First check to see if there is any such 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) { /* Now handle the special case by returning a PARALLEL indicating where each 64 bit chunk goes. INFO.REG_WORDS chunks are passed in registers. */ unsigned int i; HOST_WIDE_INT bitpos; rtx ret; /* assign_parms checks the mode of ENTRY_PARM, so we must use the actual mode here. */ 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; } } /* Handle the n32/n64 conventions for passing complex floating-point arguments in FPR pairs. The real part goes in the lower register and the imaginary part goes in the upper register. */ if (TARGET_NEWABI && info.fpr_p && GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT) { rtx real, imag; enum machine_mode inner; int reg; inner = GET_MODE_INNER (mode); reg = FP_ARG_FIRST + info.reg_offset; if (info.reg_words * UNITS_PER_WORD == GET_MODE_SIZE (inner)) { /* Real part in registers, imaginary part on stack. */ gcc_assert (info.stack_words == info.reg_words); return gen_rtx_REG (inner, reg); } else { gcc_assert (info.stack_words == 0); real = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (inner, reg), const0_rtx); imag = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (inner, reg + info.reg_words / 2), GEN_INT (GET_MODE_SIZE (inner))); return gen_rtx_PARALLEL (mode, gen_rtvec (2, real, imag)); } } if (!info.fpr_p) return gen_rtx_REG (mode, GP_ARG_FIRST + info.reg_offset); else if (info.reg_offset == 1) /* This code handles the special o32 case in which the second word of the argument structure is passed in floating-point registers. */ return gen_rtx_REG (mode, FP_ARG_FIRST + FP_INC); else return gen_rtx_REG (mode, FP_ARG_FIRST + info.reg_offset); } /* Implement TARGET_ARG_PARTIAL_BYTES. */ static int mips_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, bool named) { struct mips_arg_info info; mips_arg_info (cum, mode, type, named, &info); return info.stack_words > 0 ? info.reg_words * UNITS_PER_WORD : 0; } /* Implement FUNCTION_ARG_BOUNDARY. Every parameter gets at least PARM_BOUNDARY bits of alignment, but will be given anything up to STACK_BOUNDARY bits if the type requires it. */ int function_arg_boundary (enum machine_mode mode, tree type) { unsigned int alignment; alignment = type ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode); if (alignment < PARM_BOUNDARY) alignment = PARM_BOUNDARY; if (alignment > STACK_BOUNDARY) alignment = STACK_BOUNDARY; return alignment; } /* Return true if FUNCTION_ARG_PADDING (MODE, TYPE) should return upward rather than downward. In other words, return true if the first byte of the stack slot has useful data, false if the last byte does. */ bool mips_pad_arg_upward (enum machine_mode mode, tree type) { /* On little-endian targets, the first byte of every stack argument is passed in the first byte of the stack slot. */ if (!BYTES_BIG_ENDIAN) return true; /* Otherwise, integral types are padded downward: the last byte of a stack argument is passed in the last byte of the stack slot. */ if (type != 0 ? INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type) : GET_MODE_CLASS (mode) == MODE_INT) return false; /* Big-endian o64 pads floating-point arguments downward. */ if (mips_abi == ABI_O64) if (type != 0 ? FLOAT_TYPE_P (type) : GET_MODE_CLASS (mode) == MODE_FLOAT) return false; /* Other types are padded upward for o32, o64, n32 and n64. */ if (mips_abi != ABI_EABI) return true; /* Arguments smaller than a stack slot are padded downward. */ if (mode != BLKmode) return (GET_MODE_BITSIZE (mode) >= PARM_BOUNDARY); else return (int_size_in_bytes (type) >= (PARM_BOUNDARY / BITS_PER_UNIT)); } /* Likewise BLOCK_REG_PADDING (MODE, TYPE, ...). Return !BYTES_BIG_ENDIAN if the least significant byte of the register has useful data. Return the opposite if the most significant byte does. */ bool mips_pad_reg_upward (enum machine_mode mode, tree type) { /* No shifting is required for floating-point arguments. */ if (type != 0 ? FLOAT_TYPE_P (type) : GET_MODE_CLASS (mode) == MODE_FLOAT) return !BYTES_BIG_ENDIAN; /* Otherwise, apply the same padding to register arguments as we do to stack arguments. */ return mips_pad_arg_upward (mode, type); } static void mips_setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, int *pretend_size ATTRIBUTE_UNUSED, int no_rtl) { CUMULATIVE_ARGS local_cum; int gp_saved, fp_saved; /* The caller has advanced CUM up to, but not beyond, the last named argument. Advance a local copy of CUM past the last "real" named argument, to find out how many registers are left over. */ local_cum = *cum; FUNCTION_ARG_ADVANCE (local_cum, mode, type, 1); /* Found out how many registers we need to save. */ 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 = plus_constant (virtual_incoming_args_rtx, REG_PARM_STACK_SPACE (cfun->decl) - gp_saved * UNITS_PER_WORD); mem = gen_rtx_MEM (BLKmode, ptr); set_mem_alias_set (mem, get_varargs_alias_set ()); move_block_from_reg (local_cum.num_gprs + GP_ARG_FIRST, mem, gp_saved); } if (fp_saved > 0) { /* We can't use move_block_from_reg, because it will use the wrong mode. */ enum machine_mode mode; int off, i; /* Set OFF to the offset from virtual_incoming_args_rtx of the first float register. The FP save area lies below the integer one, and is aligned to UNITS_PER_FPVALUE bytes. */ 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, mem; ptr = plus_constant (virtual_incoming_args_rtx, off); mem = gen_rtx_MEM (mode, ptr); set_mem_alias_set (mem, get_varargs_alias_set ()); emit_move_insn (mem, gen_rtx_REG (mode, FP_ARG_FIRST + i)); off += UNITS_PER_HWFPVALUE; } } } if (REG_PARM_STACK_SPACE (cfun->decl) == 0) cfun->machine->varargs_size = (gp_saved * UNITS_PER_WORD + fp_saved * UNITS_PER_FPREG); } /* Create the va_list data type. We keep 3 pointers, and two offsets. Two pointers are to the overflow area, which starts at the CFA. One of these is constant, for addressing into the GPR save area below it. The other is advanced up the stack through the overflow region. The third pointer is to the GPR save area. Since the FPR save area is just below it, we can address FPR slots off this pointer. We also keep two one-byte offsets, which are to be subtracted from the constant pointers to yield addresses in the GPR and FPR save areas. These are downcounted as float or non-float arguments are used, and when they get to zero, the argument must be obtained from the overflow region. If !EABI_FLOAT_VARARGS_P, then no FPR save area exists, and a single pointer is enough. It's started at the GPR save area, and is advanced, period. Note that the GPR save area is not constant size, due to optimization in the prologue. Hence, we can't use a design with two pointers and two offsets, although we could have designed this with two pointers and three offsets. */ static tree mips_build_builtin_va_list (void) { if (EABI_FLOAT_VARARGS_P) { tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff, f_res, record; tree array, index; record = (*lang_hooks.types.make_type) (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); /* Explicitly pad to the size of a pointer, so that -Wpadded won't warn on every user file. */ index = build_int_cst (NULL_TREE, GET_MODE_SIZE (ptr_mode) - 2 - 1); array = build_array_type (unsigned_char_type_node, build_index_type (index)); f_res = build_decl (FIELD_DECL, get_identifier ("__reserved"), array); 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; DECL_FIELD_CONTEXT (f_res) = 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; TREE_CHAIN (f_foff) = f_res; layout_type (record); return record; } else if (TARGET_IRIX && TARGET_IRIX6) /* On IRIX 6, this type is 'char *'. */ return build_pointer_type (char_type_node); else /* Otherwise, we use 'void *'. */ return ptr_type_node; } /* Implement va_start. */ void mips_va_start (tree valist, rtx nextarg) { if (EABI_FLOAT_VARARGS_P) { const CUMULATIVE_ARGS *cum; tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff; tree ovfl, gtop, ftop, goff, foff; tree t; int gpr_save_area_size; int fpr_save_area_size; int fpr_offset; cum = ¤t_function_args_info; gpr_save_area_size = (MAX_ARGS_IN_REGISTERS - cum->num_gprs) * UNITS_PER_WORD; fpr_save_area_size = (MAX_ARGS_IN_REGISTERS - cum->num_fprs) * UNITS_PER_FPREG; 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 = build3 (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl, NULL_TREE); gtop = build3 (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop, NULL_TREE); ftop = build3 (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop, NULL_TREE); goff = build3 (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff, NULL_TREE); foff = build3 (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff, NULL_TREE); /* Emit code to initialize OVFL, which points to the next varargs stack argument. CUM->STACK_WORDS gives the number of stack words used by named arguments. */ t = make_tree (TREE_TYPE (ovfl), virtual_incoming_args_rtx); if (cum->stack_words > 0) t = build2 (PLUS_EXPR, TREE_TYPE (ovfl), t, build_int_cst (NULL_TREE, cum->stack_words * UNITS_PER_WORD)); t = build2 (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t); expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); /* Emit code to initialize GTOP, the top of the GPR save area. */ t = make_tree (TREE_TYPE (gtop), virtual_incoming_args_rtx); t = build2 (MODIFY_EXPR, TREE_TYPE (gtop), gtop, t); expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); /* Emit code to initialize FTOP, the top of the FPR save area. This address is gpr_save_area_bytes below GTOP, rounded down to the next fp-aligned boundary. */ 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 = build2 (PLUS_EXPR, TREE_TYPE (ftop), t, build_int_cst (NULL_TREE, -fpr_offset)); t = build2 (MODIFY_EXPR, TREE_TYPE (ftop), ftop, t); expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); /* Emit code to initialize GOFF, the offset from GTOP of the next GPR argument. */ t = build2 (MODIFY_EXPR, TREE_TYPE (goff), goff, build_int_cst (NULL_TREE, gpr_save_area_size)); expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); /* Likewise emit code to initialize FOFF, the offset from FTOP of the next FPR argument. */ t = build2 (MODIFY_EXPR, TREE_TYPE (foff), foff, build_int_cst (NULL_TREE, fpr_save_area_size)); expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } else { nextarg = plus_constant (nextarg, -cfun->machine->varargs_size); std_expand_builtin_va_start (valist, nextarg); } } /* Implement va_arg. */ static tree mips_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p) { HOST_WIDE_INT size, rsize; tree addr; bool indirect; indirect = pass_by_reference (NULL, TYPE_MODE (type), type, 0); if (indirect) type = build_pointer_type (type); size = int_size_in_bytes (type); rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD; if (mips_abi != ABI_EABI || !EABI_FLOAT_VARARGS_P) addr = std_gimplify_va_arg_expr (valist, type, pre_p, post_p); else { /* Not a simple merged stack. */ tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff; tree ovfl, top, off, align; HOST_WIDE_INT osize; tree t, u; 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); /* We maintain separate pointers and offsets for floating-point and integer arguments, but we need similar code in both cases. Let: TOP be the top of the register save area; OFF be the offset from TOP of the next register; ADDR_RTX be the address of the argument; RSIZE be the number of bytes used to store the argument when it's in the register save area; OSIZE be the number of bytes used to store it when it's in the stack overflow area; and PADDING be (BYTES_BIG_ENDIAN ? OSIZE - RSIZE : 0) The code we want is: 1: off &= -rsize; // round down 2: if (off != 0) 3: { 4: addr_rtx = top - off; 5: off -= rsize; 6: } 7: else 8: { 9: ovfl += ((intptr_t) ovfl + osize - 1) & -osize; 10: addr_rtx = ovfl + PADDING; 11: ovfl += osize; 14: } [1] and [9] can sometimes be optimized away. */ ovfl = build3 (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl, NULL_TREE); if (GET_MODE_CLASS (TYPE_MODE (type)) == MODE_FLOAT && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_FPVALUE) { top = build3 (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop, NULL_TREE); off = build3 (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff, NULL_TREE); /* When floating-point registers are saved to the stack, each one will take up UNITS_PER_HWFPVALUE bytes, regardless of the float's precision. */ rsize = UNITS_PER_HWFPVALUE; /* Overflow arguments are padded to UNITS_PER_WORD bytes (= PARM_BOUNDARY bits). This can be different from RSIZE in two cases: (1) On 32-bit targets when TYPE is a structure such as: struct s { float f; }; Such structures are passed in paired FPRs, so RSIZE will be 8 bytes. However, the structure only takes up 4 bytes of memory, so OSIZE will only be 4. (2) In combinations such as -mgp64 -msingle-float -fshort-double. Doubles passed in registers will then take up 4 (UNITS_PER_HWFPVALUE) bytes, but those passed on the stack take up UNITS_PER_WORD bytes. */ osize = MAX (GET_MODE_SIZE (TYPE_MODE (type)), UNITS_PER_WORD); } else { top = build3 (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop, NULL_TREE); off = build3 (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff, NULL_TREE); if (rsize > UNITS_PER_WORD) { /* [1] Emit code for: off &= -rsize. */ t = build2 (BIT_AND_EXPR, TREE_TYPE (off), off, build_int_cst (NULL_TREE, -rsize)); t = build2 (MODIFY_EXPR, TREE_TYPE (off), off, t); gimplify_and_add (t, pre_p); } osize = rsize; } /* [2] Emit code to branch if off == 0. */ t = build2 (NE_EXPR, boolean_type_node, off, build_int_cst (TREE_TYPE (off), 0)); addr = build3 (COND_EXPR, ptr_type_node, t, NULL_TREE, NULL_TREE); /* [5] Emit code for: off -= rsize. We do this as a form of post-increment not available to C. Also widen for the coming pointer arithmetic. */ t = fold_convert (TREE_TYPE (off), build_int_cst (NULL_TREE, rsize)); t = build2 (POSTDECREMENT_EXPR, TREE_TYPE (off), off, t); t = fold_convert (sizetype, t); t = fold_convert (TREE_TYPE (top), t); /* [4] Emit code for: addr_rtx = top - off. On big endian machines, the argument has RSIZE - SIZE bytes of leading padding. */ t = build2 (MINUS_EXPR, TREE_TYPE (top), top, t); if (BYTES_BIG_ENDIAN && rsize > size) { u = fold_convert (TREE_TYPE (t), build_int_cst (NULL_TREE, rsize - size)); t = build2 (PLUS_EXPR, TREE_TYPE (t), t, u); } COND_EXPR_THEN (addr) = t; if (osize > UNITS_PER_WORD) { /* [9] Emit: ovfl += ((intptr_t) ovfl + osize - 1) & -osize. */ u = fold_convert (TREE_TYPE (ovfl), build_int_cst (NULL_TREE, osize - 1)); t = build2 (PLUS_EXPR, TREE_TYPE (ovfl), ovfl, u); u = fold_convert (TREE_TYPE (ovfl), build_int_cst (NULL_TREE, -osize)); t = build2 (BIT_AND_EXPR, TREE_TYPE (ovfl), t, u); align = build2 (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t); } else align = NULL; /* [10, 11]. Emit code to store ovfl in addr_rtx, then post-increment ovfl by osize. On big-endian machines, the argument has OSIZE - SIZE bytes of leading padding. */ u = fold_convert (TREE_TYPE (ovfl), build_int_cst (NULL_TREE, osize)); t = build2 (POSTINCREMENT_EXPR, TREE_TYPE (ovfl), ovfl, u); if (BYTES_BIG_ENDIAN && osize > size) { u = fold_convert (TREE_TYPE (t), build_int_cst (NULL_TREE, osize - size)); t = build2 (PLUS_EXPR, TREE_TYPE (t), t, u); } /* String [9] and [10,11] together. */ if (align) t = build2 (COMPOUND_EXPR, TREE_TYPE (t), align, t); COND_EXPR_ELSE (addr) = t; addr = fold_convert (build_pointer_type (type), addr); addr = build_va_arg_indirect_ref (addr); } if (indirect) addr = build_va_arg_indirect_ref (addr); return addr; } /* Return true if it is possible to use left/right accesses for a bitfield of WIDTH bits starting BITPOS bits into *OP. When returning true, update *OP, *LEFT and *RIGHT as follows: *OP is a BLKmode reference to the whole field. *LEFT is a QImode reference to the first byte if big endian or the last byte if little endian. This address can be used in the left-side instructions (lwl, swl, ldl, sdl). *RIGHT is a QImode reference to the opposite end of the field and can be used in the patterning right-side instruction. */ static bool mips_get_unaligned_mem (rtx *op, unsigned int width, int bitpos, rtx *left, rtx *right) { rtx first, last; /* Check that the operand really is a MEM. Not all the extv and extzv predicates are checked. */ if (!MEM_P (*op)) return false; /* Check that the size is valid. */ if (width != 32 && (!TARGET_64BIT || width != 64)) return false; /* We can only access byte-aligned values. Since we are always passed a reference to the first byte of the field, it is not necessary to do anything with BITPOS after this check. */ if (bitpos % BITS_PER_UNIT != 0) return false; /* Reject aligned bitfields: we want to use a normal load or store instead of a left/right pair. */ if (MEM_ALIGN (*op) >= width) return false; /* Adjust *OP to refer to the whole field. This also has the effect of legitimizing *OP's address for BLKmode, possibly simplifying it. */ *op = adjust_address (*op, BLKmode, 0); set_mem_size (*op, GEN_INT (width / BITS_PER_UNIT)); /* Get references to both ends of the field. We deliberately don't use the original QImode *OP for FIRST since the new BLKmode one might have a simpler address. */ first = adjust_address (*op, QImode, 0); last = adjust_address (*op, QImode, width / BITS_PER_UNIT - 1); /* Allocate to LEFT and RIGHT according to endianness. LEFT should be the upper word and RIGHT the lower word. */ if (TARGET_BIG_ENDIAN) *left = first, *right = last; else *left = last, *right = first; return true; } /* Try to emit the equivalent of (set DEST (zero_extract SRC WIDTH BITPOS)). Return true on success. We only handle cases where zero_extract is equivalent to sign_extract. */ bool mips_expand_unaligned_load (rtx dest, rtx src, unsigned int width, int bitpos) { rtx left, right, temp; /* If TARGET_64BIT, the destination of a 32-bit load will be a paradoxical word_mode subreg. This is the only case in which we allow the destination to be larger than the source. */ if (GET_CODE (dest) == SUBREG && GET_MODE (dest) == DImode && SUBREG_BYTE (dest) == 0 && GET_MODE (SUBREG_REG (dest)) == SImode) dest = SUBREG_REG (dest); /* After the above adjustment, the destination must be the same width as the source. */ if (GET_MODE_BITSIZE (GET_MODE (dest)) != width) return false; if (!mips_get_unaligned_mem (&src, width, bitpos, &left, &right)) return false; temp = gen_reg_rtx (GET_MODE (dest)); if (GET_MODE (dest) == DImode) { emit_insn (gen_mov_ldl (temp, src, left)); emit_insn (gen_mov_ldr (dest, copy_rtx (src), right, temp)); } else { emit_insn (gen_mov_lwl (temp, src, left)); emit_insn (gen_mov_lwr (dest, copy_rtx (src), right, temp)); } return true; } /* Try to expand (set (zero_extract DEST WIDTH BITPOS) SRC). Return true on success. */ bool mips_expand_unaligned_store (rtx dest, rtx src, unsigned int width, int bitpos) { rtx left, right; enum machine_mode mode; if (!mips_get_unaligned_mem (&dest, width, bitpos, &left, &right)) return false; mode = mode_for_size (width, MODE_INT, 0); src = gen_lowpart (mode, src); if (mode == DImode) { emit_insn (gen_mov_sdl (dest, src, left)); emit_insn (gen_mov_sdr (copy_rtx (dest), copy_rtx (src), right)); } else { emit_insn (gen_mov_swl (dest, src, left)); emit_insn (gen_mov_swr (copy_rtx (dest), copy_rtx (src), right)); } return true; } /* Return true if X is a MEM with the same size as MODE. */ bool mips_mem_fits_mode_p (enum machine_mode mode, rtx x) { rtx size; if (!MEM_P (x)) return false; size = MEM_SIZE (x); return size && INTVAL (size) == GET_MODE_SIZE (mode); } /* Return true if (zero_extract OP SIZE POSITION) can be used as the source of an "ext" instruction or the destination of an "ins" instruction. OP must be a register operand and the following conditions must hold: 0 <= POSITION < GET_MODE_BITSIZE (GET_MODE (op)) 0 < SIZE <= GET_MODE_BITSIZE (GET_MODE (op)) 0 < POSITION + SIZE <= GET_MODE_BITSIZE (GET_MODE (op)) Also reject lengths equal to a word as they are better handled by the move patterns. */ bool mips_use_ins_ext_p (rtx op, rtx size, rtx position) { HOST_WIDE_INT len, pos; if (!ISA_HAS_EXT_INS || !register_operand (op, VOIDmode) || GET_MODE_BITSIZE (GET_MODE (op)) > BITS_PER_WORD) return false; len = INTVAL (size); pos = INTVAL (position); if (len <= 0 || len >= GET_MODE_BITSIZE (GET_MODE (op)) || pos < 0 || pos + len > GET_MODE_BITSIZE (GET_MODE (op))) return false; return true; } /* Set up globals to generate code for the ISA or processor described by INFO. */ static void mips_set_architecture (const struct mips_cpu_info *info) { if (info != 0) { mips_arch_info = info; mips_arch = info->cpu; mips_isa = info->isa; } } /* Likewise for tuning. */ static void mips_set_tune (const struct mips_cpu_info *info) { if (info != 0) { mips_tune_info = info; mips_tune = info->cpu; } } /* Implement TARGET_HANDLE_OPTION. */ static bool mips_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED) { switch (code) { case OPT_mabi_: if (strcmp (arg, "32") == 0) mips_abi = ABI_32; else if (strcmp (arg, "o64") == 0) mips_abi = ABI_O64; else if (strcmp (arg, "n32") == 0) mips_abi = ABI_N32; else if (strcmp (arg, "64") == 0) mips_abi = ABI_64; else if (strcmp (arg, "eabi") == 0) mips_abi = ABI_EABI; else return false; return true; case OPT_march_: case OPT_mtune_: return mips_parse_cpu (arg) != 0; case OPT_mips: mips_isa_info = mips_parse_cpu (ACONCAT (("mips", arg, NULL))); return mips_isa_info != 0; case OPT_mno_flush_func: mips_cache_flush_func = NULL; return true; default: return true; } } /* Set up the threshold for data to go into the small data area, instead of the normal data area, and detect any conflicts in the switches. */ void override_options (void) { int i, start, regno; enum machine_mode mode; mips_section_threshold = g_switch_set ? g_switch_value : MIPS_DEFAULT_GVALUE; /* The following code determines the architecture and register size. Similar code was added to GAS 2.14 (see tc-mips.c:md_after_parse_args()). The GAS and GCC code should be kept in sync as much as possible. */ if (mips_arch_string != 0) mips_set_architecture (mips_parse_cpu (mips_arch_string)); if (mips_isa_info != 0) { if (mips_arch_info == 0) mips_set_architecture (mips_isa_info); else if (mips_arch_info->isa != mips_isa_info->isa) error ("-%s conflicts with the other architecture options, " "which specify a %s processor", mips_isa_info->name, mips_cpu_info_from_isa (mips_arch_info->isa)->name); } if (mips_arch_info == 0) { #ifdef MIPS_CPU_STRING_DEFAULT mips_set_architecture (mips_parse_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); /* Optimize for mips_arch, unless -mtune selects a different processor. */ if (mips_tune_string != 0) mips_set_tune (mips_parse_cpu (mips_tune_string)); if (mips_tune_info == 0) mips_set_tune (mips_arch_info); /* Set cost structure for the processor. */ mips_cost = &mips_rtx_cost_data[mips_tune]; if ((target_flags_explicit & MASK_64BIT) != 0) { /* The user specified the size of the integer registers. Make sure it agrees with the ABI and ISA. */ 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 { /* Infer the integer register size from the ABI and processor. Restrict ourselves to 32-bit registers if that's all the processor has, or if the ABI cannot handle 64-bit registers. */ 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) { /* Really, -mfp32 and -mfp64 are ornamental options. There's only one right answer here. */ 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 { /* -msingle-float selects 32-bit float registers. Otherwise the float registers should be the same size as the integer ones. */ if (TARGET_64BIT && TARGET_DOUBLE_FLOAT) target_flags |= MASK_FLOAT64; else target_flags &= ~MASK_FLOAT64; } /* End of code shared with GAS. */ 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) { /* For some configurations, it is useful to have -march control the default setting of MASK_SOFT_FLOAT. */ switch ((int) mips_arch) { case PROCESSOR_R4100: case PROCESSOR_R4111: case PROCESSOR_R4120: case PROCESSOR_R4130: target_flags |= MASK_SOFT_FLOAT; break; default: target_flags &= ~MASK_SOFT_FLOAT; break; } } if (!TARGET_OLDABI) flag_pcc_struct_return = 0; if ((target_flags_explicit & MASK_BRANCHLIKELY) == 0) { /* If neither -mbranch-likely nor -mno-branch-likely was given on the command line, set MASK_BRANCHLIKELY based on the target architecture. By default, we enable use of Branch Likely instructions on all architectures which support them with the following exceptions: when creating MIPS32 or MIPS64 code, and when tuning for architectures where their use tends to hurt performance. The MIPS32 and MIPS64 architecture specifications say "Software is strongly encouraged to avoid use of Branch Likely instructions, as they will be removed from a future revision of the [MIPS32 and MIPS64] architecture." Therefore, we do not issue those instructions unless instructed to do so by -mbranch-likely. */ if (ISA_HAS_BRANCHLIKELY && !(ISA_MIPS32 || ISA_MIPS32R2 || ISA_MIPS64 || ISA_MIPS64R2) && !(TUNE_MIPS5500 || TUNE_SB1)) target_flags |= MASK_BRANCHLIKELY; else target_flags &= ~MASK_BRANCHLIKELY; } if (TARGET_BRANCHLIKELY && !ISA_HAS_BRANCHLIKELY) warning (0, "generation of Branch Likely instructions enabled, but not supported by architecture"); /* The effect of -mabicalls isn't defined for the EABI. */ if (mips_abi == ABI_EABI && TARGET_ABICALLS) { error ("unsupported combination: %s", "-mabicalls -mabi=eabi"); target_flags &= ~MASK_ABICALLS; } if (TARGET_ABICALLS) { /* We need to set flag_pic for executables as well as DSOs because we may reference symbols that are not defined in the final executable. (MIPS does not use things like copy relocs, for example.) Also, there is a body of code that uses __PIC__ to distinguish between -mabicalls and -mno-abicalls code. */ flag_pic = 1; if (mips_section_threshold > 0) warning (0, "%<-G%> is incompatible with %<-mabicalls%>"); } /* mips_split_addresses is a half-way house between explicit relocations and the traditional assembler macros. It can split absolute 32-bit symbolic constants into a high/lo_sum pair but uses macros for other sorts of access. Like explicit relocation support for REL targets, it relies on GNU extensions in the assembler and the linker. Although this code should work for -O0, it has traditionally been treated as an optimization. */ if (!TARGET_MIPS16 && TARGET_SPLIT_ADDRESSES && optimize && !flag_pic && !ABI_HAS_64BIT_SYMBOLS) mips_split_addresses = 1; else mips_split_addresses = 0; /* -mvr4130-align is a "speed over size" optimization: it usually produces faster code, but at the expense of more nops. Enable it at -O3 and above. */ if (optimize > 2 && (target_flags_explicit & MASK_VR4130_ALIGN) == 0) target_flags |= MASK_VR4130_ALIGN; /* When compiling for the mips16, we cannot use floating point. We record the original hard float value in mips16_hard_float. */ if (TARGET_MIPS16) { if (TARGET_SOFT_FLOAT) mips16_hard_float = 0; else mips16_hard_float = 1; target_flags |= MASK_SOFT_FLOAT; /* Don't run the scheduler before reload, since it tends to increase register pressure. */ flag_schedule_insns = 0; /* Don't do hot/cold partitioning. The constant layout code expects the whole function to be in a single section. */ flag_reorder_blocks_and_partition = 0; /* Silently disable -mexplicit-relocs since it doesn't apply to mips16 code. Even so, it would overly pedantic to warn about "-mips16 -mexplicit-relocs", especially given that we use a %gprel() operator. */ target_flags &= ~MASK_EXPLICIT_RELOCS; } /* When using explicit relocs, we call dbr_schedule from within mips_reorg. */ if (TARGET_EXPLICIT_RELOCS) { mips_flag_delayed_branch = flag_delayed_branch; flag_delayed_branch = 0; } #ifdef MIPS_TFMODE_FORMAT REAL_MODE_FORMAT (TFmode) = &MIPS_TFMODE_FORMAT; #endif /* Make sure that the user didn't turn off paired single support when MIPS-3D support is requested. */ if (TARGET_MIPS3D && (target_flags_explicit & MASK_PAIRED_SINGLE_FLOAT) && !TARGET_PAIRED_SINGLE_FLOAT) error ("-mips3d requires -mpaired-single"); /* If TARGET_MIPS3D, enable MASK_PAIRED_SINGLE_FLOAT. */ if (TARGET_MIPS3D) target_flags |= MASK_PAIRED_SINGLE_FLOAT; /* Make sure that when TARGET_PAIRED_SINGLE_FLOAT is true, TARGET_FLOAT64 and TARGET_HARD_FLOAT are both true. */ if (TARGET_PAIRED_SINGLE_FLOAT && !(TARGET_FLOAT64 && TARGET_HARD_FLOAT)) error ("-mips3d/-mpaired-single must be used with -mfp64 -mhard-float"); /* Make sure that the ISA supports TARGET_PAIRED_SINGLE_FLOAT when it is enabled. */ if (TARGET_PAIRED_SINGLE_FLOAT && !ISA_MIPS64) error ("-mips3d/-mpaired-single must be used with -mips64"); if (TARGET_MIPS16 && TARGET_DSP) error ("-mips16 and -mdsp cannot be used together"); 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_print_operand_punct['~'] = 1; /* Set up array to map GCC register number to debug register number. Ignore the special purpose register numbers. */ 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; mips_dbx_regno[HI_REGNUM] = MD_DBX_FIRST + 0; mips_dbx_regno[LO_REGNUM] = MD_DBX_FIRST + 1; /* Set up array giving whether a given register can hold a given mode. */ 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 == CCV2mode) temp = (ISA_HAS_8CC && ST_REG_P (regno) && (regno - ST_REG_FIRST) % 2 == 0); else if (mode == CCV4mode) temp = (ISA_HAS_8CC && ST_REG_P (regno) && (regno - ST_REG_FIRST) % 4 == 0); else 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) && (((class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT || class == MODE_VECTOR_FLOAT) && size <= UNITS_PER_FPVALUE) /* Allow integer modes that fit into a single register. We need to put integers into FPRs when using instructions like cvt and trunc. We can't allow sizes smaller than a word, the FPU has no appropriate load/store instructions for those. */ || (class == MODE_INT && size >= MIN_UNITS_PER_WORD && size <= UNITS_PER_FPREG) /* Allow TFmode for CCmode reloads. */ || (ISA_HAS_8CC && mode == TFmode)); else if (ACC_REG_P (regno)) temp = (INTEGRAL_MODE_P (mode) && (size <= UNITS_PER_WORD || (ACC_HI_REG_P (regno) && 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; } } /* Save GPR registers in word_mode sized hunks. word_mode hasn't been initialized yet, so we can't use that here. */ gpr_mode = TARGET_64BIT ? DImode : SImode; /* Provide default values for align_* for 64-bit targets. */ 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; } /* Function to allocate machine-dependent function status. */ init_machine_status = &mips_init_machine_status; if (ABI_HAS_64BIT_SYMBOLS) { if (TARGET_EXPLICIT_RELOCS) { mips_split_p[SYMBOL_64_HIGH] = true; mips_hi_relocs[SYMBOL_64_HIGH] = "%highest("; mips_lo_relocs[SYMBOL_64_HIGH] = "%higher("; mips_split_p[SYMBOL_64_MID] = true; mips_hi_relocs[SYMBOL_64_MID] = "%higher("; mips_lo_relocs[SYMBOL_64_MID] = "%hi("; mips_split_p[SYMBOL_64_LOW] = true; mips_hi_relocs[SYMBOL_64_LOW] = "%hi("; mips_lo_relocs[SYMBOL_64_LOW] = "%lo("; mips_split_p[SYMBOL_GENERAL] = true; mips_lo_relocs[SYMBOL_GENERAL] = "%lo("; } } else { if (TARGET_EXPLICIT_RELOCS || mips_split_addresses) { mips_split_p[SYMBOL_GENERAL] = true; mips_hi_relocs[SYMBOL_GENERAL] = "%hi("; mips_lo_relocs[SYMBOL_GENERAL] = "%lo("; } } if (TARGET_MIPS16) { /* The high part is provided by a pseudo copy of $gp. */ mips_split_p[SYMBOL_SMALL_DATA] = true; mips_lo_relocs[SYMBOL_SMALL_DATA] = "%gprel("; } if (TARGET_EXPLICIT_RELOCS) { /* Small data constants are kept whole until after reload, then lowered by mips_rewrite_small_data. */ mips_lo_relocs[SYMBOL_SMALL_DATA] = "%gp_rel("; mips_split_p[SYMBOL_GOT_LOCAL] = true; if (TARGET_NEWABI) { mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got_page("; mips_lo_relocs[SYMBOL_GOT_LOCAL] = "%got_ofst("; } else { mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got("; mips_lo_relocs[SYMBOL_GOT_LOCAL] = "%lo("; } if (TARGET_XGOT) { /* The HIGH and LO_SUM are matched by special .md patterns. */ mips_split_p[SYMBOL_GOT_GLOBAL] = true; mips_split_p[SYMBOL_GOTOFF_GLOBAL] = true; mips_hi_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got_hi("; mips_lo_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got_lo("; mips_split_p[SYMBOL_GOTOFF_CALL] = true; mips_hi_relocs[SYMBOL_GOTOFF_CALL] = "%call_hi("; mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call_lo("; } else { if (TARGET_NEWABI) mips_lo_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got_disp("; else mips_lo_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got("; mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call16("; } } if (TARGET_NEWABI) { mips_split_p[SYMBOL_GOTOFF_LOADGP] = true; mips_hi_relocs[SYMBOL_GOTOFF_LOADGP] = "%hi(%neg(%gp_rel("; mips_lo_relocs[SYMBOL_GOTOFF_LOADGP] = "%lo(%neg(%gp_rel("; } /* Thread-local relocation operators. */ mips_lo_relocs[SYMBOL_TLSGD] = "%tlsgd("; mips_lo_relocs[SYMBOL_TLSLDM] = "%tlsldm("; mips_split_p[SYMBOL_DTPREL] = 1; mips_hi_relocs[SYMBOL_DTPREL] = "%dtprel_hi("; mips_lo_relocs[SYMBOL_DTPREL] = "%dtprel_lo("; mips_lo_relocs[SYMBOL_GOTTPREL] = "%gottprel("; mips_split_p[SYMBOL_TPREL] = 1; mips_hi_relocs[SYMBOL_TPREL] = "%tprel_hi("; mips_lo_relocs[SYMBOL_TPREL] = "%tprel_lo("; /* We don't have a thread pointer access instruction on MIPS16, or appropriate TLS relocations. */ if (TARGET_MIPS16) targetm.have_tls = false; /* Default to working around R4000 errata only if the processor was selected explicitly. */ if ((target_flags_explicit & MASK_FIX_R4000) == 0 && mips_matching_cpu_name_p (mips_arch_info->name, "r4000")) target_flags |= MASK_FIX_R4000; /* Default to working around R4400 errata only if the processor was selected explicitly. */ if ((target_flags_explicit & MASK_FIX_R4400) == 0 && mips_matching_cpu_name_p (mips_arch_info->name, "r4400")) target_flags |= MASK_FIX_R4400; } /* Implement CONDITIONAL_REGISTER_USAGE. */ void mips_conditional_register_usage (void) { if (!TARGET_DSP) { int regno; for (regno = DSP_ACC_REG_FIRST; regno <= DSP_ACC_REG_LAST; regno++) fixed_regs[regno] = call_used_regs[regno] = 1; } 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; /* We only have a single condition code register. We implement this by hiding all the condition code registers, and generating RTL that refers directly to ST_REG_FIRST. */ for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++) fixed_regs[regno] = call_used_regs[regno] = 1; } /* In mips16 mode, we permit the $t temporary registers to be used for reload. We prohibit the unused $s registers, since they are caller saved, and saving them via a mips16 register would probably waste more time than just reloading the value. */ 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; } /* fp20-23 are now caller saved. */ 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; } /* Odd registers from fp21 to fp31 are now caller saved. */ if (mips_abi == ABI_N32) { 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; } } /* Allocate a chunk of memory for per-function machine-dependent data. */ static struct machine_function * mips_init_machine_status (void) { return ((struct machine_function *) ggc_alloc_cleared (sizeof (struct machine_function))); } /* On the mips16, we want to allocate $24 (T_REG) before other registers for instructions for which it is possible. This helps avoid shuffling registers around in order to set up for an xor, encouraging the compiler to use a cmp instead. */ void mips_order_regs_for_local_alloc (void) { register int i; for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) reg_alloc_order[i] = i; if (TARGET_MIPS16) { /* It really doesn't matter where we put register 0, since it is a fixed register anyhow. */ reg_alloc_order[0] = 24; reg_alloc_order[24] = 0; } } /* The MIPS debug format wants all automatic variables and arguments to be in terms of the virtual frame pointer (stack pointer before any adjustment in the function), while the MIPS 3.0 linker wants the frame pointer to be the stack pointer after the initial adjustment. So, we do the adjustment here. The arg pointer (which is eliminated) points to the virtual frame pointer, while the frame pointer (which may be eliminated) points to the stack pointer after the initial adjustments. */ HOST_WIDE_INT mips_debugger_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; /* MIPS16 frame is smaller */ if (frame_pointer_needed && TARGET_MIPS16) frame_size -= cfun->machine->frame.args_size; offset = offset - frame_size; } /* sdbout_parms does not want this to crash for unrecognized cases. */ #if 0 else if (reg != arg_pointer_rtx) fatal_insn ("mips_debugger_offset called with non stack/frame/arg pointer", addr); #endif return offset; } /* Implement the PRINT_OPERAND macro. The MIPS-specific operand codes are: 'X' OP is CONST_INT, prints 32 bits in hexadecimal format = "0x%08x", 'x' OP is CONST_INT, prints 16 bits in hexadecimal format = "0x%04x", 'h' OP is HIGH, prints %hi(X), 'd' output integer constant in decimal, 'z' if the operand is 0, use $0 instead of normal operand. 'D' print second part of double-word register or memory operand. 'L' print low-order register of double-word register operand. 'M' print high-order register of double-word register operand. 'C' print part of opcode for a branch condition. 'F' print part of opcode for a floating-point branch condition. 'N' print part of opcode for a branch condition, inverted. 'W' print part of opcode for a floating-point branch condition, inverted. 'T' print 'f' for (eq:CC ...), 't' for (ne:CC ...), 'z' for (eq:?I ...), 'n' for (ne:?I ...). 't' like 'T', but with the EQ/NE cases reversed 'Y' for a CONST_INT X, print mips_fp_conditions[X] 'Z' print the operand and a comma for ISA_HAS_8CC, otherwise print nothing 'R' print the reloc associated with LO_SUM 'q' print DSP accumulator registers The punctuation characters are: '(' Turn on .set noreorder ')' Turn on .set reorder '[' Turn on .set noat ']' Turn on .set at '<' Turn on .set nomacro '>' Turn on .set macro '{' Turn on .set volatile (not GAS) '}' Turn on .set novolatile (not GAS) '&' Turn on .set noreorder if filling delay slots '*' Turn on both .set noreorder and .set nomacro if filling delay slots '!' Turn on .set nomacro if filling delay slots '#' Print nop if in a .set noreorder section. '/' Like '#', but does nothing within a delayed branch sequence '?' Print 'l' if we are to use a branch likely instead of normal branch. '@' Print the name of the assembler temporary register (at or $1). '.' Print the name of the register with a hard-wired zero (zero or $0). '^' Print the name of the pic call-through register (t9 or $25). '$' Print the name of the stack pointer register (sp or $29). '+' Print the name of the gp register (usually gp or $28). '~' Output a branch alignment to LABEL_ALIGN(NULL). */ void print_operand (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[PIC_OFFSET_TABLE_REGNUM], 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); break; case '/': /* Print an extra newline so that the delayed insn is separated from the following ones. This looks neater and is consistent with non-nop delayed sequences. */ if (set_noreorder != 0 && final_sequence == 0) fputs ("\n\tnop\n", 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) fputs ("#.set\tvolatile\n\t", file); break; case '}': if (set_volatile == 0) error ("internal error: %%} found without a %%{ in assembler pattern"); else if (--set_volatile == 0) fputs ("\n\t#.set\tnovolatile", file); 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 (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: fatal_insn ("PRINT_OPERAND, invalid insn for %%C", op); } 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: fatal_insn ("PRINT_OPERAND, invalid insn for %%N", op); } else if (letter == 'F') switch (code) { case EQ: fputs ("c1f", file); break; case NE: fputs ("c1t", file); break; default: fatal_insn ("PRINT_OPERAND, invalid insn for %%F", op); } else if (letter == 'W') switch (code) { case EQ: fputs ("c1t", file); break; case NE: fputs ("c1f", file); break; default: fatal_insn ("PRINT_OPERAND, invalid insn for %%W", op); } else if (letter == 'h') { if (GET_CODE (op) == HIGH) op = XEXP (op, 0); print_operand_reloc (file, op, mips_hi_relocs); } else if (letter == 'R') print_operand_reloc (file, op, mips_lo_relocs); else if (letter == 'Y') { if (GET_CODE (op) == CONST_INT && ((unsigned HOST_WIDE_INT) INTVAL (op) < ARRAY_SIZE (mips_fp_conditions))) fputs (mips_fp_conditions[INTVAL (op)], file); else output_operand_lossage ("invalid %%Y value"); } else if (letter == 'Z') { if (ISA_HAS_8CC) { print_operand (file, op, 0); fputc (',', file); } } else if (letter == 'q') { int regnum; if (code != REG) fatal_insn ("PRINT_OPERAND, invalid insn for %%q", op); regnum = REGNO (op); if (MD_REG_P (regnum)) fprintf (file, "$ac0"); else if (DSP_ACC_REG_P (regnum)) fprintf (file, "$ac%c", reg_names[regnum][3]); else fatal_insn ("PRINT_OPERAND, invalid insn for %%q", op); } 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 (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' && op == CONST0_RTX (GET_MODE (op))) 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 == 'T' || letter == 't') { int truth = (code == NE) == (letter == 'T'); fputc ("zfnt"[truth * 2 + (GET_MODE (op) == CCmode)], file); } else if (CONST_GP_P (op)) fputs (reg_names[GLOBAL_POINTER_REGNUM], file); else output_addr_const (file, op); } /* Print symbolic operand OP, which is part of a HIGH or LO_SUM. RELOCS is the array of relocations to use. */ static void print_operand_reloc (FILE *file, rtx op, const char **relocs) { enum mips_symbol_type symbol_type; const char *p; rtx base; HOST_WIDE_INT offset; symbol_type = mips_classify_symbolic_expression (op); if (relocs[symbol_type] == 0) fatal_insn ("PRINT_OPERAND, invalid operand for relocation", op); /* If OP uses an UNSPEC address, we want to print the inner symbol. */ mips_split_const (op, &base, &offset); if (UNSPEC_ADDRESS_P (base)) op = plus_constant (UNSPEC_ADDRESS (base), offset); fputs (relocs[symbol_type], file); output_addr_const (file, op); for (p = relocs[symbol_type]; *p != 0; p++) if (*p == '(') fputc (')', file); } /* Output address operand X to FILE. */ void print_operand_address (FILE *file, rtx x) { struct mips_address_info addr; if (mips_classify_address (&addr, x, word_mode, true)) switch (addr.type) { case ADDRESS_REG: print_operand (file, addr.offset, 0); fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]); return; case ADDRESS_LO_SUM: print_operand (file, addr.offset, 'R'); fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]); return; case ADDRESS_CONST_INT: output_addr_const (file, x); fprintf (file, "(%s)", reg_names[0]); return; case ADDRESS_SYMBOLIC: output_addr_const (file, x); return; } gcc_unreachable (); } /* When using assembler macros, keep track of all of small-data externs so that mips_file_end can emit the appropriate declarations for them. In most cases it would be safe (though pointless) to emit .externs for other symbols too. One exception is when an object is within the -G limit but declared by the user to be in a section other than .sbss or .sdata. */ int mips_output_external (FILE *file ATTRIBUTE_UNUSED, tree decl, const char *name) { register struct extern_list *p; if (!TARGET_EXPLICIT_RELOCS && mips_in_small_data_p (decl)) { p = (struct extern_list *) ggc_alloc (sizeof (struct extern_list)); p->next = extern_head; p->name = name; p->size = int_size_in_bytes (TREE_TYPE (decl)); extern_head = p; } if (TARGET_IRIX && mips_abi == ABI_32 && TREE_CODE (decl) == FUNCTION_DECL) { p = (struct extern_list *) ggc_alloc (sizeof (struct extern_list)); p->next = extern_head; p->name = name; p->size = -1; extern_head = p; } return 0; } #if TARGET_IRIX static void irix_output_external_libcall (rtx fun) { register struct extern_list *p; if (mips_abi == ABI_32) { p = (struct extern_list *) ggc_alloc (sizeof (struct extern_list)); p->next = extern_head; p->name = XSTR (fun, 0); p->size = -1; extern_head = p; } } #endif /* Emit a new filename to a stream. If we are smuggling stabs, try to put out a MIPS ECOFF file and a stab. */ void mips_output_filename (FILE *stream, const char *name) { /* If we are emitting DWARF-2, let dwarf2out handle the ".file" directives. */ if (write_symbols == DWARF2_DEBUG) return; else if (mips_output_filename_first_time) { mips_output_filename_first_time = 0; num_source_filenames += 1; current_function_file = name; fprintf (stream, "\t.file\t%d ", num_source_filenames); output_quoted_string (stream, name); putc ('\n', stream); } /* If we are emitting stabs, let dbxout.c handle this (except for the mips_output_filename_first_time case). */ else if (write_symbols == DBX_DEBUG) return; else if (name != current_function_file && strcmp (name, current_function_file) != 0) { num_source_filenames += 1; current_function_file = name; fprintf (stream, "\t.file\t%d ", num_source_filenames); output_quoted_string (stream, name); putc ('\n', stream); } } /* Output an ASCII string, in a space-saving way. PREFIX is the string that should be written before the opening quote, such as "\t.ascii\t" for real string data or "\t# " for a comment. */ void mips_output_ascii (FILE *stream, const char *string_param, size_t len, const char *prefix) { size_t i; int cur_pos = 17; register const unsigned char *string = (const unsigned char *)string_param; fprintf (stream, "%s\"", prefix); for (i = 0; i < len; i++) { register int c = string[i]; if (ISPRINT (c)) { if (c == '\\' || c == '\"') { putc ('\\', stream); cur_pos++; } 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%s\"", prefix); } } fprintf (stream, "\"\n"); } /* Implement TARGET_ASM_FILE_START. */ static void mips_file_start (void) { default_file_start (); if (!TARGET_IRIX) { /* Generate a special section to describe the ABI switches used to produce the resultant binary. This used to be done by the assembler setting bits in the ELF header's flags field, but we have run out of bits. GDB needs this information in order to be able to correctly debug these binaries. See the function mips_gdbarch_init() in gdb/mips-tdep.c. This is unnecessary for the IRIX 5/6 ABIs and causes unnecessary IRIX 6 ld warnings. */ 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; default: gcc_unreachable (); } /* Note - we use fprintf directly rather than calling switch_to_section because in this way we can avoid creating an allocated section. We do not want this section to take up any space in the running executable. */ fprintf (asm_out_file, "\t.section .mdebug.%s\n", abi_string); /* There is no ELF header flag to distinguish long32 forms of the EABI from long64 forms. Emit a special section to help tools such as GDB. Do the same for o64, which is sometimes used with -mlong64. */ if (mips_abi == ABI_EABI || mips_abi == ABI_O64) fprintf (asm_out_file, "\t.section .gcc_compiled_long%d\n", TARGET_LONG64 ? 64 : 32); /* Restore the default section. */ fprintf (asm_out_file, "\t.previous\n"); } /* Generate the pseudo ops that System V.4 wants. */ if (TARGET_ABICALLS) fprintf (asm_out_file, "\t.abicalls\n"); if (TARGET_MIPS16) fprintf (asm_out_file, "\t.set\tmips16\n"); if (flag_verbose_asm) fprintf (asm_out_file, "\n%s -G value = %d, Arch = %s, ISA = %d\n", ASM_COMMENT_START, mips_section_threshold, mips_arch_info->name, mips_isa); } #ifdef BSS_SECTION_ASM_OP /* Implement ASM_OUTPUT_ALIGNED_BSS. This differs from the default only in the use of sbss. */ void mips_output_aligned_bss (FILE *stream, tree decl, const char *name, unsigned HOST_WIDE_INT size, int align) { extern tree last_assemble_variable_decl; if (mips_in_small_data_p (decl)) switch_to_section (get_named_section (NULL, ".sbss", 0)); else switch_to_section (bss_section); ASM_OUTPUT_ALIGN (stream, floor_log2 (align / BITS_PER_UNIT)); last_assemble_variable_decl = decl; ASM_DECLARE_OBJECT_NAME (stream, name, decl); ASM_OUTPUT_SKIP (stream, size != 0 ? size : 1); } #endif /* Implement TARGET_ASM_FILE_END. When using assembler macros, emit .externs for any small-data variables that turned out to be external. */ static void mips_file_end (void) { tree name_tree; struct extern_list *p; if (extern_head) { fputs ("\n", asm_out_file); for (p = extern_head; p != 0; p = p->next) { name_tree = get_identifier (p->name); /* Positively ensure only one .extern for any given symbol. */ if (!TREE_ASM_WRITTEN (name_tree) && TREE_SYMBOL_REFERENCED (name_tree)) { TREE_ASM_WRITTEN (name_tree) = 1; /* In IRIX 5 or IRIX 6 for the O32 ABI, we must output a `.global name .text' directive for every used but undefined function. If we don't, the linker may perform an optimization (skipping over the insns that set $gp) when it is unsafe. */ if (TARGET_IRIX && mips_abi == ABI_32 && p->size == -1) { fputs ("\t.globl ", asm_out_file); assemble_name (asm_out_file, p->name); fputs (" .text\n", asm_out_file); } else { fputs ("\t.extern\t", asm_out_file); assemble_name (asm_out_file, p->name); fprintf (asm_out_file, ", %d\n", p->size); } } } } } /* Implement ASM_OUTPUT_ALIGNED_DECL_COMMON. This is usually the same as the elfos.h version, but we also need to handle -muninit-const-in-rodata. */ void mips_output_aligned_decl_common (FILE *stream, tree decl, const char *name, unsigned HOST_WIDE_INT size, unsigned int align) { /* If the target wants uninitialized const declarations in .rdata then don't put them in .comm. */ if (TARGET_EMBEDDED_DATA && TARGET_UNINIT_CONST_IN_RODATA && TREE_CODE (decl) == VAR_DECL && TREE_READONLY (decl) && (DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node)) { if (TREE_PUBLIC (decl) && DECL_NAME (decl)) targetm.asm_out.globalize_label (stream, name); switch_to_section (readonly_data_section); ASM_OUTPUT_ALIGN (stream, floor_log2 (align / BITS_PER_UNIT)); mips_declare_object (stream, name, "", ":\n\t.space\t" HOST_WIDE_INT_PRINT_UNSIGNED "\n", size); } else mips_declare_common_object (stream, name, "\n\t.comm\t", size, align, true); } /* Declare a common object of SIZE bytes using asm directive INIT_STRING. NAME is the name of the object and ALIGN is the required alignment in bytes. TAKES_ALIGNMENT_P is true if the directive takes a third alignment argument. */ void mips_declare_common_object (FILE *stream, const char *name, const char *init_string, unsigned HOST_WIDE_INT size, unsigned int align, bool takes_alignment_p) { if (!takes_alignment_p) { size += (align / BITS_PER_UNIT) - 1; size -= size % (align / BITS_PER_UNIT); mips_declare_object (stream, name, init_string, "," HOST_WIDE_INT_PRINT_UNSIGNED "\n", size); } else mips_declare_object (stream, name, init_string, "," HOST_WIDE_INT_PRINT_UNSIGNED ",%u\n", size, align / BITS_PER_UNIT); } /* Emit either a label, .comm, or .lcomm directive. When using assembler macros, mark the symbol as written so that mips_file_end won't emit an .extern for it. STREAM is the output file, NAME is the name of the symbol, INIT_STRING is the string that should be written before the symbol and FINAL_STRING is the string that should be written after it. FINAL_STRING is a printf() format that consumes the remaining arguments. */ void mips_declare_object (FILE *stream, const char *name, const char *init_string, const char *final_string, ...) { va_list ap; fputs (init_string, stream); assemble_name (stream, name); va_start (ap, final_string); vfprintf (stream, final_string, ap); va_end (ap); if (!TARGET_EXPLICIT_RELOCS) { tree name_tree = get_identifier (name); TREE_ASM_WRITTEN (name_tree) = 1; } } #ifdef ASM_OUTPUT_SIZE_DIRECTIVE extern int size_directive_output; /* Implement ASM_DECLARE_OBJECT_NAME. This is like most of the standard ELF definitions except that it uses mips_declare_object() to emit the label. */ void mips_declare_object_name (FILE *stream, const char *name, tree decl ATTRIBUTE_UNUSED) { #ifdef ASM_OUTPUT_TYPE_DIRECTIVE ASM_OUTPUT_TYPE_DIRECTIVE (stream, name, "object"); #endif size_directive_output = 0; if (!flag_inhibit_size_directive && DECL_SIZE (decl)) { HOST_WIDE_INT size; size_directive_output = 1; size = int_size_in_bytes (TREE_TYPE (decl)); ASM_OUTPUT_SIZE_DIRECTIVE (stream, name, size); } mips_declare_object (stream, name, "", ":\n"); } /* Implement ASM_FINISH_DECLARE_OBJECT. This is generic ELF stuff. */ void mips_finish_declare_object (FILE *stream, tree decl, int top_level, int at_end) { const char *name; name = XSTR (XEXP (DECL_RTL (decl), 0), 0); if (!flag_inhibit_size_directive && DECL_SIZE (decl) != 0 && !at_end && top_level && DECL_INITIAL (decl) == error_mark_node && !size_directive_output) { HOST_WIDE_INT size; size_directive_output = 1; size = int_size_in_bytes (TREE_TYPE (decl)); ASM_OUTPUT_SIZE_DIRECTIVE (stream, name, size); } } #endif /* Return true if X is a small data address that can be rewritten as a LO_SUM. */ static bool mips_rewrite_small_data_p (rtx x) { enum mips_symbol_type symbol_type; return (TARGET_EXPLICIT_RELOCS && mips_symbolic_constant_p (x, &symbol_type) && symbol_type == SYMBOL_SMALL_DATA); } /* A for_each_rtx callback for mips_small_data_pattern_p. */ static int mips_small_data_pattern_1 (rtx *loc, void *data ATTRIBUTE_UNUSED) { if (GET_CODE (*loc) == LO_SUM) return -1; return mips_rewrite_small_data_p (*loc); } /* Return true if OP refers to small data symbols directly, not through a LO_SUM. */ bool mips_small_data_pattern_p (rtx op) { return for_each_rtx (&op, mips_small_data_pattern_1, 0); } /* A for_each_rtx callback, used by mips_rewrite_small_data. */ static int mips_rewrite_small_data_1 (rtx *loc, void *data ATTRIBUTE_UNUSED) { if (mips_rewrite_small_data_p (*loc)) *loc = gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, *loc); if (GET_CODE (*loc) == LO_SUM) return -1; return 0; } /* If possible, rewrite OP so that it refers to small data using explicit relocations. */ rtx mips_rewrite_small_data (rtx op) { op = copy_insn (op); for_each_rtx (&op, mips_rewrite_small_data_1, 0); return op; } /* Return true if the current function has an insn that implicitly refers to $gp. */ static bool mips_function_has_gp_insn (void) { /* Don't bother rechecking if we found one last time. */ if (!cfun->machine->has_gp_insn_p) { rtx insn; push_topmost_sequence (); for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) if (INSN_P (insn) && GET_CODE (PATTERN (insn)) != USE && GET_CODE (PATTERN (insn)) != CLOBBER && (get_attr_got (insn) != GOT_UNSET || small_data_pattern (PATTERN (insn), VOIDmode))) break; pop_topmost_sequence (); cfun->machine->has_gp_insn_p = (insn != 0); } return cfun->machine->has_gp_insn_p; } /* Return the register that should be used as the global pointer within this function. Return 0 if the function doesn't need a global pointer. */ static unsigned int mips_global_pointer (void) { unsigned int regno; /* $gp is always available in non-abicalls code. */ if (!TARGET_ABICALLS) return GLOBAL_POINTER_REGNUM; /* We must always provide $gp when it is used implicitly. */ if (!TARGET_EXPLICIT_RELOCS) return GLOBAL_POINTER_REGNUM; /* FUNCTION_PROFILER includes a jal macro, so we need to give it a valid gp. */ if (current_function_profile) return GLOBAL_POINTER_REGNUM; /* If the function has a nonlocal goto, $gp must hold the correct global pointer for the target function. */ if (current_function_has_nonlocal_goto) return GLOBAL_POINTER_REGNUM; /* If the gp is never referenced, there's no need to initialize it. Note that reload can sometimes introduce constant pool references into a function that otherwise didn't need them. For example, suppose we have an instruction like: (set (reg:DF R1) (float:DF (reg:SI R2))) If R2 turns out to be constant such as 1, the instruction may have a REG_EQUAL note saying that R1 == 1.0. Reload then has the option of using this constant if R2 doesn't get allocated to a register. In cases like these, reload will have added the constant to the pool but no instruction will yet refer to it. */ if (!regs_ever_live[GLOBAL_POINTER_REGNUM] && !current_function_uses_const_pool && !mips_function_has_gp_insn ()) return 0; /* We need a global pointer, but perhaps we can use a call-clobbered register instead of $gp. */ if (TARGET_NEWABI && current_function_is_leaf) for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) if (!regs_ever_live[regno] && call_used_regs[regno] && !fixed_regs[regno] && regno != PIC_FUNCTION_ADDR_REGNUM) return regno; return GLOBAL_POINTER_REGNUM; } /* Return true if the current function must save REGNO. */ static bool mips_save_reg_p (unsigned int regno) { /* We only need to save $gp for NewABI PIC. */ if (regno == GLOBAL_POINTER_REGNUM) return (TARGET_ABICALLS && TARGET_NEWABI && cfun->machine->global_pointer == regno); /* Check call-saved registers. */ if (regs_ever_live[regno] && !call_used_regs[regno]) return true; /* We need to save the old frame pointer before setting up a new one. */ if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed) return true; /* We need to save the incoming return address if it is ever clobbered within the function. */ if (regno == GP_REG_FIRST + 31 && regs_ever_live[regno]) return true; if (TARGET_MIPS16) { tree return_type; return_type = DECL_RESULT (current_function_decl); /* $18 is a special case in mips16 code. It may be used to call a function which returns a floating point value, but it is marked in call_used_regs. */ if (regno == GP_REG_FIRST + 18 && regs_ever_live[regno]) return true; /* $31 is also a special case. It will be used to copy a return value into the floating point registers if the return value is floating point. */ if (regno == GP_REG_FIRST + 31 && mips16_hard_float && !aggregate_value_p (return_type, current_function_decl) && GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT && GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE) return true; } return false; } /* Return the bytes needed to compute the frame pointer from the current stack pointer. SIZE is the size (in bytes) of the local variables. MIPS stack frames look like: Before call After call +-----------------------+ +-----------------------+ high | | | | mem. | | | | | caller's temps. | | caller's temps. | | | | | +-----------------------+ +-----------------------+ | | | | | arguments on stack. | | arguments on stack. | | | | | +-----------------------+ +-----------------------+ | 4 words to save | | 4 words to save | | arguments passed | | arguments passed | | in registers, even | | in registers, even | SP->| if not passed. | VFP->| if not passed. | +-----------------------+ +-----------------------+ | | | fp register save | | | +-----------------------+ | | | gp register save | | | +-----------------------+ | | | local variables | | | +-----------------------+ | | | alloca allocations | | | +-----------------------+ | | | GP save for V.4 abi | | | +-----------------------+ | | | arguments on stack | | | +-----------------------+ | 4 words to save | | arguments passed | | in registers, even | low SP->| if not passed. | memory +-----------------------+ */ HOST_WIDE_INT compute_frame_size (HOST_WIDE_INT size) { unsigned int regno; HOST_WIDE_INT total_size; /* # bytes that the entire frame takes up */ HOST_WIDE_INT var_size; /* # bytes that variables take up */ HOST_WIDE_INT args_size; /* # bytes that outgoing arguments take up */ HOST_WIDE_INT cprestore_size; /* # bytes that the cprestore slot takes up */ HOST_WIDE_INT gp_reg_rounded; /* # bytes needed to store gp after rounding */ HOST_WIDE_INT gp_reg_size; /* # bytes needed to store gp regs */ HOST_WIDE_INT fp_reg_size; /* # bytes needed to store fp regs */ unsigned int mask; /* mask of saved gp registers */ unsigned int fmask; /* mask of saved fp registers */ cfun->machine->global_pointer = mips_global_pointer (); gp_reg_size = 0; fp_reg_size = 0; mask = 0; fmask = 0; var_size = MIPS_STACK_ALIGN (size); args_size = current_function_outgoing_args_size; cprestore_size = MIPS_STACK_ALIGN (STARTING_FRAME_OFFSET) - args_size; /* The space set aside by STARTING_FRAME_OFFSET isn't needed in leaf functions. If the function has local variables, we're committed to allocating it anyway. Otherwise reclaim it here. */ if (var_size == 0 && current_function_is_leaf) cprestore_size = args_size = 0; /* The MIPS 3.0 linker does not like functions that dynamically allocate the stack and have 0 for STACK_DYNAMIC_OFFSET, since it looks like we are trying to create a second frame pointer to the function, so allocate some stack space to make it happy. */ if (args_size == 0 && current_function_calls_alloca) args_size = 4 * UNITS_PER_WORD; total_size = var_size + args_size + cprestore_size; /* Calculate space needed for gp registers. */ for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) if (mips_save_reg_p (regno)) { gp_reg_size += GET_MODE_SIZE (gpr_mode); mask |= 1 << (regno - GP_REG_FIRST); } /* We need to restore these for the handler. */ 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 |= 1 << (regno - GP_REG_FIRST); } } /* This loop must iterate over the same space as its companion in save_restore_insns. */ for (regno = (FP_REG_LAST - FP_INC + 1); regno >= FP_REG_FIRST; regno -= FP_INC) { if (mips_save_reg_p (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); /* Add in the space required for saving incoming register arguments. */ total_size += current_function_pretend_args_size; total_size += MIPS_STACK_ALIGN (cfun->machine->varargs_size); /* Save other computed information. */ cfun->machine->frame.total_size = total_size; cfun->machine->frame.var_size = var_size; cfun->machine->frame.args_size = args_size; cfun->machine->frame.cprestore_size = cprestore_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) { HOST_WIDE_INT offset; offset = (args_size + cprestore_size + var_size + gp_reg_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) { HOST_WIDE_INT offset; offset = (args_size + cprestore_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; } /* Ok, we're done. */ return total_size; } /* Implement INITIAL_ELIMINATION_OFFSET. FROM is either the frame pointer or argument pointer. TO is either the stack pointer or hard frame pointer. */ HOST_WIDE_INT mips_initial_elimination_offset (int from, int to) { HOST_WIDE_INT offset; compute_frame_size (get_frame_size ()); /* Set OFFSET to the offset from the stack pointer. */ switch (from) { case FRAME_POINTER_REGNUM: offset = 0; break; case ARG_POINTER_REGNUM: offset = (cfun->machine->frame.total_size - current_function_pretend_args_size); break; default: gcc_unreachable (); } if (TARGET_MIPS16 && to == HARD_FRAME_POINTER_REGNUM) offset -= cfun->machine->frame.args_size; return offset; } /* Implement RETURN_ADDR_RTX. Note, we do not support moving back to a previous frame. */ rtx mips_return_addr (int count, rtx frame ATTRIBUTE_UNUSED) { if (count != 0) return const0_rtx; return get_hard_reg_initial_val (Pmode, GP_REG_FIRST + 31); } /* Use FN to save or restore register REGNO. MODE is the register's mode and OFFSET is the offset of its save slot from the current stack pointer. */ static void mips_save_restore_reg (enum machine_mode mode, int regno, HOST_WIDE_INT offset, mips_save_restore_fn fn) { rtx mem; mem = gen_frame_mem (mode, plus_constant (stack_pointer_rtx, offset)); fn (gen_rtx_REG (mode, regno), mem); } /* Call FN for each register that is saved by the current function. SP_OFFSET is the offset of the current stack pointer from the start of the frame. */ static void mips_for_each_saved_reg (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn) { #define BITSET_P(VALUE, BIT) (((VALUE) & (1L << (BIT))) != 0) enum machine_mode fpr_mode; HOST_WIDE_INT offset; int regno; /* Save registers starting from high to low. The debuggers prefer at least the return register be stored at func+4, and also it allows us not to need a nop in the epilog if at least one register is reloaded in addition to return address. */ offset = cfun->machine->frame.gp_sp_offset - sp_offset; for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--) if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST)) { mips_save_restore_reg (gpr_mode, regno, offset, fn); offset -= GET_MODE_SIZE (gpr_mode); } /* This loop must iterate over the same space as its companion in compute_frame_size. */ offset = cfun->machine->frame.fp_sp_offset - sp_offset; fpr_mode = (TARGET_SINGLE_FLOAT ? SFmode : DFmode); for (regno = (FP_REG_LAST - FP_INC + 1); regno >= FP_REG_FIRST; regno -= FP_INC) if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST)) { mips_save_restore_reg (fpr_mode, regno, offset, fn); offset -= GET_MODE_SIZE (fpr_mode); } #undef BITSET_P } /* If we're generating n32 or n64 abicalls, and the current function does not use $28 as its global pointer, emit a cplocal directive. Use pic_offset_table_rtx as the argument to the directive. */ static void mips_output_cplocal (void) { if (!TARGET_EXPLICIT_RELOCS && cfun->machine->global_pointer > 0 && cfun->machine->global_pointer != GLOBAL_POINTER_REGNUM) output_asm_insn (".cplocal %+", 0); } /* Return the style of GP load sequence that is being used for the current function. */ enum mips_loadgp_style mips_current_loadgp_style (void) { if (!TARGET_ABICALLS || cfun->machine->global_pointer == 0) return LOADGP_NONE; if (TARGET_ABSOLUTE_ABICALLS) return LOADGP_ABSOLUTE; return TARGET_NEWABI ? LOADGP_NEWABI : LOADGP_OLDABI; } /* The __gnu_local_gp symbol. */ static GTY(()) rtx mips_gnu_local_gp; /* If we're generating n32 or n64 abicalls, emit instructions to set up the global pointer. */ static void mips_emit_loadgp (void) { rtx addr, offset, incoming_address; switch (mips_current_loadgp_style ()) { case LOADGP_ABSOLUTE: if (mips_gnu_local_gp == NULL) { mips_gnu_local_gp = gen_rtx_SYMBOL_REF (Pmode, "__gnu_local_gp"); SYMBOL_REF_FLAGS (mips_gnu_local_gp) |= SYMBOL_FLAG_LOCAL; } emit_insn (gen_loadgp_noshared (mips_gnu_local_gp)); break; case LOADGP_NEWABI: addr = XEXP (DECL_RTL (current_function_decl), 0); offset = mips_unspec_address (addr, SYMBOL_GOTOFF_LOADGP); incoming_address = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM); emit_insn (gen_loadgp (offset, incoming_address)); if (!TARGET_EXPLICIT_RELOCS) emit_insn (gen_loadgp_blockage ()); break; default: break; } } /* Set up the stack and frame (if desired) for the function. */ static void mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) { const char *fnname; HOST_WIDE_INT tsize = cfun->machine->frame.total_size; #ifdef SDB_DEBUGGING_INFO if (debug_info_level != DINFO_LEVEL_TERSE && write_symbols == SDB_DEBUG) SDB_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl)); #endif /* In mips16 mode, we may need to generate a 32 bit to handle floating point arguments. The linker will arrange for any 32 bit functions to call this stub, which will then jump to the 16 bit function proper. */ if (TARGET_MIPS16 && !TARGET_SOFT_FLOAT && current_function_args_info.fp_code != 0) build_mips16_function_stub (file); if (!FUNCTION_NAME_ALREADY_DECLARED) { /* Get the function name the same way that toplev.c does before calling assemble_start_function. This is needed so that the name used here exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */ 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); } /* Stop mips_file_end from treating this function as external. */ if (TARGET_IRIX && mips_abi == ABI_32) TREE_ASM_WRITTEN (DECL_NAME (cfun->decl)) = 1; if (!flag_inhibit_size_directive) { /* .frame FRAMEREG, FRAMESIZE, RETREG */ fprintf (file, "\t.frame\t%s," HOST_WIDE_INT_PRINT_DEC ",%s\t\t" "# vars= " HOST_WIDE_INT_PRINT_DEC ", regs= %d/%d" ", args= " HOST_WIDE_INT_PRINT_DEC ", gp= " HOST_WIDE_INT_PRINT_DEC "\n", (reg_names[(frame_pointer_needed) ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM]), ((frame_pointer_needed && TARGET_MIPS16) ? tsize - cfun->machine->frame.args_size : tsize), reg_names[GP_REG_FIRST + 31], cfun->machine->frame.var_size, cfun->machine->frame.num_gp, cfun->machine->frame.num_fp, cfun->machine->frame.args_size, cfun->machine->frame.cprestore_size); /* .mask MASK, GPOFFSET; .fmask FPOFFSET */ fprintf (file, "\t.mask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n", cfun->machine->frame.mask, cfun->machine->frame.gp_save_offset); fprintf (file, "\t.fmask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n", cfun->machine->frame.fmask, cfun->machine->frame.fp_save_offset); /* Require: OLD_SP == *FRAMEREG + FRAMESIZE => can find old_sp from nominated FP reg. HIGHEST_GP_SAVED == *FRAMEREG + FRAMESIZE + GPOFFSET => can find saved regs. */ } if (mips_current_loadgp_style () == LOADGP_OLDABI) { /* Handle the initialization of $gp for SVR4 PIC. */ if (!cfun->machine->all_noreorder_p) output_asm_insn ("%(.cpload\t%^%)", 0); else output_asm_insn ("%(.cpload\t%^\n\t%<", 0); } else if (cfun->machine->all_noreorder_p) output_asm_insn ("%(%<", 0); /* Tell the assembler which register we're using as the global pointer. This is needed for thunks, since they can use either explicit relocs or assembler macros. */ mips_output_cplocal (); } /* Make the last instruction frame related and note that it performs the operation described by FRAME_PATTERN. */ static void mips_set_frame_expr (rtx frame_pattern) { rtx insn; insn = get_last_insn (); RTX_FRAME_RELATED_P (insn) = 1; REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR, frame_pattern, REG_NOTES (insn)); } /* Return a frame-related rtx that stores REG at MEM. REG must be a single register. */ static rtx mips_frame_set (rtx mem, rtx reg) { rtx set; /* If we're saving the return address register and the dwarf return address column differs from the hard register number, adjust the note reg to refer to the former. */ if (REGNO (reg) == GP_REG_FIRST + 31 && DWARF_FRAME_RETURN_COLUMN != GP_REG_FIRST + 31) reg = gen_rtx_REG (GET_MODE (reg), DWARF_FRAME_RETURN_COLUMN); set = gen_rtx_SET (VOIDmode, mem, reg); RTX_FRAME_RELATED_P (set) = 1; return set; } /* Save register REG to MEM. Make the instruction frame-related. */ static void mips_save_reg (rtx reg, rtx mem) { if (GET_MODE (reg) == DFmode && !TARGET_FLOAT64) { rtx x1, x2; if (mips_split_64bit_move_p (mem, reg)) mips_split_64bit_move (mem, reg); else emit_move_insn (mem, reg); x1 = mips_frame_set (mips_subword (mem, 0), mips_subword (reg, 0)); x2 = mips_frame_set (mips_subword (mem, 1), mips_subword (reg, 1)); mips_set_frame_expr (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x1, x2))); } else { if (TARGET_MIPS16 && REGNO (reg) != GP_REG_FIRST + 31 && !M16_REG_P (REGNO (reg))) { /* Save a non-mips16 register by moving it through a temporary. We don't need to do this for $31 since there's a special instruction for it. */ emit_move_insn (MIPS_PROLOGUE_TEMP (GET_MODE (reg)), reg); emit_move_insn (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg))); } else emit_move_insn (mem, reg); mips_set_frame_expr (mips_frame_set (mem, reg)); } } /* Expand the prologue into a bunch of separate insns. */ void mips_expand_prologue (void) { HOST_WIDE_INT size; if (cfun->machine->global_pointer > 0) REGNO (pic_offset_table_rtx) = cfun->machine->global_pointer; size = compute_frame_size (get_frame_size ()); /* Save the registers. Allocate up to MIPS_MAX_FIRST_STACK_STEP bytes beforehand; this is enough to cover the register save area without going out of range. */ if ((cfun->machine->frame.mask | cfun->machine->frame.fmask) != 0) { HOST_WIDE_INT step1; step1 = MIN (size, MIPS_MAX_FIRST_STACK_STEP); RTX_FRAME_RELATED_P (emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (-step1)))) = 1; size -= step1; mips_for_each_saved_reg (size, mips_save_reg); } /* Allocate the rest of the frame. */ if (size > 0) { if (SMALL_OPERAND (-size)) RTX_FRAME_RELATED_P (emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (-size)))) = 1; else { emit_move_insn (MIPS_PROLOGUE_TEMP (Pmode), GEN_INT (size)); if (TARGET_MIPS16) { /* There are no instructions to add or subtract registers from the stack pointer, so use the frame pointer as a temporary. We should always be using a frame pointer in this case anyway. */ gcc_assert (frame_pointer_needed); emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx); emit_insn (gen_sub3_insn (hard_frame_pointer_rtx, hard_frame_pointer_rtx, MIPS_PROLOGUE_TEMP (Pmode))); emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx); } else emit_insn (gen_sub3_insn (stack_pointer_rtx, stack_pointer_rtx, MIPS_PROLOGUE_TEMP (Pmode))); /* Describe the combined effect of the previous instructions. */ mips_set_frame_expr (gen_rtx_SET (VOIDmode, stack_pointer_rtx, plus_constant (stack_pointer_rtx, -size))); } } /* Set up the frame pointer, if we're using one. In mips16 code, we point the frame pointer ahead of the outgoing argument area. This should allow more variables & incoming arguments to be accessed with unextended instructions. */ if (frame_pointer_needed) { if (TARGET_MIPS16 && cfun->machine->frame.args_size != 0) { rtx offset = GEN_INT (cfun->machine->frame.args_size); if (SMALL_OPERAND (cfun->machine->frame.args_size)) RTX_FRAME_RELATED_P (emit_insn (gen_add3_insn (hard_frame_pointer_rtx, stack_pointer_rtx, offset))) = 1; else { emit_move_insn (MIPS_PROLOGUE_TEMP (Pmode), offset); emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx); emit_insn (gen_add3_insn (hard_frame_pointer_rtx, hard_frame_pointer_rtx, MIPS_PROLOGUE_TEMP (Pmode))); mips_set_frame_expr (gen_rtx_SET (VOIDmode, hard_frame_pointer_rtx, plus_constant (stack_pointer_rtx, cfun->machine->frame.args_size))); } } else RTX_FRAME_RELATED_P (emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx)) = 1; } mips_emit_loadgp (); /* If generating o32/o64 abicalls, save $gp on the stack. */ if (TARGET_ABICALLS && !TARGET_NEWABI && !current_function_is_leaf) emit_insn (gen_cprestore (GEN_INT (current_function_outgoing_args_size))); /* If we are profiling, make sure no instructions are scheduled before the call to mcount. */ if (current_function_profile) emit_insn (gen_blockage ()); } /* Do any necessary cleanup after a function to restore stack, frame, and regs. */ #define RA_MASK BITMASK_HIGH /* 1 << 31 */ static void mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED, HOST_WIDE_INT size ATTRIBUTE_UNUSED) { /* Reinstate the normal $gp. */ REGNO (pic_offset_table_rtx) = GLOBAL_POINTER_REGNUM; mips_output_cplocal (); if (cfun->machine->all_noreorder_p) { /* Avoid using %>%) since it adds excess whitespace. */ output_asm_insn (".set\tmacro", 0); output_asm_insn (".set\treorder", 0); set_noreorder = set_nomacro = 0; } if (!FUNCTION_NAME_ALREADY_DECLARED && !flag_inhibit_size_directive) { const char *fnname; /* Get the function name the same way that toplev.c does before calling assemble_start_function. This is needed so that the name used here exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */ fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); fputs ("\t.end\t", file); assemble_name (file, fnname); fputs ("\n", file); } } /* Emit instructions to restore register REG from slot MEM. */ static void mips_restore_reg (rtx reg, rtx mem) { /* There's no mips16 instruction to load $31 directly. Load into $7 instead and adjust the return insn appropriately. */ if (TARGET_MIPS16 && REGNO (reg) == GP_REG_FIRST + 31) reg = gen_rtx_REG (GET_MODE (reg), 7); if (TARGET_MIPS16 && !M16_REG_P (REGNO (reg))) { /* Can't restore directly; move through a temporary. */ emit_move_insn (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem); emit_move_insn (reg, MIPS_EPILOGUE_TEMP (GET_MODE (reg))); } else emit_move_insn (reg, mem); } /* Expand the epilogue into a bunch of separate insns. SIBCALL_P is true if this epilogue precedes a sibling call, false if it is for a normal "epilogue" pattern. */ void mips_expand_epilogue (int sibcall_p) { HOST_WIDE_INT step1, step2; rtx base, target; if (!sibcall_p && mips_can_use_return_insn ()) { emit_jump_insn (gen_return ()); return; } /* Split the frame into two. STEP1 is the amount of stack we should deallocate before restoring the registers. STEP2 is the amount we should deallocate afterwards. Start off by assuming that no registers need to be restored. */ step1 = cfun->machine->frame.total_size; step2 = 0; /* Work out which register holds the frame address. Account for the frame pointer offset used by mips16 code. */ if (!frame_pointer_needed) base = stack_pointer_rtx; else { base = hard_frame_pointer_rtx; if (TARGET_MIPS16) step1 -= cfun->machine->frame.args_size; } /* If we need to restore registers, deallocate as much stack as possible in the second step without going out of range. */ if ((cfun->machine->frame.mask | cfun->machine->frame.fmask) != 0) { step2 = MIN (step1, MIPS_MAX_FIRST_STACK_STEP); step1 -= step2; } /* Set TARGET to BASE + STEP1. */ target = base; if (step1 > 0) { rtx adjust; /* Get an rtx for STEP1 that we can add to BASE. */ adjust = GEN_INT (step1); if (!SMALL_OPERAND (step1)) { emit_move_insn (MIPS_EPILOGUE_TEMP (Pmode), adjust); adjust = MIPS_EPILOGUE_TEMP (Pmode); } /* Normal mode code can copy the result straight into $sp. */ if (!TARGET_MIPS16) target = stack_pointer_rtx; emit_insn (gen_add3_insn (target, base, adjust)); } /* Copy TARGET into the stack pointer. */ if (target != stack_pointer_rtx) emit_move_insn (stack_pointer_rtx, target); /* If we're using addressing macros for n32/n64 abicalls, $gp is implicitly used by all SYMBOL_REFs. We must emit a blockage insn before restoring it. */ if (TARGET_ABICALLS && TARGET_NEWABI && !TARGET_EXPLICIT_RELOCS) emit_insn (gen_blockage ()); /* Restore the registers. */ mips_for_each_saved_reg (cfun->machine->frame.total_size - step2, mips_restore_reg); /* Deallocate the final bit of the frame. */ if (step2 > 0) emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (step2))); /* Add in the __builtin_eh_return stack adjustment. We need to use a temporary in mips16 code. */ if (current_function_calls_eh_return) { if (TARGET_MIPS16) { emit_move_insn (MIPS_EPILOGUE_TEMP (Pmode), stack_pointer_rtx); emit_insn (gen_add3_insn (MIPS_EPILOGUE_TEMP (Pmode), MIPS_EPILOGUE_TEMP (Pmode), EH_RETURN_STACKADJ_RTX)); emit_move_insn (stack_pointer_rtx, MIPS_EPILOGUE_TEMP (Pmode)); } else emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, EH_RETURN_STACKADJ_RTX)); } if (!sibcall_p) { /* The mips16 loads the return address into $7, not $31. */ 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))); } } /* Return nonzero if this function is known to have a null epilogue. This allows the optimizer to omit jumps to jumps if no stack was created. */ int mips_can_use_return_insn (void) { 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); /* In mips16 mode, a function which returns a floating point value needs to arrange to copy the return value into the floating point registers. */ if (TARGET_MIPS16 && mips16_hard_float && ! aggregate_value_p (return_type, current_function_decl) && 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; } /* Implement TARGET_ASM_OUTPUT_MI_THUNK. Generate rtl rather than asm text in order to avoid duplicating too much logic from elsewhere. */ static void mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, tree function) { rtx this, temp1, temp2, insn, fnaddr; /* Pretend to be a post-reload pass while generating rtl. */ no_new_pseudos = 1; reload_completed = 1; reset_block_changes (); /* Pick a global pointer for -mabicalls. Use $15 rather than $28 for TARGET_NEWABI since the latter is a call-saved register. */ if (TARGET_ABICALLS) cfun->machine->global_pointer = REGNO (pic_offset_table_rtx) = TARGET_NEWABI ? 15 : GLOBAL_POINTER_REGNUM; /* Set up the global pointer for n32 or n64 abicalls. */ mips_emit_loadgp (); /* We need two temporary registers in some cases. */ temp1 = gen_rtx_REG (Pmode, 2); temp2 = gen_rtx_REG (Pmode, 3); /* Find out which register contains the "this" pointer. */ if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) this = gen_rtx_REG (Pmode, GP_ARG_FIRST + 1); else this = gen_rtx_REG (Pmode, GP_ARG_FIRST); /* Add DELTA to THIS. */ if (delta != 0) { rtx offset = GEN_INT (delta); if (!SMALL_OPERAND (delta)) { emit_move_insn (temp1, offset); offset = temp1; } emit_insn (gen_add3_insn (this, this, offset)); } /* If needed, add *(*THIS + VCALL_OFFSET) to THIS. */ if (vcall_offset != 0) { rtx addr; /* Set TEMP1 to *THIS. */ emit_move_insn (temp1, gen_rtx_MEM (Pmode, this)); /* Set ADDR to a legitimate address for *THIS + VCALL_OFFSET. */ addr = mips_add_offset (temp2, temp1, vcall_offset); /* Load the offset and add it to THIS. */ emit_move_insn (temp1, gen_rtx_MEM (Pmode, addr)); emit_insn (gen_add3_insn (this, this, temp1)); } /* Jump to the target function. Use a sibcall if direct jumps are allowed, otherwise load the address into a register first. */ fnaddr = XEXP (DECL_RTL (function), 0); if (TARGET_MIPS16 || TARGET_ABICALLS || TARGET_LONG_CALLS) { /* This is messy. gas treats "la $25,foo" as part of a call sequence and may allow a global "foo" to be lazily bound. The general move patterns therefore reject this combination. In this context, lazy binding would actually be OK for o32 and o64, but it's still wrong for n32 and n64; see mips_load_call_address. We must therefore load the address via a temporary register if mips_dangerous_for_la25_p. If we jump to the temporary register rather than $25, the assembler can use the move insn to fill the jump's delay slot. */ if (TARGET_ABICALLS && !mips_dangerous_for_la25_p (fnaddr)) temp1 = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM); mips_load_call_address (temp1, fnaddr, true); if (TARGET_ABICALLS && REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM) emit_move_insn (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1); emit_jump_insn (gen_indirect_jump (temp1)); } else { insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx)); SIBLING_CALL_P (insn) = 1; } /* Run just enough of rest_of_compilation. This sequence was "borrowed" from alpha.c. */ insn = get_insns (); insn_locators_initialize (); split_all_insns_noflow (); if (TARGET_MIPS16) mips16_lay_out_constants (); shorten_branches (insn); final_start_function (insn, file, 1); final (insn, file, 1); final_end_function (); /* Clean up the vars set above. Note that final_end_function resets the global pointer for us. */ reload_completed = 0; no_new_pseudos = 0; } /* Returns nonzero if X contains a SYMBOL_REF. */ static int symbolic_expression_p (rtx x) { if (GET_CODE (x) == SYMBOL_REF) return 1; if (GET_CODE (x) == CONST) return symbolic_expression_p (XEXP (x, 0)); if (UNARY_P (x)) return symbolic_expression_p (XEXP (x, 0)); if (ARITHMETIC_P (x)) return (symbolic_expression_p (XEXP (x, 0)) || symbolic_expression_p (XEXP (x, 1))); return 0; } /* Choose the section to use for the constant rtx expression X that has mode MODE. */ static section * mips_select_rtx_section (enum machine_mode mode, rtx x, unsigned HOST_WIDE_INT align) { if (TARGET_MIPS16) { /* In mips16 mode, the constant table always goes in the same section as the function, so that constants can be loaded using PC relative addressing. */ return function_section (current_function_decl); } else if (TARGET_EMBEDDED_DATA) { /* For embedded applications, always put constants in read-only data, in order to reduce RAM usage. */ return mergeable_constant_section (mode, align, 0); } else { /* For hosted applications, always put constants in small data if possible, as this gives the best performance. */ /* ??? Consider using mergeable small data sections. */ if (GET_MODE_SIZE (mode) <= (unsigned) mips_section_threshold && mips_section_threshold > 0) return get_named_section (NULL, ".sdata", 0); else if (flag_pic && symbolic_expression_p (x)) return get_named_section (NULL, ".data.rel.ro", 3); else return mergeable_constant_section (mode, align, 0); } } /* Implement TARGET_ASM_FUNCTION_RODATA_SECTION. The complication here is that, with the combination TARGET_ABICALLS && !TARGET_GPWORD, jump tables will use absolute addresses, and should therefore not be included in the read-only part of a DSO. Handle such cases by selecting a normal data section instead of a read-only one. The logic apes that in default_function_rodata_section. */ static section * mips_function_rodata_section (tree decl) { if (!TARGET_ABICALLS || TARGET_GPWORD) return default_function_rodata_section (decl); if (decl && DECL_SECTION_NAME (decl)) { const char *name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl)); if (DECL_ONE_ONLY (decl) && strncmp (name, ".gnu.linkonce.t.", 16) == 0) { char *rname = ASTRDUP (name); rname[14] = 'd'; return get_section (rname, SECTION_LINKONCE | SECTION_WRITE, decl); } else if (flag_function_sections && flag_data_sections && strncmp (name, ".text.", 6) == 0) { char *rname = ASTRDUP (name); memcpy (rname + 1, "data", 4); return get_section (rname, SECTION_WRITE, decl); } } return data_section; } /* Implement TARGET_IN_SMALL_DATA_P. This function controls whether locally-defined objects go in a small data section. It also controls the setting of the SYMBOL_REF_SMALL_P flag, which in turn helps mips_classify_symbol decide when to use %gp_rel(...)($gp) accesses. */ static bool mips_in_small_data_p (tree decl) { HOST_WIDE_INT size; if (TREE_CODE (decl) == STRING_CST || TREE_CODE (decl) == FUNCTION_DECL) return false; /* We don't yet generate small-data references for -mabicalls. See related -G handling in override_options. */ if (TARGET_ABICALLS) return false; if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl) != 0) { const char *name; /* Reject anything that isn't in a known small-data section. */ name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl)); if (strcmp (name, ".sdata") != 0 && strcmp (name, ".sbss") != 0) return false; /* If a symbol is defined externally, the assembler will use the usual -G rules when deciding how to implement macros. */ if (TARGET_EXPLICIT_RELOCS || !DECL_EXTERNAL (decl)) return true; } else if (TARGET_EMBEDDED_DATA) { /* Don't put constants into the small data section: we want them to be in ROM rather than RAM. */ if (TREE_CODE (decl) != VAR_DECL) return false; if (TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl) && (!DECL_INITIAL (decl) || TREE_CONSTANT (DECL_INITIAL (decl)))) return false; } size = int_size_in_bytes (TREE_TYPE (decl)); return (size > 0 && size <= mips_section_threshold); } /* Implement TARGET_USE_ANCHORS_FOR_SYMBOL_P. We don't want to use anchors for small data: the GP register acts as an anchor in that case. We also don't want to use them for PC-relative accesses, where the PC acts as an anchor. */ static bool mips_use_anchors_for_symbol_p (rtx symbol) { switch (mips_classify_symbol (symbol)) { case SYMBOL_CONSTANT_POOL: case SYMBOL_SMALL_DATA: return false; default: return true; } } /* See whether VALTYPE is a record whose fields should be returned in floating-point registers. If so, return the number of fields and list them in FIELDS (which should have two elements). Return 0 otherwise. For n32 & n64, a structure with one or two fields is returned in floating-point registers as long as every field has a floating-point type. */ static int mips_fpr_return_fields (tree valtype, tree *fields) { tree field; int i; if (!TARGET_NEWABI) return 0; if (TREE_CODE (valtype) != RECORD_TYPE) return 0; i = 0; for (field = TYPE_FIELDS (valtype); field != 0; field = TREE_CHAIN (field)) { if (TREE_CODE (field) != FIELD_DECL) continue; if (TREE_CODE (TREE_TYPE (field)) != REAL_TYPE) return 0; if (i == 2) return 0; fields[i++] = field; } return i; } /* Implement TARGET_RETURN_IN_MSB. For n32 & n64, we should return a value in the most significant part of $2/$3 if: - the target is big-endian; - the value has a structure or union type (we generalize this to cover aggregates from other languages too); and - the structure is not returned in floating-point registers. */ static bool mips_return_in_msb (tree valtype) { tree fields[2]; return (TARGET_NEWABI && TARGET_BIG_ENDIAN && AGGREGATE_TYPE_P (valtype) && mips_fpr_return_fields (valtype, fields) == 0); } /* Return a composite value in a pair of floating-point registers. MODE1 and OFFSET1 are the mode and byte offset for the first value, likewise MODE2 and OFFSET2 for the second. MODE is the mode of the complete value. For n32 & n64, $f0 always holds the first value and $f2 the second. Otherwise the values are packed together as closely as possible. */ static rtx mips_return_fpr_pair (enum machine_mode mode, enum machine_mode mode1, HOST_WIDE_INT offset1, enum machine_mode mode2, HOST_WIDE_INT offset2) { int inc; inc = (TARGET_NEWABI ? 2 : FP_INC); return gen_rtx_PARALLEL (mode, gen_rtvec (2, gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (mode1, FP_RETURN), GEN_INT (offset1)), gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (mode2, FP_RETURN + inc), GEN_INT (offset2)))); } /* Implement FUNCTION_VALUE and LIBCALL_VALUE. For normal calls, VALTYPE is the return type and MODE is VOIDmode. For libcalls, VALTYPE is null and MODE is the mode of the return value. */ rtx mips_function_value (tree valtype, tree func ATTRIBUTE_UNUSED, enum machine_mode mode) { if (valtype) { tree fields[2]; int unsignedp; mode = TYPE_MODE (valtype); unsignedp = TYPE_UNSIGNED (valtype); /* Since we define TARGET_PROMOTE_FUNCTION_RETURN that returns true, we must promote the mode just as PROMOTE_MODE does. */ mode = promote_mode (valtype, mode, &unsignedp, 1); /* Handle structures whose fields are returned in $f0/$f2. */ switch (mips_fpr_return_fields (valtype, fields)) { case 1: return gen_rtx_REG (mode, FP_RETURN); case 2: return mips_return_fpr_pair (mode, TYPE_MODE (TREE_TYPE (fields[0])), int_byte_position (fields[0]), TYPE_MODE (TREE_TYPE (fields[1])), int_byte_position (fields[1])); } /* If a value is passed in the most significant part of a register, see whether we have to round the mode up to a whole number of words. */ if (mips_return_in_msb (valtype)) { HOST_WIDE_INT size = int_size_in_bytes (valtype); if (size % UNITS_PER_WORD != 0) { size += UNITS_PER_WORD - size % UNITS_PER_WORD; mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0); } } /* For EABI, the class of return register depends entirely on MODE. For example, "struct { some_type x; }" and "union { some_type x; }" are returned in the same way as a bare "some_type" would be. Other ABIs only use FPRs for scalar, complex or vector types. */ if (mips_abi != ABI_EABI && !FLOAT_TYPE_P (valtype)) return gen_rtx_REG (mode, GP_RETURN); } if ((GET_MODE_CLASS (mode) == MODE_FLOAT || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT) && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE) return gen_rtx_REG (mode, FP_RETURN); /* Handle long doubles for n32 & n64. */ if (mode == TFmode) return mips_return_fpr_pair (mode, DImode, 0, DImode, GET_MODE_SIZE (mode) / 2); if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE * 2) return mips_return_fpr_pair (mode, GET_MODE_INNER (mode), 0, GET_MODE_INNER (mode), GET_MODE_SIZE (mode) / 2); return gen_rtx_REG (mode, GP_RETURN); } /* Return nonzero when an argument must be passed by reference. */ static bool mips_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED, enum machine_mode mode, tree type, bool named ATTRIBUTE_UNUSED) { if (mips_abi == ABI_EABI) { int size; /* ??? How should SCmode be handled? */ if (mode == DImode || mode == DFmode) return 0; size = type ? int_size_in_bytes (type) : GET_MODE_SIZE (mode); return size == -1 || size > UNITS_PER_WORD; } else { /* If we have a variable-sized parameter, we have no choice. */ return targetm.calls.must_pass_in_stack (mode, type); } } static bool mips_callee_copies (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED, enum machine_mode mode ATTRIBUTE_UNUSED, tree type ATTRIBUTE_UNUSED, bool named) { return mips_abi == ABI_EABI && named; } /* Return true if registers of class CLASS cannot change from mode FROM to mode TO. */ bool mips_cannot_change_mode_class (enum machine_mode from, enum machine_mode to, enum reg_class class) { if (MIN (GET_MODE_SIZE (from), GET_MODE_SIZE (to)) <= UNITS_PER_WORD && MAX (GET_MODE_SIZE (from), GET_MODE_SIZE (to)) > UNITS_PER_WORD) { if (TARGET_BIG_ENDIAN) { /* When a multi-word value is stored in paired floating-point registers, the first register always holds the low word. We therefore can't allow FPRs to change between single-word and multi-word modes. */ if (FP_INC > 1 && reg_classes_intersect_p (FP_REGS, class)) return true; } else { /* LO_REGNO == HI_REGNO + 1, so if a multi-word value is stored in LO and HI, the high word always comes first. We therefore can't allow values stored in HI to change between single-word and multi-word modes. This rule applies to both the original HI/LO pair and the new DSP accumulators. */ if (reg_classes_intersect_p (ACC_REGS, class)) return true; } } /* Loading a 32-bit value into a 64-bit floating-point register will not sign-extend the value, despite what LOAD_EXTEND_OP says. We can't allow 64-bit float registers to change from SImode to to a wider mode. */ if (TARGET_FLOAT64 && from == SImode && GET_MODE_SIZE (to) >= UNITS_PER_WORD && reg_classes_intersect_p (FP_REGS, class)) return true; return false; } /* Return true if X should not be moved directly into register $25. We need this because many versions of GAS will treat "la $25,foo" as part of a call sequence and so allow a global "foo" to be lazily bound. */ bool mips_dangerous_for_la25_p (rtx x) { HOST_WIDE_INT offset; if (TARGET_EXPLICIT_RELOCS) return false; mips_split_const (x, &x, &offset); return global_got_operand (x, VOIDmode); } /* Implement PREFERRED_RELOAD_CLASS. */ enum reg_class mips_preferred_reload_class (rtx x, enum reg_class class) { if (mips_dangerous_for_la25_p (x) && reg_class_subset_p (LEA_REGS, class)) return LEA_REGS; if (TARGET_HARD_FLOAT && FLOAT_MODE_P (GET_MODE (x)) && reg_class_subset_p (FP_REGS, class)) return FP_REGS; if (reg_class_subset_p (GR_REGS, class)) class = GR_REGS; if (TARGET_MIPS16 && reg_class_subset_p (M16_REGS, class)) class = M16_REGS; return class; } /* This function returns the register class required for a secondary register when copying between one of the registers in CLASS, and X, using MODE. If IN_P is nonzero, the copy is going from X to the register, otherwise the register is the source. A return value of NO_REGS means that no secondary register is required. */ enum reg_class mips_secondary_reload_class (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 (REG_P (x)|| GET_CODE (x) == SUBREG) regno = true_regnum (x); gp_reg_p = TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno); if (mips_dangerous_for_la25_p (x)) { gr_regs = LEA_REGS; if (TEST_HARD_REG_BIT (reg_class_contents[(int) class], 25)) return gr_regs; } /* Copying from HI or LO to anywhere other than a general register requires a general register. This rule applies to both the original HI/LO pair and the new DSP accumulators. */ if (reg_class_subset_p (class, ACC_REGS)) { if (TARGET_MIPS16 && in_p) { /* We can't really copy to HI or LO at all in mips16 mode. */ return M16_REGS; } return gp_reg_p ? NO_REGS : gr_regs; } if (ACC_REG_P (regno)) { if (TARGET_MIPS16 && ! in_p) { /* We can't really copy to HI or LO at all in mips16 mode. */ return M16_REGS; } return class == gr_regs ? NO_REGS : gr_regs; } /* We can only copy a value to a condition code register from a floating point register, and even then we require a scratch floating point register. We can only copy a value out of a condition code register into a general register. */ if (class == ST_REGS) { if (in_p) return FP_REGS; return gp_reg_p ? 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 (MEM_P (x)) { /* In this case we can use lwc1, swc1, ldc1 or sdc1. */ return NO_REGS; } else if (CONSTANT_P (x) && GET_MODE_CLASS (mode) == MODE_FLOAT) { /* We can use the l.s and l.d macros to load floating-point constants. ??? For l.s, we could probably get better code by returning GR_REGS here. */ return NO_REGS; } else if (gp_reg_p || x == CONST0_RTX (mode)) { /* In this case we can use mtc1, mfc1, dmtc1 or dmfc1. */ return NO_REGS; } else if (FP_REG_P (regno)) { /* In this case we can use mov.s or mov.d. */ return NO_REGS; } else { /* Otherwise, we need to reload through an integer register. */ return gr_regs; } } /* In mips16 mode, going between memory and anything but M16_REGS requires an M16_REG. */ 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 (class == M16_REGS || class == M16_NA_REGS) return NO_REGS; return M16_REGS; } } return NO_REGS; } /* Implement CLASS_MAX_NREGS. Usually all registers are word-sized. The only supported exception is -mgp64 -msingle-float, which has 64-bit words but 32-bit float registers. A word-based calculation is correct even in that case, since -msingle-float disallows multi-FPR values. The FP status registers are an exception to this rule. They are always 4 bytes wide as they only hold condition code modes, and CCmode is always considered to be 4 bytes wide. */ int mips_class_max_nregs (enum reg_class class ATTRIBUTE_UNUSED, enum machine_mode mode) { if (class == ST_REGS) return (GET_MODE_SIZE (mode) + 3) / 4; else return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; } static bool mips_valid_pointer_mode (enum machine_mode mode) { return (mode == SImode || (TARGET_64BIT && mode == DImode)); } /* Target hook for vector_mode_supported_p. */ static bool mips_vector_mode_supported_p (enum machine_mode mode) { switch (mode) { case V2SFmode: return TARGET_PAIRED_SINGLE_FLOAT; case V2HImode: case V4QImode: return TARGET_DSP; default: return false; } } /* If we can access small data directly (using gp-relative relocation operators) return the small data pointer, otherwise return null. For each mips16 function which refers to GP relative symbols, we use a pseudo register, initialized at the start of the function, to hold the $gp value. */ static rtx mips16_gp_pseudo_reg (void) { if (cfun->machine->mips16_gp_pseudo_rtx == NULL_RTX) { rtx unspec; rtx insn, scan; cfun->machine->mips16_gp_pseudo_rtx = gen_reg_rtx (Pmode); /* We want to initialize this to a value which gcc will believe is constant. */ start_sequence (); unspec = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, const0_rtx), UNSPEC_GP); emit_move_insn (cfun->machine->mips16_gp_pseudo_rtx, gen_rtx_CONST (Pmode, unspec)); insn = get_insns (); end_sequence (); push_topmost_sequence (); /* We need to emit the initialization after the FUNCTION_BEG note, so that it will be integrated. */ for (scan = get_insns (); scan != NULL_RTX; scan = NEXT_INSN (scan)) if (NOTE_P (scan) && 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; } /* Write out code to move floating point arguments in or out of general registers. Output the instructions to FILE. FP_CODE is the code describing which arguments are present (see the comment at the definition of CUMULATIVE_ARGS in mips.h). FROM_FP_P is nonzero if we are copying from the floating point registers. */ static void mips16_fp_args (FILE *file, int fp_code, int from_fp_p) { const char *s; int gparg, fparg; unsigned int f; /* This code only works for the original 32 bit ABI and the O64 ABI. */ gcc_assert (TARGET_OLDABI); 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 gcc_unreachable (); ++gparg; ++fparg; } } /* Build a mips16 function stub. This is used for functions which take arguments in the floating point registers. It is 32 bit code that moves the floating point args into the general registers, and then jumps to the 16 bit code. */ static void build_mips16_function_stub (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"); switch_to_section (function_section (stubdecl)); ASM_OUTPUT_ALIGN (file, floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT)); /* ??? If FUNCTION_NAME_ALREADY_DECLARED is defined, then we are within a .ent, and we cannot emit another .ent. */ if (!FUNCTION_NAME_ALREADY_DECLARED) { fputs ("\t.ent\t", file); assemble_name (file, stubname); fputs ("\n", file); } assemble_name (file, stubname); fputs (":\n", file); /* We don't want the assembler to insert any nops here. */ 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"); /* Unfortunately, we can't fill the jump delay slot. We can't fill with one of the mfc1 instructions, because the result is not available for one instruction, so if the very first instruction in the function refers to the register, it will see the wrong value. */ fprintf (file, "\tnop\n"); fprintf (file, "\t.set\treorder\n"); if (!FUNCTION_NAME_ALREADY_DECLARED) { fputs ("\t.end\t", file); assemble_name (file, stubname); fputs ("\n", file); } fprintf (file, "\t.set\tmips16\n"); switch_to_section (function_section (current_function_decl)); } /* We keep a list of functions for which we have already built stubs in build_mips16_call_stub. */ struct mips16_stub { struct mips16_stub *next; char *name; int fpret; }; static struct mips16_stub *mips16_stubs; /* Build a call stub for a mips16 call. A stub is needed if we are passing any floating point values which should go into the floating point registers. If we are, and the call turns out to be to a 32 bit function, the stub will be used to move the values into the floating point registers before calling the 32 bit function. The linker will magically adjust the function call to either the 16 bit function or the 32 bit stub, depending upon where the function call is actually defined. Similarly, we need a stub if the return value might come back in a floating point register. RETVAL is the location of the return value, or null if this is a call rather than a call_value. FN is the address of the function and ARG_SIZE is the size of the arguments. FP_CODE is the code built by function_arg. This function returns a nonzero value if it builds the call instruction itself. */ int build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code) { int fpret; const char *fnname; char *secname, *stubname; struct mips16_stub *l; tree stubid, stubdecl; int need_comma; unsigned int f; /* We don't need to do anything if we aren't in mips16 mode, or if we were invoked with the -msoft-float option. */ if (! TARGET_MIPS16 || ! mips16_hard_float) return 0; /* Figure out whether the value might come back in a floating point register. */ fpret = (retval != 0 && GET_MODE_CLASS (GET_MODE (retval)) == MODE_FLOAT && GET_MODE_SIZE (GET_MODE (retval)) <= UNITS_PER_FPVALUE); /* We don't need to do anything if there were no floating point arguments and the value will not be returned in a floating point register. */ if (fp_code == 0 && ! fpret) return 0; /* We don't need to do anything if this is a call to a special mips16 support function. */ if (GET_CODE (fn) == SYMBOL_REF && strncmp (XSTR (fn, 0), "__mips16_", 9) == 0) return 0; /* This code will only work for o32 and o64 abis. The other ABI's require more sophisticated support. */ gcc_assert (TARGET_OLDABI); /* We can only handle SFmode and DFmode floating point return values. */ if (fpret) gcc_assert (GET_MODE (retval) == SFmode || GET_MODE (retval) == DFmode); /* If we're calling via a function pointer, then we must always call via a stub. There are magic stubs provided in libgcc.a for each of the required cases. Each of them expects the function address to arrive in register $2. */ if (GET_CODE (fn) != SYMBOL_REF) { char buf[30]; tree id; rtx stub_fn, insn; /* ??? If this code is modified to support other ABI's, we need to handle PARALLEL return values here. */ 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)); emit_move_insn (gen_rtx_REG (Pmode, 2), fn); if (retval == NULL_RTX) insn = gen_call_internal (stub_fn, arg_size); else insn = gen_call_value_internal (retval, stub_fn, arg_size); insn = emit_call_insn (insn); /* Put the register usage information on the CALL. */ 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 we are handling a floating point return value, we need to save $18 in the function prologue. Putting a note on the call will mean that regs_ever_live[$18] will be true if the call is not eliminated, and we can check that in the prologue code. */ 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 to tell the caller that we've generated the call insn. */ return 1; } /* We know the function we are going to call. If we have already built a stub, we don't need to do anything further. */ fnname = XSTR (fn, 0); for (l = mips16_stubs; l != NULL; l = l->next) if (strcmp (l->name, fnname) == 0) break; if (l == NULL) { /* Build a special purpose stub. When the linker sees a function call in mips16 code, it will check where the target is defined. If the target is a 32 bit call, the linker will search for the section defined here. It can tell which symbol this section is associated with by looking at the relocation information (the name is unreliable, since this might be a static function). If such a section is found, the linker will redirect the call to the start of the magic section. If the function does not return a floating point value, the special stub section is named .mips16.call.FNNAME If the function does return a floating point value, the stub section is named .mips16.call.fp.FNNAME */ 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); if (!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); } /* We build the stub code by hand. That's the only way we can do it, since we can't generate 32 bit code during a 16 bit compilation. */ /* We don't want the assembler to insert any nops here. */ 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"); /* Unfortunately, we can't fill the jump delay slot. We can't fill with one of the mtc1 instructions, because the result is not available for one instruction, so if the very first instruction in the function refers to the register, it will see the wrong value. */ 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); /* As above, we can't fill the delay slot. */ 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]); /* As above, we can't fill the delay slot. */ 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 if (!FUNCTION_NAME_ALREADY_DECLARED) { fputs ("\t.end\t", asm_out_file); assemble_name (asm_out_file, stubname); fputs ("\n", asm_out_file); } fprintf (asm_out_file, "\t.set\tmips16\n"); /* Record this stub. */ l = (struct mips16_stub *) xmalloc (sizeof *l); l->name = xstrdup (fnname); l->fpret = fpret; l->next = mips16_stubs; mips16_stubs = l; } /* If we expect a floating point return value, but we've built a stub which does not expect one, then we're in trouble. We can't use the existing stub, because it won't handle the floating point value. We can't build a new stub, because the linker won't know which stub to use for the various calls in this object file. Fortunately, this case is illegal, since it means that a function was declared in two different ways in a single compilation. */ if (fpret && ! l->fpret) error ("cannot handle inconsistent calls to %qs", fnname); /* If we are calling a stub which handles a floating point return value, we need to arrange to save $18 in the prologue. We do this by marking the function call as using the register. The prologue will later see that it is used, and emit code to save it. */ if (l->fpret) { rtx insn; if (retval == NULL_RTX) insn = gen_call_internal (fn, arg_size); else insn = gen_call_value_internal (retval, fn, arg_size); insn = emit_call_insn (insn); 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 to tell the caller that we've generated the call insn. */ return 1; } /* Return 0 to let the caller generate the call insn. */ return 0; } /* An entry in the mips16 constant pool. VALUE is the pool constant, MODE is its mode, and LABEL is the CODE_LABEL associated with it. */ struct mips16_constant { struct mips16_constant *next; rtx value; rtx label; enum machine_mode mode; }; /* Information about an incomplete mips16 constant pool. FIRST is the first constant, HIGHEST_ADDRESS is the highest address that the first byte of the pool can have, and INSN_ADDRESS is the current instruction address. */ struct mips16_constant_pool { struct mips16_constant *first; int highest_address; int insn_address; }; /* Add constant VALUE to POOL and return its label. MODE is the value's mode (used for CONST_INTs, etc.). */ static rtx add_constant (struct mips16_constant_pool *pool, rtx value, enum machine_mode mode) { struct mips16_constant **p, *c; bool first_of_size_p; /* See whether the constant is already in the pool. If so, return the existing label, otherwise leave P pointing to the place where the constant should be added. Keep the pool sorted in increasing order of mode size so that we can reduce the number of alignments needed. */ first_of_size_p = true; for (p = &pool->first; *p != 0; p = &(*p)->next) { if (mode == (*p)->mode && rtx_equal_p (value, (*p)->value)) return (*p)->label; if (GET_MODE_SIZE (mode) < GET_MODE_SIZE ((*p)->mode)) break; if (GET_MODE_SIZE (mode) == GET_MODE_SIZE ((*p)->mode)) first_of_size_p = false; } /* In the worst case, the constant needed by the earliest instruction will end up at the end of the pool. The entire pool must then be accessible from that instruction. When adding the first constant, set the pool's highest address to the address of the first out-of-range byte. Adjust this address downwards each time a new constant is added. */ if (pool->first == 0) /* For pc-relative lw, addiu and daddiu instructions, the base PC value is the address of the instruction with the lowest two bits clear. The base PC value for ld has the lowest three bits clear. Assume the worst case here. */ pool->highest_address = pool->insn_address - (UNITS_PER_WORD - 2) + 0x8000; pool->highest_address -= GET_MODE_SIZE (mode); if (first_of_size_p) /* Take into account the worst possible padding due to alignment. */ pool->highest_address -= GET_MODE_SIZE (mode) - 1; /* Create a new entry. */ c = (struct mips16_constant *) xmalloc (sizeof *c); c->value = value; c->mode = mode; c->label = gen_label_rtx (); c->next = *p; *p = c; return c->label; } /* Output constant VALUE after instruction INSN and return the last instruction emitted. MODE is the mode of the constant. */ static rtx dump_constants_1 (enum machine_mode mode, rtx value, rtx insn) { switch (GET_MODE_CLASS (mode)) { case MODE_INT: { rtx size = GEN_INT (GET_MODE_SIZE (mode)); return emit_insn_after (gen_consttable_int (value, size), insn); } case MODE_FLOAT: return emit_insn_after (gen_consttable_float (value), insn); case MODE_VECTOR_FLOAT: case MODE_VECTOR_INT: { int i; for (i = 0; i < CONST_VECTOR_NUNITS (value); i++) insn = dump_constants_1 (GET_MODE_INNER (mode), CONST_VECTOR_ELT (value, i), insn); return insn; } default: gcc_unreachable (); } } /* Dump out the constants in CONSTANTS after INSN. */ static void dump_constants (struct mips16_constant *constants, rtx insn) { struct mips16_constant *c, *next; int align; align = 0; for (c = constants; c != NULL; c = next) { /* If necessary, increase the alignment of PC. */ if (align < GET_MODE_SIZE (c->mode)) { int align_log = floor_log2 (GET_MODE_SIZE (c->mode)); insn = emit_insn_after (gen_align (GEN_INT (align_log)), insn); } align = GET_MODE_SIZE (c->mode); insn = emit_label_after (c->label, insn); insn = dump_constants_1 (c->mode, c->value, insn); next = c->next; free (c); } emit_barrier_after (insn); } /* Return the length of instruction INSN. */ static int mips16_insn_length (rtx insn) { if (JUMP_P (insn)) { rtx body = PATTERN (insn); if (GET_CODE (body) == ADDR_VEC) return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, 0); if (GET_CODE (body) == ADDR_DIFF_VEC) return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, 1); } return get_attr_length (insn); } /* Rewrite *X so that constant pool references refer to the constant's label instead. DATA points to the constant pool structure. */ static int mips16_rewrite_pool_refs (rtx *x, void *data) { struct mips16_constant_pool *pool = data; if (GET_CODE (*x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (*x)) *x = gen_rtx_LABEL_REF (Pmode, add_constant (pool, get_pool_constant (*x), get_pool_mode (*x))); return 0; } /* Build MIPS16 constant pools. */ static void mips16_lay_out_constants (void) { struct mips16_constant_pool pool; rtx insn, barrier; barrier = 0; memset (&pool, 0, sizeof (pool)); for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) { /* Rewrite constant pool references in INSN. */ if (INSN_P (insn)) for_each_rtx (&PATTERN (insn), mips16_rewrite_pool_refs, &pool); pool.insn_address += mips16_insn_length (insn); if (pool.first != NULL) { /* If there are no natural barriers between the first user of the pool and the highest acceptable address, we'll need to create a new instruction to jump around the constant pool. In the worst case, this instruction will be 4 bytes long. If it's too late to do this transformation after INSN, do it immediately before INSN. */ if (barrier == 0 && pool.insn_address + 4 > pool.highest_address) { rtx label, jump; label = gen_label_rtx (); jump = emit_jump_insn_before (gen_jump (label), insn); JUMP_LABEL (jump) = label; LABEL_NUSES (label) = 1; barrier = emit_barrier_after (jump); emit_label_after (label, barrier); pool.insn_address += 4; } /* See whether the constant pool is now out of range of the first user. If so, output the constants after the previous barrier. Note that any instructions between BARRIER and INSN (inclusive) will use negative offsets to refer to the pool. */ if (pool.insn_address > pool.highest_address) { dump_constants (pool.first, barrier); pool.first = NULL; barrier = 0; } else if (BARRIER_P (insn)) barrier = insn; } } dump_constants (pool.first, get_last_insn ()); } /* A temporary variable used by for_each_rtx callbacks, etc. */ static rtx mips_sim_insn; /* A structure representing the state of the processor pipeline. Used by the mips_sim_* family of functions. */ struct mips_sim { /* The maximum number of instructions that can be issued in a cycle. (Caches mips_issue_rate.) */ unsigned int issue_rate; /* The current simulation time. */ unsigned int time; /* How many more instructions can be issued in the current cycle. */ unsigned int insns_left; /* LAST_SET[X].INSN is the last instruction to set register X. LAST_SET[X].TIME is the time at which that instruction was issued. INSN is null if no instruction has yet set register X. */ struct { rtx insn; unsigned int time; } last_set[FIRST_PSEUDO_REGISTER]; /* The pipeline's current DFA state. */ state_t dfa_state; }; /* Reset STATE to the initial simulation state. */ static void mips_sim_reset (struct mips_sim *state) { state->time = 0; state->insns_left = state->issue_rate; memset (&state->last_set, 0, sizeof (state->last_set)); state_reset (state->dfa_state); } /* Initialize STATE before its first use. DFA_STATE points to an allocated but uninitialized DFA state. */ static void mips_sim_init (struct mips_sim *state, state_t dfa_state) { state->issue_rate = mips_issue_rate (); state->dfa_state = dfa_state; mips_sim_reset (state); } /* Advance STATE by one clock cycle. */ static void mips_sim_next_cycle (struct mips_sim *state) { state->time++; state->insns_left = state->issue_rate; state_transition (state->dfa_state, 0); } /* Advance simulation state STATE until instruction INSN can read register REG. */ static void mips_sim_wait_reg (struct mips_sim *state, rtx insn, rtx reg) { unsigned int i; for (i = 0; i < HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)); i++) if (state->last_set[REGNO (reg) + i].insn != 0) { unsigned int t; t = state->last_set[REGNO (reg) + i].time; t += insn_latency (state->last_set[REGNO (reg) + i].insn, insn); while (state->time < t) mips_sim_next_cycle (state); } } /* A for_each_rtx callback. If *X is a register, advance simulation state DATA until mips_sim_insn can read the register's value. */ static int mips_sim_wait_regs_2 (rtx *x, void *data) { if (REG_P (*x)) mips_sim_wait_reg (data, mips_sim_insn, *x); return 0; } /* Call mips_sim_wait_regs_2 (R, DATA) for each register R mentioned in *X. */ static void mips_sim_wait_regs_1 (rtx *x, void *data) { for_each_rtx (x, mips_sim_wait_regs_2, data); } /* Advance simulation state STATE until all of INSN's register dependencies are satisfied. */ static void mips_sim_wait_regs (struct mips_sim *state, rtx insn) { mips_sim_insn = insn; note_uses (&PATTERN (insn), mips_sim_wait_regs_1, state); } /* Advance simulation state STATE until the units required by instruction INSN are available. */ static void mips_sim_wait_units (struct mips_sim *state, rtx insn) { state_t tmp_state; tmp_state = alloca (state_size ()); while (state->insns_left == 0 || (memcpy (tmp_state, state->dfa_state, state_size ()), state_transition (tmp_state, insn) >= 0)) mips_sim_next_cycle (state); } /* Advance simulation state STATE until INSN is ready to issue. */ static void mips_sim_wait_insn (struct mips_sim *state, rtx insn) { mips_sim_wait_regs (state, insn); mips_sim_wait_units (state, insn); } /* mips_sim_insn has just set X. Update the LAST_SET array in simulation state DATA. */ static void mips_sim_record_set (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data) { struct mips_sim *state; unsigned int i; state = data; if (REG_P (x)) for (i = 0; i < HARD_REGNO_NREGS (REGNO (x), GET_MODE (x)); i++) { state->last_set[REGNO (x) + i].insn = mips_sim_insn; state->last_set[REGNO (x) + i].time = state->time; } } /* Issue instruction INSN in scheduler state STATE. Assume that INSN can issue immediately (i.e., that mips_sim_wait_insn has already been called). */ static void mips_sim_issue_insn (struct mips_sim *state, rtx insn) { state_transition (state->dfa_state, insn); state->insns_left--; mips_sim_insn = insn; note_stores (PATTERN (insn), mips_sim_record_set, state); } /* Simulate issuing a NOP in state STATE. */ static void mips_sim_issue_nop (struct mips_sim *state) { if (state->insns_left == 0) mips_sim_next_cycle (state); state->insns_left--; } /* Update simulation state STATE so that it's ready to accept the instruction after INSN. INSN should be part of the main rtl chain, not a member of a SEQUENCE. */ static void mips_sim_finish_insn (struct mips_sim *state, rtx insn) { /* If INSN is a jump with an implicit delay slot, simulate a nop. */ if (JUMP_P (insn)) mips_sim_issue_nop (state); switch (GET_CODE (SEQ_BEGIN (insn))) { case CODE_LABEL: case CALL_INSN: /* We can't predict the processor state after a call or label. */ mips_sim_reset (state); break; case JUMP_INSN: /* The delay slots of branch likely instructions are only executed when the branch is taken. Therefore, if the caller has simulated the delay slot instruction, STATE does not really reflect the state of the pipeline for the instruction after the delay slot. Also, branch likely instructions tend to incur a penalty when not taken, so there will probably be an extra delay between the branch and the instruction after the delay slot. */ if (INSN_ANNULLED_BRANCH_P (SEQ_BEGIN (insn))) mips_sim_reset (state); break; default: break; } } /* The VR4130 pipeline issues aligned pairs of instructions together, but it stalls the second instruction if it depends on the first. In order to cut down the amount of logic required, this dependence check is not based on a full instruction decode. Instead, any non-SPECIAL instruction is assumed to modify the register specified by bits 20-16 (which is usually the "rt" field). In beq, beql, bne and bnel instructions, the rt field is actually an input, so we can end up with a false dependence between the branch and its delay slot. If this situation occurs in instruction INSN, try to avoid it by swapping rs and rt. */ static void vr4130_avoid_branch_rt_conflict (rtx insn) { rtx first, second; first = SEQ_BEGIN (insn); second = SEQ_END (insn); if (JUMP_P (first) && NONJUMP_INSN_P (second) && GET_CODE (PATTERN (first)) == SET && GET_CODE (SET_DEST (PATTERN (first))) == PC && GET_CODE (SET_SRC (PATTERN (first))) == IF_THEN_ELSE) { /* Check for the right kind of condition. */ rtx cond = XEXP (SET_SRC (PATTERN (first)), 0); if ((GET_CODE (cond) == EQ || GET_CODE (cond) == NE) && REG_P (XEXP (cond, 0)) && REG_P (XEXP (cond, 1)) && reg_referenced_p (XEXP (cond, 1), PATTERN (second)) && !reg_referenced_p (XEXP (cond, 0), PATTERN (second))) { /* SECOND mentions the rt register but not the rs register. */ rtx tmp = XEXP (cond, 0); XEXP (cond, 0) = XEXP (cond, 1); XEXP (cond, 1) = tmp; } } } /* Implement -mvr4130-align. Go through each basic block and simulate the processor pipeline. If we find that a pair of instructions could execute in parallel, and the first of those instruction is not 8-byte aligned, insert a nop to make it aligned. */ static void vr4130_align_insns (void) { struct mips_sim state; rtx insn, subinsn, last, last2, next; bool aligned_p; dfa_start (); /* LAST is the last instruction before INSN to have a nonzero length. LAST2 is the last such instruction before LAST. */ last = 0; last2 = 0; /* ALIGNED_P is true if INSN is known to be at an aligned address. */ aligned_p = true; mips_sim_init (&state, alloca (state_size ())); for (insn = get_insns (); insn != 0; insn = next) { unsigned int length; next = NEXT_INSN (insn); /* See the comment above vr4130_avoid_branch_rt_conflict for details. This isn't really related to the alignment pass, but we do it on the fly to avoid a separate instruction walk. */ vr4130_avoid_branch_rt_conflict (insn); if (USEFUL_INSN_P (insn)) FOR_EACH_SUBINSN (subinsn, insn) { mips_sim_wait_insn (&state, subinsn); /* If we want this instruction to issue in parallel with the previous one, make sure that the previous instruction is aligned. There are several reasons why this isn't worthwhile when the second instruction is a call: - Calls are less likely to be performance critical, - There's a good chance that the delay slot can execute in parallel with the call. - The return address would then be unaligned. In general, if we're going to insert a nop between instructions X and Y, it's better to insert it immediately after X. That way, if the nop makes Y aligned, it will also align any labels between X and Y. */ if (state.insns_left != state.issue_rate && !CALL_P (subinsn)) { if (subinsn == SEQ_BEGIN (insn) && aligned_p) { /* SUBINSN is the first instruction in INSN and INSN is aligned. We want to align the previous instruction instead, so insert a nop between LAST2 and LAST. Note that LAST could be either a single instruction or a branch with a delay slot. In the latter case, LAST, like INSN, is already aligned, but the delay slot must have some extra delay that stops it from issuing at the same time as the branch. We therefore insert a nop before the branch in order to align its delay slot. */ emit_insn_after (gen_nop (), last2); aligned_p = false; } else if (subinsn != SEQ_BEGIN (insn) && !aligned_p) { /* SUBINSN is the delay slot of INSN, but INSN is currently unaligned. Insert a nop between LAST and INSN to align it. */ emit_insn_after (gen_nop (), last); aligned_p = true; } } mips_sim_issue_insn (&state, subinsn); } mips_sim_finish_insn (&state, insn); /* Update LAST, LAST2 and ALIGNED_P for the next instruction. */ length = get_attr_length (insn); if (length > 0) { /* If the instruction is an asm statement or multi-instruction mips.md patern, the length is only an estimate. Insert an 8 byte alignment after it so that the following instructions can be handled correctly. */ if (NONJUMP_INSN_P (SEQ_BEGIN (insn)) && (recog_memoized (insn) < 0 || length >= 8)) { next = emit_insn_after (gen_align (GEN_INT (3)), insn); next = NEXT_INSN (next); mips_sim_next_cycle (&state); aligned_p = true; } else if (length & 4) aligned_p = !aligned_p; last2 = last; last = insn; } /* See whether INSN is an aligned label. */ if (LABEL_P (insn) && label_to_alignment (insn) >= 3) aligned_p = true; } dfa_finish (); } /* Subroutine of mips_reorg. If there is a hazard between INSN and a previous instruction, avoid it by inserting nops after instruction AFTER. *DELAYED_REG and *HILO_DELAY describe the hazards that apply at this point. If *DELAYED_REG is non-null, INSN must wait a cycle before using the value of that register. *HILO_DELAY counts the number of instructions since the last hilo hazard (that is, the number of instructions since the last mflo or mfhi). After inserting nops for INSN, update *DELAYED_REG and *HILO_DELAY for the next instruction. LO_REG is an rtx for the LO register, used in dependence checking. */ static void mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay, rtx *delayed_reg, rtx lo_reg) { rtx pattern, set; int nops, ninsns; if (!INSN_P (insn)) return; pattern = PATTERN (insn); /* Do not put the whole function in .set noreorder if it contains an asm statement. We don't know whether there will be hazards between the asm statement and the gcc-generated code. */ if (GET_CODE (pattern) == ASM_INPUT || asm_noperands (pattern) >= 0) cfun->machine->all_noreorder_p = false; /* Ignore zero-length instructions (barriers and the like). */ ninsns = get_attr_length (insn) / 4; if (ninsns == 0) return; /* Work out how many nops are needed. Note that we only care about registers that are explicitly mentioned in the instruction's pattern. It doesn't matter that calls use the argument registers or that they clobber hi and lo. */ if (*hilo_delay < 2 && reg_set_p (lo_reg, pattern)) nops = 2 - *hilo_delay; else if (*delayed_reg != 0 && reg_referenced_p (*delayed_reg, pattern)) nops = 1; else nops = 0; /* Insert the nops between this instruction and the previous one. Each new nop takes us further from the last hilo hazard. */ *hilo_delay += nops; while (nops-- > 0) emit_insn_after (gen_hazard_nop (), after); /* Set up the state for the next instruction. */ *hilo_delay += ninsns; *delayed_reg = 0; if (INSN_CODE (insn) >= 0) switch (get_attr_hazard (insn)) { case HAZARD_NONE: break; case HAZARD_HILO: *hilo_delay = 0; break; case HAZARD_DELAY: set = single_set (insn); gcc_assert (set != 0); *delayed_reg = SET_DEST (set); break; } } /* Go through the instruction stream and insert nops where necessary. See if the whole function can then be put into .set noreorder & .set nomacro. */ static void mips_avoid_hazards (void) { rtx insn, last_insn, lo_reg, delayed_reg; int hilo_delay, i; /* Force all instructions to be split into their final form. */ split_all_insns_noflow (); /* Recalculate instruction lengths without taking nops into account. */ cfun->machine->ignore_hazard_length_p = true; shorten_branches (get_insns ()); cfun->machine->all_noreorder_p = true; /* Profiled functions can't be all noreorder because the profiler support uses assembler macros. */ if (current_function_profile) cfun->machine->all_noreorder_p = false; /* Code compiled with -mfix-vr4120 can't be all noreorder because we rely on the assembler to work around some errata. */ if (TARGET_FIX_VR4120) cfun->machine->all_noreorder_p = false; /* The same is true for -mfix-vr4130 if we might generate mflo or mfhi instructions. Note that we avoid using mflo and mfhi if the VR4130 macc and dmacc instructions are available instead; see the *mfhilo_{si,di}_macc patterns. */ if (TARGET_FIX_VR4130 && !ISA_HAS_MACCHI) cfun->machine->all_noreorder_p = false; last_insn = 0; hilo_delay = 2; delayed_reg = 0; lo_reg = gen_rtx_REG (SImode, LO_REGNUM); for (insn = get_insns (); insn != 0; insn = NEXT_INSN (insn)) if (INSN_P (insn)) { if (GET_CODE (PATTERN (insn)) == SEQUENCE) for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) mips_avoid_hazard (last_insn, XVECEXP (PATTERN (insn), 0, i), &hilo_delay, &delayed_reg, lo_reg); else mips_avoid_hazard (last_insn, insn, &hilo_delay, &delayed_reg, lo_reg); last_insn = insn; } } /* Implement TARGET_MACHINE_DEPENDENT_REORG. */ static void mips_reorg (void) { if (TARGET_MIPS16) mips16_lay_out_constants (); else if (TARGET_EXPLICIT_RELOCS) { if (mips_flag_delayed_branch) dbr_schedule (get_insns ()); mips_avoid_hazards (); if (TUNE_MIPS4130 && TARGET_VR4130_ALIGN) vr4130_align_insns (); } } /* This function does three things: - Register the special divsi3 and modsi3 functions if -mfix-vr4120. - Register the mips16 hardware floating point stubs. - Register the gofast functions if selected using --enable-gofast. */ #include "config/gofast.h" static void mips_init_libfuncs (void) { if (TARGET_FIX_VR4120) { set_optab_libfunc (sdiv_optab, SImode, "__vr4120_divsi3"); set_optab_libfunc (smod_optab, SImode, "__vr4120_modsi3"); } if (TARGET_MIPS16 && mips16_hard_float) { set_optab_libfunc (add_optab, SFmode, "__mips16_addsf3"); set_optab_libfunc (sub_optab, SFmode, "__mips16_subsf3"); set_optab_libfunc (smul_optab, SFmode, "__mips16_mulsf3"); set_optab_libfunc (sdiv_optab, SFmode, "__mips16_divsf3"); set_optab_libfunc (eq_optab, SFmode, "__mips16_eqsf2"); set_optab_libfunc (ne_optab, SFmode, "__mips16_nesf2"); set_optab_libfunc (gt_optab, SFmode, "__mips16_gtsf2"); set_optab_libfunc (ge_optab, SFmode, "__mips16_gesf2"); set_optab_libfunc (lt_optab, SFmode, "__mips16_ltsf2"); set_optab_libfunc (le_optab, SFmode, "__mips16_lesf2"); set_conv_libfunc (sfix_optab, SImode, SFmode, "__mips16_fix_truncsfsi"); set_conv_libfunc (sfloat_optab, SFmode, SImode, "__mips16_floatsisf"); if (TARGET_DOUBLE_FLOAT) { set_optab_libfunc (add_optab, DFmode, "__mips16_adddf3"); set_optab_libfunc (sub_optab, DFmode, "__mips16_subdf3"); set_optab_libfunc (smul_optab, DFmode, "__mips16_muldf3"); set_optab_libfunc (sdiv_optab, DFmode, "__mips16_divdf3"); set_optab_libfunc (eq_optab, DFmode, "__mips16_eqdf2"); set_optab_libfunc (ne_optab, DFmode, "__mips16_nedf2"); set_optab_libfunc (gt_optab, DFmode, "__mips16_gtdf2"); set_optab_libfunc (ge_optab, DFmode, "__mips16_gedf2"); set_optab_libfunc (lt_optab, DFmode, "__mips16_ltdf2"); set_optab_libfunc (le_optab, DFmode, "__mips16_ledf2"); set_conv_libfunc (sext_optab, DFmode, SFmode, "__mips16_extendsfdf2"); set_conv_libfunc (trunc_optab, SFmode, DFmode, "__mips16_truncdfsf2"); set_conv_libfunc (sfix_optab, SImode, DFmode, "__mips16_fix_truncdfsi"); set_conv_libfunc (sfloat_optab, DFmode, SImode, "__mips16_floatsidf"); } } else gofast_maybe_init_libfuncs (); } /* Return a number assessing the cost of moving a register in class FROM to class TO. The classes are expressed using the enumeration values such as `GENERAL_REGS'. A value of 2 is the default; other values are interpreted relative to that. It is not required that the cost always equal 2 when FROM is the same as TO; on some machines it is expensive to move between registers if they are not general registers. If reload sees an insn consisting of a single `set' between two hard registers, and if `REGISTER_MOVE_COST' applied to their classes returns a value of 2, reload does not check to ensure that the constraints of the insn are met. Setting a cost of other than 2 will allow reload to verify that the constraints are met. You should do this if the `movM' pattern's constraints do not allow such copying. ??? We make the cost of moving from HI/LO into general registers the same as for one of moving general registers to HI/LO for TARGET_MIPS16 in order to prevent allocating a pseudo to HI/LO. This might hurt optimizations though, it isn't clear if it is wise. And it might not work in all cases. We could solve the DImode LO reg problem by using a multiply, just like reload_{in,out}si. We could solve the SImode/HImode HI reg problem by using divide instructions. divu puts the remainder in the HI reg, so doing a divide by -1 will move the value in the HI reg for all values except -1. We could handle that case by using a signed divide, e.g. -1 / 2 (or maybe 1 / -2?). We'd have to emit a compare/branch to test the input value to see which instruction we need to use. This gets pretty messy, but it is feasible. */ int mips_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED, enum reg_class to, enum reg_class 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 (reg_class_subset_p (to, ACC_REGS)) { 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 (reg_class_subset_p (from, ACC_REGS)) { 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; } /* Fall through. ??? What cases are these? Shouldn't we return 2 here? */ return 12; } /* Return the length of INSN. LENGTH is the initial length computed by attributes in the machine-description file. */ int mips_adjust_insn_length (rtx insn, int length) { /* A unconditional jump has an unfilled delay slot if it is not part of a sequence. A conditional jump normally has a delay slot, but does not on MIPS16. */ if (CALL_P (insn) || (TARGET_MIPS16 ? simplejump_p (insn) : JUMP_P (insn))) length += 4; /* See how many nops might be needed to avoid hardware hazards. */ if (!cfun->machine->ignore_hazard_length_p && INSN_CODE (insn) >= 0) switch (get_attr_hazard (insn)) { case HAZARD_NONE: break; case HAZARD_DELAY: length += 4; break; case HAZARD_HILO: length += 8; break; } /* All MIPS16 instructions are a measly two bytes. */ if (TARGET_MIPS16) length /= 2; return length; } /* Return an asm sequence to start a noat block and load the address of a label into $1. */ const char * mips_output_load_label (void) { if (TARGET_EXPLICIT_RELOCS) switch (mips_abi) { case ABI_N32: return "%[lw\t%@,%%got_page(%0)(%+)\n\taddiu\t%@,%@,%%got_ofst(%0)"; case ABI_64: return "%[ld\t%@,%%got_page(%0)(%+)\n\tdaddiu\t%@,%@,%%got_ofst(%0)"; default: if (ISA_HAS_LOAD_DELAY) return "%[lw\t%@,%%got(%0)(%+)%#\n\taddiu\t%@,%@,%%lo(%0)"; return "%[lw\t%@,%%got(%0)(%+)\n\taddiu\t%@,%@,%%lo(%0)"; } else { if (Pmode == DImode) return "%[dla\t%@,%0"; else return "%[la\t%@,%0"; } } /* Return the assembly code for INSN, which has the operands given by OPERANDS, and which branches to OPERANDS[1] if some condition is true. BRANCH_IF_TRUE is the asm template that should be used if OPERANDS[1] is in range of a direct branch. BRANCH_IF_FALSE is an inverted version of BRANCH_IF_TRUE. */ const char * mips_output_conditional_branch (rtx insn, rtx *operands, const char *branch_if_true, const char *branch_if_false) { unsigned int length; rtx taken, not_taken; length = get_attr_length (insn); if (length <= 8) { /* Just a simple conditional branch. */ mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn)); return branch_if_true; } /* Generate a reversed branch around a direct jump. This fallback does not use branch-likely instructions. */ mips_branch_likely = false; not_taken = gen_label_rtx (); taken = operands[1]; /* Generate the reversed branch to NOT_TAKEN. */ operands[1] = not_taken; output_asm_insn (branch_if_false, operands); /* If INSN has a delay slot, we must provide delay slots for both the branch to NOT_TAKEN and the conditional jump. We must also ensure that INSN's delay slot is executed in the appropriate cases. */ if (final_sequence) { /* This first delay slot will always be executed, so use INSN's delay slot if is not annulled. */ if (!INSN_ANNULLED_BRANCH_P (insn)) { final_scan_insn (XVECEXP (final_sequence, 0, 1), asm_out_file, optimize, 1, NULL); INSN_DELETED_P (XVECEXP (final_sequence, 0, 1)) = 1; } else output_asm_insn ("nop", 0); fprintf (asm_out_file, "\n"); } /* Output the unconditional branch to TAKEN. */ if (length <= 16) output_asm_insn ("j\t%0%/", &taken); else { output_asm_insn (mips_output_load_label (), &taken); output_asm_insn ("jr\t%@%]%/", 0); } /* Now deal with its delay slot; see above. */ if (final_sequence) { /* This delay slot will only be executed if the branch is taken. Use INSN's delay slot if is annulled. */ if (INSN_ANNULLED_BRANCH_P (insn)) { final_scan_insn (XVECEXP (final_sequence, 0, 1), asm_out_file, optimize, 1, NULL); INSN_DELETED_P (XVECEXP (final_sequence, 0, 1)) = 1; } else output_asm_insn ("nop", 0); fprintf (asm_out_file, "\n"); } /* Output NOT_TAKEN. */ (*targetm.asm_out.internal_label) (asm_out_file, "L", CODE_LABEL_NUMBER (not_taken)); return ""; } /* Return the assembly code for INSN, which branches to OPERANDS[1] if some ordered condition is true. The condition is given by OPERANDS[0] if !INVERTED_P, otherwise it is the inverse of OPERANDS[0]. OPERANDS[2] is the comparison's first operand; its second is always zero. */ const char * mips_output_order_conditional_branch (rtx insn, rtx *operands, bool inverted_p) { const char *branch[2]; /* Make BRANCH[1] branch to OPERANDS[1] when the condition is true. Make BRANCH[0] branch on the inverse condition. */ switch (GET_CODE (operands[0])) { /* These cases are equivalent to comparisons against zero. */ case LEU: inverted_p = !inverted_p; /* Fall through. */ case GTU: branch[!inverted_p] = MIPS_BRANCH ("bne", "%2,%.,%1"); branch[inverted_p] = MIPS_BRANCH ("beq", "%2,%.,%1"); break; /* These cases are always true or always false. */ case LTU: inverted_p = !inverted_p; /* Fall through. */ case GEU: branch[!inverted_p] = MIPS_BRANCH ("beq", "%.,%.,%1"); branch[inverted_p] = MIPS_BRANCH ("bne", "%.,%.,%1"); break; default: branch[!inverted_p] = MIPS_BRANCH ("b%C0z", "%2,%1"); branch[inverted_p] = MIPS_BRANCH ("b%N0z", "%2,%1"); break; } return mips_output_conditional_branch (insn, operands, branch[1], branch[0]); } /* Used to output div or ddiv instruction DIVISION, which has the operands given by OPERANDS. Add in a divide-by-zero check if needed. When working around R4000 and R4400 errata, we need to make sure that the division is not immediately followed by a shift[1][2]. We also need to stop the division from being put into a branch delay slot[3]. The easiest way to avoid both problems is to add a nop after the division. When a divide-by-zero check is needed, this nop can be used to fill the branch delay slot. [1] If a double-word or a variable shift executes immediately after starting an integer division, the shift may give an incorrect result. See quotations of errata #16 and #28 from "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0" in mips.md for details. [2] A similar bug to [1] exists for all revisions of the R4000 and the R4400 when run in an MC configuration. From "MIPS R4000MC Errata, Processor Revision 2.2 and 3.0": "19. In this following sequence: ddiv (or ddivu or div or divu) dsll32 (or dsrl32, dsra32) if an MPT stall occurs, while the divide is slipping the cpu pipeline, then the following double shift would end up with an incorrect result. Workaround: The compiler needs to avoid generating any sequence with divide followed by extended double shift." This erratum is also present in "MIPS R4400MC Errata, Processor Revision 1.0" and "MIPS R4400MC Errata, Processor Revision 2.0 & 3.0" as errata #10 and #4, respectively. [3] From "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0" (also valid for MIPS R4000MC processors): "52. R4000SC: This bug does not apply for the R4000PC. There are two flavors of this bug: 1) If the instruction just after divide takes an RF exception (tlb-refill, tlb-invalid) and gets an instruction cache miss (both primary and secondary) and the line which is currently in secondary cache at this index had the first data word, where the bits 5..2 are set, then R4000 would get a wrong result for the div. ##1 nop div r8, r9 ------------------- # end-of page. -tlb-refill nop ##2 nop div r8, r9 ------------------- # end-of page. -tlb-invalid nop 2) If the divide is in the taken branch delay slot, where the target takes RF exception and gets an I-cache miss for the exception vector or where I-cache miss occurs for the target address, under the above mentioned scenarios, the div would get wrong results. ##1 j r2 # to next page mapped or unmapped div r8,r9 # this bug would be there as long # as there is an ICache miss and nop # the "data pattern" is present ##2 beq r0, r0, NextPage # to Next page div r8,r9 nop This bug is present for div, divu, ddiv, and ddivu instructions. Workaround: For item 1), OS could make sure that the next page after the divide instruction is also mapped. For item 2), the compiler could make sure that the divide instruction is not in the branch delay slot." These processors have PRId values of 0x00004220 and 0x00004300 for the R4000 and 0x00004400, 0x00004500 and 0x00004600 for the R4400. */ const char * mips_output_division (const char *division, rtx *operands) { const char *s; s = division; if (TARGET_FIX_R4000 || TARGET_FIX_R4400) { output_asm_insn (s, operands); s = "nop"; } if (TARGET_CHECK_ZERO_DIV) { if (TARGET_MIPS16) { output_asm_insn (s, operands); s = "bnez\t%2,1f\n\tbreak\t7\n1:"; } else if (GENERATE_DIVIDE_TRAPS) { output_asm_insn (s, operands); s = "teq\t%2,%.,7"; } else { output_asm_insn ("%(bne\t%2,%.,1f", operands); output_asm_insn (s, operands); s = "break\t7%)\n1:"; } } return s; } /* Return true if GIVEN is the same as CANONICAL, or if it is CANONICAL with a final "000" replaced by "k". Ignore case. Note: this function is shared between GCC and GAS. */ static bool mips_strict_matching_cpu_name_p (const char *canonical, const char *given) { while (*given != 0 && TOLOWER (*given) == TOLOWER (*canonical)) given++, canonical++; return ((*given == 0 && *canonical == 0) || (strcmp (canonical, "000") == 0 && strcasecmp (given, "k") == 0)); } /* Return true if GIVEN matches CANONICAL, where GIVEN is a user-supplied CPU name. We've traditionally allowed a lot of variation here. Note: this function is shared between GCC and GAS. */ static bool mips_matching_cpu_name_p (const char *canonical, const char *given) { /* First see if the name matches exactly, or with a final "000" turned into "k". */ if (mips_strict_matching_cpu_name_p (canonical, given)) return true; /* If not, try comparing based on numerical designation alone. See if GIVEN is an unadorned number, or 'r' followed by a number. */ if (TOLOWER (*given) == 'r') given++; if (!ISDIGIT (*given)) return false; /* Skip over some well-known prefixes in the canonical name, hoping to find a number there too. */ 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); } /* Return the mips_cpu_info entry for the processor or ISA given by CPU_STRING. Return null if the string isn't recognized. A similar function exists in GAS. */ static const struct mips_cpu_info * mips_parse_cpu (const char *cpu_string) { const struct mips_cpu_info *p; const char *s; /* In the past, we allowed upper-case CPU names, but it doesn't work well with the multilib machinery. */ for (s = cpu_string; *s != 0; s++) if (ISUPPER (*s)) { warning (0, "the cpu name must be lower case"); break; } /* 'from-abi' selects the most compatible architecture for the given ABI: MIPS I for 32-bit ABIs and MIPS III for 64-bit ABIs. For the EABIs, we have to decide whether we're using the 32-bit or 64-bit version. Look first at the -mgp options, if given, otherwise base the choice on MASK_64BIT in TARGET_DEFAULT. */ 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)); /* 'default' has traditionally been a no-op. Probably not very useful. */ 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; return 0; } /* Return the processor associated with the given ISA level, or null if the ISA isn't valid. */ static const struct mips_cpu_info * mips_cpu_info_from_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; } /* Implement HARD_REGNO_NREGS. The size of FP registers is controlled by UNITS_PER_FPREG. The size of FP status registers is always 4, because they only hold condition code modes, and CCmode is always considered to be 4 bytes wide. All other registers are word sized. */ unsigned int mips_hard_regno_nregs (int regno, enum machine_mode mode) { if (ST_REG_P (regno)) return ((GET_MODE_SIZE (mode) + 3) / 4); else 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); } /* Implement TARGET_RETURN_IN_MEMORY. Under the old (i.e., 32 and O64 ABIs) all BLKmode objects are returned in memory. Under the new (N32 and 64-bit MIPS ABIs) small structures are returned in a register. Objects with varying size must still be returned in memory, of course. */ static bool mips_return_in_memory (tree type, tree fndecl ATTRIBUTE_UNUSED) { if (TARGET_OLDABI) return (TYPE_MODE (type) == BLKmode); else return ((int_size_in_bytes (type) > (2 * UNITS_PER_WORD)) || (int_size_in_bytes (type) == -1)); } static bool mips_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED) { return !TARGET_OLDABI; } /* Return true if INSN is a multiply-add or multiply-subtract instruction and PREV assigns to the accumulator operand. */ bool mips_linked_madd_p (rtx prev, rtx insn) { rtx x; x = single_set (insn); if (x == 0) return false; x = SET_SRC (x); if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == MULT && reg_set_p (XEXP (x, 1), prev)) return true; if (GET_CODE (x) == MINUS && GET_CODE (XEXP (x, 1)) == MULT && reg_set_p (XEXP (x, 0), prev)) return true; return false; } /* Used by TUNE_MACC_CHAINS to record the last scheduled instruction that may clobber hi or lo. */ static rtx mips_macc_chains_last_hilo; /* A TUNE_MACC_CHAINS helper function. Record that instruction INSN has been scheduled, updating mips_macc_chains_last_hilo appropriately. */ static void mips_macc_chains_record (rtx insn) { if (get_attr_may_clobber_hilo (insn)) mips_macc_chains_last_hilo = insn; } /* A TUNE_MACC_CHAINS helper function. Search ready queue READY, which has NREADY elements, looking for a multiply-add or multiply-subtract instruction that is cumulative with mips_macc_chains_last_hilo. If there is one, promote it ahead of anything else that might clobber hi or lo. */ static void mips_macc_chains_reorder (rtx *ready, int nready) { int i, j; if (mips_macc_chains_last_hilo != 0) for (i = nready - 1; i >= 0; i--) if (mips_linked_madd_p (mips_macc_chains_last_hilo, ready[i])) { for (j = nready - 1; j > i; j--) if (recog_memoized (ready[j]) >= 0 && get_attr_may_clobber_hilo (ready[j])) { mips_promote_ready (ready, i, j); break; } break; } } /* The last instruction to be scheduled. */ static rtx vr4130_last_insn; /* A note_stores callback used by vr4130_true_reg_dependence_p. DATA points to an rtx that is initially an instruction. Nullify the rtx if the instruction uses the value of register X. */ static void vr4130_true_reg_dependence_p_1 (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data) { rtx *insn_ptr = data; if (REG_P (x) && *insn_ptr != 0 && reg_referenced_p (x, PATTERN (*insn_ptr))) *insn_ptr = 0; } /* Return true if there is true register dependence between vr4130_last_insn and INSN. */ static bool vr4130_true_reg_dependence_p (rtx insn) { note_stores (PATTERN (vr4130_last_insn), vr4130_true_reg_dependence_p_1, &insn); return insn == 0; } /* A TUNE_MIPS4130 helper function. Given that INSN1 is at the head of the ready queue and that INSN2 is the instruction after it, return true if it is worth promoting INSN2 ahead of INSN1. Look for cases in which INSN1 and INSN2 can probably issue in parallel, but for which (INSN2, INSN1) should be less sensitive to instruction alignment than (INSN1, INSN2). See 4130.md for more details. */ static bool vr4130_swap_insns_p (rtx insn1, rtx insn2) { rtx dep; /* Check for the following case: 1) there is some other instruction X with an anti dependence on INSN1; 2) X has a higher priority than INSN2; and 3) X is an arithmetic instruction (and thus has no unit restrictions). If INSN1 is the last instruction blocking X, it would better to choose (INSN1, X) over (INSN2, INSN1). */ for (dep = INSN_DEPEND (insn1); dep != 0; dep = XEXP (dep, 1)) if (REG_NOTE_KIND (dep) == REG_DEP_ANTI && INSN_PRIORITY (XEXP (dep, 0)) > INSN_PRIORITY (insn2) && recog_memoized (XEXP (dep, 0)) >= 0 && get_attr_vr4130_class (XEXP (dep, 0)) == VR4130_CLASS_ALU) return false; if (vr4130_last_insn != 0 && recog_memoized (insn1) >= 0 && recog_memoized (insn2) >= 0) { /* See whether INSN1 and INSN2 use different execution units, or if they are both ALU-type instructions. If so, they can probably execute in parallel. */ enum attr_vr4130_class class1 = get_attr_vr4130_class (insn1); enum attr_vr4130_class class2 = get_attr_vr4130_class (insn2); if (class1 != class2 || class1 == VR4130_CLASS_ALU) { /* If only one of the instructions has a dependence on vr4130_last_insn, prefer to schedule the other one first. */ bool dep1 = vr4130_true_reg_dependence_p (insn1); bool dep2 = vr4130_true_reg_dependence_p (insn2); if (dep1 != dep2) return dep1; /* Prefer to schedule INSN2 ahead of INSN1 if vr4130_last_insn is not an ALU-type instruction and if INSN1 uses the same execution unit. (Note that if this condition holds, we already know that INSN2 uses a different execution unit.) */ if (class1 != VR4130_CLASS_ALU && recog_memoized (vr4130_last_insn) >= 0 && class1 == get_attr_vr4130_class (vr4130_last_insn)) return true; } } return false; } /* A TUNE_MIPS4130 helper function. (READY, NREADY) describes a ready queue with at least two instructions. Swap the first two if vr4130_swap_insns_p says that it could be worthwhile. */ static void vr4130_reorder (rtx *ready, int nready) { if (vr4130_swap_insns_p (ready[nready - 1], ready[nready - 2])) mips_promote_ready (ready, nready - 2, nready - 1); } /* Remove the instruction at index LOWER from ready queue READY and reinsert it in front of the instruction at index HIGHER. LOWER must be <= HIGHER. */ static void mips_promote_ready (rtx *ready, int lower, int higher) { rtx new_head; int i; new_head = ready[lower]; for (i = lower; i < higher; i++) ready[i] = ready[i + 1]; ready[i] = new_head; } /* Implement TARGET_SCHED_REORDER. */ static int mips_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED, rtx *ready, int *nreadyp, int cycle) { if (!reload_completed && TUNE_MACC_CHAINS) { if (cycle == 0) mips_macc_chains_last_hilo = 0; if (*nreadyp > 0) mips_macc_chains_reorder (ready, *nreadyp); } if (reload_completed && TUNE_MIPS4130 && !TARGET_VR4130_ALIGN) { if (cycle == 0) vr4130_last_insn = 0; if (*nreadyp > 1) vr4130_reorder (ready, *nreadyp); } return mips_issue_rate (); } /* Implement TARGET_SCHED_VARIABLE_ISSUE. */ static int mips_variable_issue (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED, rtx insn, int more) { switch (GET_CODE (PATTERN (insn))) { case USE: case CLOBBER: /* Don't count USEs and CLOBBERs against the issue rate. */ break; default: more--; if (!reload_completed && TUNE_MACC_CHAINS) mips_macc_chains_record (insn); vr4130_last_insn = insn; break; } return more; } /* Implement TARGET_SCHED_ADJUST_COST. We assume that anti and output dependencies have no cost. */ static int mips_adjust_cost (rtx insn ATTRIBUTE_UNUSED, rtx link, rtx dep ATTRIBUTE_UNUSED, int cost) { if (REG_NOTE_KIND (link) != 0) return 0; return cost; } /* Return the number of instructions that can be issued per cycle. */ static int mips_issue_rate (void) { switch (mips_tune) { case PROCESSOR_R4130: case PROCESSOR_R5400: case PROCESSOR_R5500: case PROCESSOR_R7000: case PROCESSOR_R9000: case PROCESSOR_OCTEON: return 2; case PROCESSOR_SB1: case PROCESSOR_SB1A: /* This is actually 4, but we get better performance if we claim 3. This is partly because of unwanted speculative code motion with the larger number, and partly because in most common cases we can't reach the theoretical max of 4. */ return 3; default: return 1; } } /* Implements TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD. This should be as wide as the scheduling freedom in the DFA. */ static int mips_multipass_dfa_lookahead (void) { /* Can schedule up to 4 of the 6 function units in any one cycle. */ if (TUNE_SB1) return 4; return 0; } /* Implements a store data bypass check. We need this because the cprestore pattern is type store, but defined using an UNSPEC. This UNSPEC causes the default routine to abort. We just return false for that case. */ /* ??? Should try to give a better result here than assuming false. */ int mips_store_data_bypass_p (rtx out_insn, rtx in_insn) { if (GET_CODE (PATTERN (in_insn)) == UNSPEC_VOLATILE) return false; return ! store_data_bypass_p (out_insn, in_insn); } /* Given that we have an rtx of the form (prefetch ... WRITE LOCALITY), return the first operand of the associated "pref" or "prefx" insn. */ rtx mips_prefetch_cookie (rtx write, rtx locality) { /* store_streamed / load_streamed. */ if (INTVAL (locality) <= 0) return GEN_INT (INTVAL (write) + 4); /* store / load. */ if (INTVAL (locality) <= 2) return write; /* store_retained / load_retained. */ return GEN_INT (INTVAL (write) + 6); } /* MIPS builtin function support. */ struct builtin_description { /* The code of the main .md file instruction. See mips_builtin_type for more information. */ enum insn_code icode; /* The floating-point comparison code to use with ICODE, if any. */ enum mips_fp_condition cond; /* The name of the builtin function. */ const char *name; /* Specifies how the function should be expanded. */ enum mips_builtin_type builtin_type; /* The function's prototype. */ enum mips_function_type function_type; /* The target flags required for this function. */ int target_flags; }; /* Define a MIPS_BUILTIN_DIRECT function for instruction CODE_FOR_mips_. FUNCTION_TYPE and TARGET_FLAGS are builtin_description fields. */ #define DIRECT_BUILTIN(INSN, FUNCTION_TYPE, TARGET_FLAGS) \ { CODE_FOR_mips_ ## INSN, 0, "__builtin_mips_" #INSN, \ MIPS_BUILTIN_DIRECT, FUNCTION_TYPE, TARGET_FLAGS } /* Define __builtin_mips___{s,d}, both of which require TARGET_FLAGS. */ #define CMP_SCALAR_BUILTINS(INSN, COND, TARGET_FLAGS) \ { CODE_FOR_mips_ ## INSN ## _cond_s, MIPS_FP_COND_ ## COND, \ "__builtin_mips_" #INSN "_" #COND "_s", \ MIPS_BUILTIN_CMP_SINGLE, MIPS_INT_FTYPE_SF_SF, TARGET_FLAGS }, \ { CODE_FOR_mips_ ## INSN ## _cond_d, MIPS_FP_COND_ ## COND, \ "__builtin_mips_" #INSN "_" #COND "_d", \ MIPS_BUILTIN_CMP_SINGLE, MIPS_INT_FTYPE_DF_DF, TARGET_FLAGS } /* Define __builtin_mips_{any,all,upper,lower}___ps. The lower and upper forms require TARGET_FLAGS while the any and all forms require MASK_MIPS3D. */ #define CMP_PS_BUILTINS(INSN, COND, TARGET_FLAGS) \ { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \ "__builtin_mips_any_" #INSN "_" #COND "_ps", \ MIPS_BUILTIN_CMP_ANY, MIPS_INT_FTYPE_V2SF_V2SF, MASK_MIPS3D }, \ { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \ "__builtin_mips_all_" #INSN "_" #COND "_ps", \ MIPS_BUILTIN_CMP_ALL, MIPS_INT_FTYPE_V2SF_V2SF, MASK_MIPS3D }, \ { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \ "__builtin_mips_lower_" #INSN "_" #COND "_ps", \ MIPS_BUILTIN_CMP_LOWER, MIPS_INT_FTYPE_V2SF_V2SF, TARGET_FLAGS }, \ { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \ "__builtin_mips_upper_" #INSN "_" #COND "_ps", \ MIPS_BUILTIN_CMP_UPPER, MIPS_INT_FTYPE_V2SF_V2SF, TARGET_FLAGS } /* Define __builtin_mips_{any,all}___4s. The functions require MASK_MIPS3D. */ #define CMP_4S_BUILTINS(INSN, COND) \ { CODE_FOR_mips_ ## INSN ## _cond_4s, MIPS_FP_COND_ ## COND, \ "__builtin_mips_any_" #INSN "_" #COND "_4s", \ MIPS_BUILTIN_CMP_ANY, MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF, \ MASK_MIPS3D }, \ { CODE_FOR_mips_ ## INSN ## _cond_4s, MIPS_FP_COND_ ## COND, \ "__builtin_mips_all_" #INSN "_" #COND "_4s", \ MIPS_BUILTIN_CMP_ALL, MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF, \ MASK_MIPS3D } /* Define __builtin_mips_mov{t,f}___ps. The comparison instruction requires TARGET_FLAGS. */ #define MOVTF_BUILTINS(INSN, COND, TARGET_FLAGS) \ { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \ "__builtin_mips_movt_" #INSN "_" #COND "_ps", \ MIPS_BUILTIN_MOVT, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF, \ TARGET_FLAGS }, \ { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \ "__builtin_mips_movf_" #INSN "_" #COND "_ps", \ MIPS_BUILTIN_MOVF, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF, \ TARGET_FLAGS } /* Define all the builtins related to c.cond.fmt condition COND. */ #define CMP_BUILTINS(COND) \ MOVTF_BUILTINS (c, COND, MASK_PAIRED_SINGLE_FLOAT), \ MOVTF_BUILTINS (cabs, COND, MASK_MIPS3D), \ CMP_SCALAR_BUILTINS (cabs, COND, MASK_MIPS3D), \ CMP_PS_BUILTINS (c, COND, MASK_PAIRED_SINGLE_FLOAT), \ CMP_PS_BUILTINS (cabs, COND, MASK_MIPS3D), \ CMP_4S_BUILTINS (c, COND), \ CMP_4S_BUILTINS (cabs, COND) static const struct builtin_description mips_bdesc[] = { DIRECT_BUILTIN (pll_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT), DIRECT_BUILTIN (pul_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT), DIRECT_BUILTIN (plu_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT), DIRECT_BUILTIN (puu_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT), DIRECT_BUILTIN (cvt_ps_s, MIPS_V2SF_FTYPE_SF_SF, MASK_PAIRED_SINGLE_FLOAT), DIRECT_BUILTIN (cvt_s_pl, MIPS_SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT), DIRECT_BUILTIN (cvt_s_pu, MIPS_SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT), DIRECT_BUILTIN (abs_ps, MIPS_V2SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT), DIRECT_BUILTIN (alnv_ps, MIPS_V2SF_FTYPE_V2SF_V2SF_INT, MASK_PAIRED_SINGLE_FLOAT), DIRECT_BUILTIN (addr_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D), DIRECT_BUILTIN (mulr_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D), DIRECT_BUILTIN (cvt_pw_ps, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D), DIRECT_BUILTIN (cvt_ps_pw, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D), DIRECT_BUILTIN (recip1_s, MIPS_SF_FTYPE_SF, MASK_MIPS3D), DIRECT_BUILTIN (recip1_d, MIPS_DF_FTYPE_DF, MASK_MIPS3D), DIRECT_BUILTIN (recip1_ps, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D), DIRECT_BUILTIN (recip2_s, MIPS_SF_FTYPE_SF_SF, MASK_MIPS3D), DIRECT_BUILTIN (recip2_d, MIPS_DF_FTYPE_DF_DF, MASK_MIPS3D), DIRECT_BUILTIN (recip2_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D), DIRECT_BUILTIN (rsqrt1_s, MIPS_SF_FTYPE_SF, MASK_MIPS3D), DIRECT_BUILTIN (rsqrt1_d, MIPS_DF_FTYPE_DF, MASK_MIPS3D), DIRECT_BUILTIN (rsqrt1_ps, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D), DIRECT_BUILTIN (rsqrt2_s, MIPS_SF_FTYPE_SF_SF, MASK_MIPS3D), DIRECT_BUILTIN (rsqrt2_d, MIPS_DF_FTYPE_DF_DF, MASK_MIPS3D), DIRECT_BUILTIN (rsqrt2_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D), MIPS_FP_CONDITIONS (CMP_BUILTINS) }; /* Builtin functions for the SB-1 processor. */ #define CODE_FOR_mips_sqrt_ps CODE_FOR_sqrtv2sf2 static const struct builtin_description sb1_bdesc[] = { DIRECT_BUILTIN (sqrt_ps, MIPS_V2SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT) }; /* Builtin functions for DSP ASE. */ #define CODE_FOR_mips_addq_ph CODE_FOR_addv2hi3 #define CODE_FOR_mips_addu_qb CODE_FOR_addv4qi3 #define CODE_FOR_mips_subq_ph CODE_FOR_subv2hi3 #define CODE_FOR_mips_subu_qb CODE_FOR_subv4qi3 /* Define a MIPS_BUILTIN_DIRECT_NO_TARGET function for instruction CODE_FOR_mips_. FUNCTION_TYPE and TARGET_FLAGS are builtin_description fields. */ #define DIRECT_NO_TARGET_BUILTIN(INSN, FUNCTION_TYPE, TARGET_FLAGS) \ { CODE_FOR_mips_ ## INSN, 0, "__builtin_mips_" #INSN, \ MIPS_BUILTIN_DIRECT_NO_TARGET, FUNCTION_TYPE, TARGET_FLAGS } /* Define __builtin_mips_bposge. is 32 for the MIPS32 DSP branch instruction. TARGET_FLAGS is a builtin_description field. */ #define BPOSGE_BUILTIN(VALUE, TARGET_FLAGS) \ { CODE_FOR_mips_bposge, 0, "__builtin_mips_bposge" #VALUE, \ MIPS_BUILTIN_BPOSGE ## VALUE, MIPS_SI_FTYPE_VOID, TARGET_FLAGS } static const struct builtin_description dsp_bdesc[] = { DIRECT_BUILTIN (addq_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (addq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (addq_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP), DIRECT_BUILTIN (addu_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP), DIRECT_BUILTIN (addu_s_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP), DIRECT_BUILTIN (subq_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (subq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (subq_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP), DIRECT_BUILTIN (subu_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP), DIRECT_BUILTIN (subu_s_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP), DIRECT_BUILTIN (addsc, MIPS_SI_FTYPE_SI_SI, MASK_DSP), DIRECT_BUILTIN (addwc, MIPS_SI_FTYPE_SI_SI, MASK_DSP), DIRECT_BUILTIN (modsub, MIPS_SI_FTYPE_SI_SI, MASK_DSP), DIRECT_BUILTIN (raddu_w_qb, MIPS_SI_FTYPE_V4QI, MASK_DSP), DIRECT_BUILTIN (absq_s_ph, MIPS_V2HI_FTYPE_V2HI, MASK_DSP), DIRECT_BUILTIN (absq_s_w, MIPS_SI_FTYPE_SI, MASK_DSP), DIRECT_BUILTIN (precrq_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (precrq_ph_w, MIPS_V2HI_FTYPE_SI_SI, MASK_DSP), DIRECT_BUILTIN (precrq_rs_ph_w, MIPS_V2HI_FTYPE_SI_SI, MASK_DSP), DIRECT_BUILTIN (precrqu_s_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (preceq_w_phl, MIPS_SI_FTYPE_V2HI, MASK_DSP), DIRECT_BUILTIN (preceq_w_phr, MIPS_SI_FTYPE_V2HI, MASK_DSP), DIRECT_BUILTIN (precequ_ph_qbl, MIPS_V2HI_FTYPE_V4QI, MASK_DSP), DIRECT_BUILTIN (precequ_ph_qbr, MIPS_V2HI_FTYPE_V4QI, MASK_DSP), DIRECT_BUILTIN (precequ_ph_qbla, MIPS_V2HI_FTYPE_V4QI, MASK_DSP), DIRECT_BUILTIN (precequ_ph_qbra, MIPS_V2HI_FTYPE_V4QI, MASK_DSP), DIRECT_BUILTIN (preceu_ph_qbl, MIPS_V2HI_FTYPE_V4QI, MASK_DSP), DIRECT_BUILTIN (preceu_ph_qbr, MIPS_V2HI_FTYPE_V4QI, MASK_DSP), DIRECT_BUILTIN (preceu_ph_qbla, MIPS_V2HI_FTYPE_V4QI, MASK_DSP), DIRECT_BUILTIN (preceu_ph_qbra, MIPS_V2HI_FTYPE_V4QI, MASK_DSP), DIRECT_BUILTIN (shll_qb, MIPS_V4QI_FTYPE_V4QI_SI, MASK_DSP), DIRECT_BUILTIN (shll_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP), DIRECT_BUILTIN (shll_s_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP), DIRECT_BUILTIN (shll_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP), DIRECT_BUILTIN (shrl_qb, MIPS_V4QI_FTYPE_V4QI_SI, MASK_DSP), DIRECT_BUILTIN (shra_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP), DIRECT_BUILTIN (shra_r_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP), DIRECT_BUILTIN (shra_r_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP), DIRECT_BUILTIN (muleu_s_ph_qbl, MIPS_V2HI_FTYPE_V4QI_V2HI, MASK_DSP), DIRECT_BUILTIN (muleu_s_ph_qbr, MIPS_V2HI_FTYPE_V4QI_V2HI, MASK_DSP), DIRECT_BUILTIN (mulq_rs_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (muleq_s_w_phl, MIPS_SI_FTYPE_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (muleq_s_w_phr, MIPS_SI_FTYPE_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (dpau_h_qbl, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP), DIRECT_BUILTIN (dpau_h_qbr, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP), DIRECT_BUILTIN (dpsu_h_qbl, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP), DIRECT_BUILTIN (dpsu_h_qbr, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP), DIRECT_BUILTIN (dpaq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (dpsq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (mulsaq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (dpaq_sa_l_w, MIPS_DI_FTYPE_DI_SI_SI, MASK_DSP), DIRECT_BUILTIN (dpsq_sa_l_w, MIPS_DI_FTYPE_DI_SI_SI, MASK_DSP), DIRECT_BUILTIN (maq_s_w_phl, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (maq_s_w_phr, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (maq_sa_w_phl, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (maq_sa_w_phr, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (bitrev, MIPS_SI_FTYPE_SI, MASK_DSP), DIRECT_BUILTIN (insv, MIPS_SI_FTYPE_SI_SI, MASK_DSP), DIRECT_BUILTIN (repl_qb, MIPS_V4QI_FTYPE_SI, MASK_DSP), DIRECT_BUILTIN (repl_ph, MIPS_V2HI_FTYPE_SI, MASK_DSP), DIRECT_NO_TARGET_BUILTIN (cmpu_eq_qb, MIPS_VOID_FTYPE_V4QI_V4QI, MASK_DSP), DIRECT_NO_TARGET_BUILTIN (cmpu_lt_qb, MIPS_VOID_FTYPE_V4QI_V4QI, MASK_DSP), DIRECT_NO_TARGET_BUILTIN (cmpu_le_qb, MIPS_VOID_FTYPE_V4QI_V4QI, MASK_DSP), DIRECT_BUILTIN (cmpgu_eq_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSP), DIRECT_BUILTIN (cmpgu_lt_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSP), DIRECT_BUILTIN (cmpgu_le_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSP), DIRECT_NO_TARGET_BUILTIN (cmp_eq_ph, MIPS_VOID_FTYPE_V2HI_V2HI, MASK_DSP), DIRECT_NO_TARGET_BUILTIN (cmp_lt_ph, MIPS_VOID_FTYPE_V2HI_V2HI, MASK_DSP), DIRECT_NO_TARGET_BUILTIN (cmp_le_ph, MIPS_VOID_FTYPE_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (pick_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP), DIRECT_BUILTIN (pick_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (packrl_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP), DIRECT_BUILTIN (extr_w, MIPS_SI_FTYPE_DI_SI, MASK_DSP), DIRECT_BUILTIN (extr_r_w, MIPS_SI_FTYPE_DI_SI, MASK_DSP), DIRECT_BUILTIN (extr_rs_w, MIPS_SI_FTYPE_DI_SI, MASK_DSP), DIRECT_BUILTIN (extr_s_h, MIPS_SI_FTYPE_DI_SI, MASK_DSP), DIRECT_BUILTIN (extp, MIPS_SI_FTYPE_DI_SI, MASK_DSP), DIRECT_BUILTIN (extpdp, MIPS_SI_FTYPE_DI_SI, MASK_DSP), DIRECT_BUILTIN (shilo, MIPS_DI_FTYPE_DI_SI, MASK_DSP), DIRECT_BUILTIN (mthlip, MIPS_DI_FTYPE_DI_SI, MASK_DSP), DIRECT_NO_TARGET_BUILTIN (wrdsp, MIPS_VOID_FTYPE_SI_SI, MASK_DSP), DIRECT_BUILTIN (rddsp, MIPS_SI_FTYPE_SI, MASK_DSP), DIRECT_BUILTIN (lbux, MIPS_SI_FTYPE_PTR_SI, MASK_DSP), DIRECT_BUILTIN (lhx, MIPS_SI_FTYPE_PTR_SI, MASK_DSP), DIRECT_BUILTIN (lwx, MIPS_SI_FTYPE_PTR_SI, MASK_DSP), BPOSGE_BUILTIN (32, MASK_DSP) }; /* This helps provide a mapping from builtin function codes to bdesc arrays. */ struct bdesc_map { /* The builtin function table that this entry describes. */ const struct builtin_description *bdesc; /* The number of entries in the builtin function table. */ unsigned int size; /* The target processor that supports these builtin functions. PROCESSOR_MAX means we enable them for all processors. */ enum processor_type proc; }; static const struct bdesc_map bdesc_arrays[] = { { mips_bdesc, ARRAY_SIZE (mips_bdesc), PROCESSOR_MAX }, { sb1_bdesc, ARRAY_SIZE (sb1_bdesc), PROCESSOR_SB1 }, { dsp_bdesc, ARRAY_SIZE (dsp_bdesc), PROCESSOR_MAX } }; /* Take the head of argument list *ARGLIST and convert it into a form suitable for input operand OP of instruction ICODE. Return the value and point *ARGLIST at the next element of the list. */ static rtx mips_prepare_builtin_arg (enum insn_code icode, unsigned int op, tree *arglist) { rtx value; enum machine_mode mode; value = expand_normal (TREE_VALUE (*arglist)); mode = insn_data[icode].operand[op].mode; if (!insn_data[icode].operand[op].predicate (value, mode)) { value = copy_to_mode_reg (mode, value); /* Check the predicate again. */ if (!insn_data[icode].operand[op].predicate (value, mode)) { error ("invalid argument to builtin function"); return const0_rtx; } } *arglist = TREE_CHAIN (*arglist); return value; } /* Return an rtx suitable for output operand OP of instruction ICODE. If TARGET is non-null, try to use it where possible. */ static rtx mips_prepare_builtin_target (enum insn_code icode, unsigned int op, rtx target) { enum machine_mode mode; mode = insn_data[icode].operand[op].mode; if (target == 0 || !insn_data[icode].operand[op].predicate (target, mode)) target = gen_reg_rtx (mode); return target; } /* Expand builtin functions. This is called from TARGET_EXPAND_BUILTIN. */ rtx mips_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, enum machine_mode mode ATTRIBUTE_UNUSED, int ignore ATTRIBUTE_UNUSED) { enum insn_code icode; enum mips_builtin_type type; tree fndecl, arglist; unsigned int fcode; const struct builtin_description *bdesc; const struct bdesc_map *m; fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); arglist = TREE_OPERAND (exp, 1); fcode = DECL_FUNCTION_CODE (fndecl); bdesc = NULL; for (m = bdesc_arrays; m < &bdesc_arrays[ARRAY_SIZE (bdesc_arrays)]; m++) { if (fcode < m->size) { bdesc = m->bdesc; icode = bdesc[fcode].icode; type = bdesc[fcode].builtin_type; break; } fcode -= m->size; } if (bdesc == NULL) return 0; switch (type) { case MIPS_BUILTIN_DIRECT: return mips_expand_builtin_direct (icode, target, arglist, true); case MIPS_BUILTIN_DIRECT_NO_TARGET: return mips_expand_builtin_direct (icode, target, arglist, false); case MIPS_BUILTIN_MOVT: case MIPS_BUILTIN_MOVF: return mips_expand_builtin_movtf (type, icode, bdesc[fcode].cond, target, arglist); case MIPS_BUILTIN_CMP_ANY: case MIPS_BUILTIN_CMP_ALL: case MIPS_BUILTIN_CMP_UPPER: case MIPS_BUILTIN_CMP_LOWER: case MIPS_BUILTIN_CMP_SINGLE: return mips_expand_builtin_compare (type, icode, bdesc[fcode].cond, target, arglist); case MIPS_BUILTIN_BPOSGE32: return mips_expand_builtin_bposge (type, target); default: return 0; } } /* Init builtin functions. This is called from TARGET_INIT_BUILTIN. */ void mips_init_builtins (void) { const struct builtin_description *d; const struct bdesc_map *m; tree types[(int) MIPS_MAX_FTYPE_MAX]; tree V2SF_type_node; tree V2HI_type_node; tree V4QI_type_node; unsigned int offset; /* We have only builtins for -mpaired-single, -mips3d and -mdsp. */ if (!TARGET_PAIRED_SINGLE_FLOAT && !TARGET_DSP) return; if (TARGET_PAIRED_SINGLE_FLOAT) { V2SF_type_node = build_vector_type_for_mode (float_type_node, V2SFmode); types[MIPS_V2SF_FTYPE_V2SF] = build_function_type_list (V2SF_type_node, V2SF_type_node, NULL_TREE); types[MIPS_V2SF_FTYPE_V2SF_V2SF] = build_function_type_list (V2SF_type_node, V2SF_type_node, V2SF_type_node, NULL_TREE); types[MIPS_V2SF_FTYPE_V2SF_V2SF_INT] = build_function_type_list (V2SF_type_node, V2SF_type_node, V2SF_type_node, integer_type_node, NULL_TREE); types[MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF] = build_function_type_list (V2SF_type_node, V2SF_type_node, V2SF_type_node, V2SF_type_node, V2SF_type_node, NULL_TREE); types[MIPS_V2SF_FTYPE_SF_SF] = build_function_type_list (V2SF_type_node, float_type_node, float_type_node, NULL_TREE); types[MIPS_INT_FTYPE_V2SF_V2SF] = build_function_type_list (integer_type_node, V2SF_type_node, V2SF_type_node, NULL_TREE); types[MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF] = build_function_type_list (integer_type_node, V2SF_type_node, V2SF_type_node, V2SF_type_node, V2SF_type_node, NULL_TREE); types[MIPS_INT_FTYPE_SF_SF] = build_function_type_list (integer_type_node, float_type_node, float_type_node, NULL_TREE); types[MIPS_INT_FTYPE_DF_DF] = build_function_type_list (integer_type_node, double_type_node, double_type_node, NULL_TREE); types[MIPS_SF_FTYPE_V2SF] = build_function_type_list (float_type_node, V2SF_type_node, NULL_TREE); types[MIPS_SF_FTYPE_SF] = build_function_type_list (float_type_node, float_type_node, NULL_TREE); types[MIPS_SF_FTYPE_SF_SF] = build_function_type_list (float_type_node, float_type_node, float_type_node, NULL_TREE); types[MIPS_DF_FTYPE_DF] = build_function_type_list (double_type_node, double_type_node, NULL_TREE); types[MIPS_DF_FTYPE_DF_DF] = build_function_type_list (double_type_node, double_type_node, double_type_node, NULL_TREE); } if (TARGET_DSP) { V2HI_type_node = build_vector_type_for_mode (intHI_type_node, V2HImode); V4QI_type_node = build_vector_type_for_mode (intQI_type_node, V4QImode); types[MIPS_V2HI_FTYPE_V2HI_V2HI] = build_function_type_list (V2HI_type_node, V2HI_type_node, V2HI_type_node, NULL_TREE); types[MIPS_SI_FTYPE_SI_SI] = build_function_type_list (intSI_type_node, intSI_type_node, intSI_type_node, NULL_TREE); types[MIPS_V4QI_FTYPE_V4QI_V4QI] = build_function_type_list (V4QI_type_node, V4QI_type_node, V4QI_type_node, NULL_TREE); types[MIPS_SI_FTYPE_V4QI] = build_function_type_list (intSI_type_node, V4QI_type_node, NULL_TREE); types[MIPS_V2HI_FTYPE_V2HI] = build_function_type_list (V2HI_type_node, V2HI_type_node, NULL_TREE); types[MIPS_SI_FTYPE_SI] = build_function_type_list (intSI_type_node, intSI_type_node, NULL_TREE); types[MIPS_V4QI_FTYPE_V2HI_V2HI] = build_function_type_list (V4QI_type_node, V2HI_type_node, V2HI_type_node, NULL_TREE); types[MIPS_V2HI_FTYPE_SI_SI] = build_function_type_list (V2HI_type_node, intSI_type_node, intSI_type_node, NULL_TREE); types[MIPS_SI_FTYPE_V2HI] = build_function_type_list (intSI_type_node, V2HI_type_node, NULL_TREE); types[MIPS_V2HI_FTYPE_V4QI] = build_function_type_list (V2HI_type_node, V4QI_type_node, NULL_TREE); types[MIPS_V4QI_FTYPE_V4QI_SI] = build_function_type_list (V4QI_type_node, V4QI_type_node, intSI_type_node, NULL_TREE); types[MIPS_V2HI_FTYPE_V2HI_SI] = build_function_type_list (V2HI_type_node, V2HI_type_node, intSI_type_node, NULL_TREE); types[MIPS_V2HI_FTYPE_V4QI_V2HI] = build_function_type_list (V2HI_type_node, V4QI_type_node, V2HI_type_node, NULL_TREE); types[MIPS_SI_FTYPE_V2HI_V2HI] = build_function_type_list (intSI_type_node, V2HI_type_node, V2HI_type_node, NULL_TREE); types[MIPS_DI_FTYPE_DI_V4QI_V4QI] = build_function_type_list (intDI_type_node, intDI_type_node, V4QI_type_node, V4QI_type_node, NULL_TREE); types[MIPS_DI_FTYPE_DI_V2HI_V2HI] = build_function_type_list (intDI_type_node, intDI_type_node, V2HI_type_node, V2HI_type_node, NULL_TREE); types[MIPS_DI_FTYPE_DI_SI_SI] = build_function_type_list (intDI_type_node, intDI_type_node, intSI_type_node, intSI_type_node, NULL_TREE); types[MIPS_V4QI_FTYPE_SI] = build_function_type_list (V4QI_type_node, intSI_type_node, NULL_TREE); types[MIPS_V2HI_FTYPE_SI] = build_function_type_list (V2HI_type_node, intSI_type_node, NULL_TREE); types[MIPS_VOID_FTYPE_V4QI_V4QI] = build_function_type_list (void_type_node, V4QI_type_node, V4QI_type_node, NULL_TREE); types[MIPS_SI_FTYPE_V4QI_V4QI] = build_function_type_list (intSI_type_node, V4QI_type_node, V4QI_type_node, NULL_TREE); types[MIPS_VOID_FTYPE_V2HI_V2HI] = build_function_type_list (void_type_node, V2HI_type_node, V2HI_type_node, NULL_TREE); types[MIPS_SI_FTYPE_DI_SI] = build_function_type_list (intSI_type_node, intDI_type_node, intSI_type_node, NULL_TREE); types[MIPS_DI_FTYPE_DI_SI] = build_function_type_list (intDI_type_node, intDI_type_node, intSI_type_node, NULL_TREE); types[MIPS_VOID_FTYPE_SI_SI] = build_function_type_list (void_type_node, intSI_type_node, intSI_type_node, NULL_TREE); types[MIPS_SI_FTYPE_PTR_SI] = build_function_type_list (intSI_type_node, ptr_type_node, intSI_type_node, NULL_TREE); types[MIPS_SI_FTYPE_VOID] = build_function_type (intSI_type_node, void_list_node); } /* Iterate through all of the bdesc arrays, initializing all of the builtin functions. */ offset = 0; for (m = bdesc_arrays; m < &bdesc_arrays[ARRAY_SIZE (bdesc_arrays)]; m++) { if (m->proc == PROCESSOR_MAX || (m->proc == mips_arch)) for (d = m->bdesc; d < &m->bdesc[m->size]; d++) if ((d->target_flags & target_flags) == d->target_flags) lang_hooks.builtin_function (d->name, types[d->function_type], d - m->bdesc + offset, BUILT_IN_MD, NULL, NULL); offset += m->size; } } /* Expand a MIPS_BUILTIN_DIRECT function. ICODE is the code of the .md pattern and ARGLIST is the list of function arguments. TARGET, if nonnull, suggests a good place to put the result. HAS_TARGET indicates the function must return something. */ static rtx mips_expand_builtin_direct (enum insn_code icode, rtx target, tree arglist, bool has_target) { rtx ops[MAX_RECOG_OPERANDS]; int i = 0; if (has_target) { /* We save target to ops[0]. */ ops[0] = mips_prepare_builtin_target (icode, 0, target); i = 1; } /* We need to test if arglist is not zero. Some instructions have extra clobber registers. */ for (; i < insn_data[icode].n_operands && arglist != 0; i++) ops[i] = mips_prepare_builtin_arg (icode, i, &arglist); switch (i) { case 2: emit_insn (GEN_FCN (icode) (ops[0], ops[1])); break; case 3: emit_insn (GEN_FCN (icode) (ops[0], ops[1], ops[2])); break; case 4: emit_insn (GEN_FCN (icode) (ops[0], ops[1], ops[2], ops[3])); break; default: gcc_unreachable (); } return target; } /* Expand a __builtin_mips_movt_*_ps() or __builtin_mips_movf_*_ps() function (TYPE says which). ARGLIST is the list of arguments to the function, ICODE is the instruction that should be used to compare the first two arguments, and COND is the condition it should test. TARGET, if nonnull, suggests a good place to put the result. */ static rtx mips_expand_builtin_movtf (enum mips_builtin_type type, enum insn_code icode, enum mips_fp_condition cond, rtx target, tree arglist) { rtx cmp_result, op0, op1; cmp_result = mips_prepare_builtin_target (icode, 0, 0); op0 = mips_prepare_builtin_arg (icode, 1, &arglist); op1 = mips_prepare_builtin_arg (icode, 2, &arglist); emit_insn (GEN_FCN (icode) (cmp_result, op0, op1, GEN_INT (cond))); icode = CODE_FOR_mips_cond_move_tf_ps; target = mips_prepare_builtin_target (icode, 0, target); if (type == MIPS_BUILTIN_MOVT) { op1 = mips_prepare_builtin_arg (icode, 2, &arglist); op0 = mips_prepare_builtin_arg (icode, 1, &arglist); } else { op0 = mips_prepare_builtin_arg (icode, 1, &arglist); op1 = mips_prepare_builtin_arg (icode, 2, &arglist); } emit_insn (gen_mips_cond_move_tf_ps (target, op0, op1, cmp_result)); return target; } /* Move VALUE_IF_TRUE into TARGET if CONDITION is true; move VALUE_IF_FALSE into TARGET otherwise. Return TARGET. */ static rtx mips_builtin_branch_and_move (rtx condition, rtx target, rtx value_if_true, rtx value_if_false) { rtx true_label, done_label; true_label = gen_label_rtx (); done_label = gen_label_rtx (); /* First assume that CONDITION is false. */ emit_move_insn (target, value_if_false); /* Branch to TRUE_LABEL if CONDITION is true and DONE_LABEL otherwise. */ emit_jump_insn (gen_condjump (condition, true_label)); emit_jump_insn (gen_jump (done_label)); emit_barrier (); /* Fix TARGET if CONDITION is true. */ emit_label (true_label); emit_move_insn (target, value_if_true); emit_label (done_label); return target; } /* Expand a comparison builtin of type BUILTIN_TYPE. ICODE is the code of the comparison instruction and COND is the condition it should test. ARGLIST is the list of function arguments and TARGET, if nonnull, suggests a good place to put the boolean result. */ static rtx mips_expand_builtin_compare (enum mips_builtin_type builtin_type, enum insn_code icode, enum mips_fp_condition cond, rtx target, tree arglist) { rtx offset, condition, cmp_result, ops[MAX_RECOG_OPERANDS]; int i; if (target == 0 || GET_MODE (target) != SImode) target = gen_reg_rtx (SImode); /* Prepare the operands to the comparison. */ cmp_result = mips_prepare_builtin_target (icode, 0, 0); for (i = 1; i < insn_data[icode].n_operands - 1; i++) ops[i] = mips_prepare_builtin_arg (icode, i, &arglist); switch (insn_data[icode].n_operands) { case 4: emit_insn (GEN_FCN (icode) (cmp_result, ops[1], ops[2], GEN_INT (cond))); break; case 6: emit_insn (GEN_FCN (icode) (cmp_result, ops[1], ops[2], ops[3], ops[4], GEN_INT (cond))); break; default: gcc_unreachable (); } /* If the comparison sets more than one register, we define the result to be 0 if all registers are false and -1 if all registers are true. The value of the complete result is indeterminate otherwise. */ switch (builtin_type) { case MIPS_BUILTIN_CMP_ALL: condition = gen_rtx_NE (VOIDmode, cmp_result, constm1_rtx); return mips_builtin_branch_and_move (condition, target, const0_rtx, const1_rtx); case MIPS_BUILTIN_CMP_UPPER: case MIPS_BUILTIN_CMP_LOWER: offset = GEN_INT (builtin_type == MIPS_BUILTIN_CMP_UPPER); condition = gen_single_cc (cmp_result, offset); return mips_builtin_branch_and_move (condition, target, const1_rtx, const0_rtx); default: condition = gen_rtx_NE (VOIDmode, cmp_result, const0_rtx); return mips_builtin_branch_and_move (condition, target, const1_rtx, const0_rtx); } } /* Expand a bposge builtin of type BUILTIN_TYPE. TARGET, if nonnull, suggests a good place to put the boolean result. */ static rtx mips_expand_builtin_bposge (enum mips_builtin_type builtin_type, rtx target) { rtx condition, cmp_result; int cmp_value; if (target == 0 || GET_MODE (target) != SImode) target = gen_reg_rtx (SImode); cmp_result = gen_rtx_REG (CCDSPmode, CCDSP_PO_REGNUM); if (builtin_type == MIPS_BUILTIN_BPOSGE32) cmp_value = 32; else gcc_assert (0); condition = gen_rtx_GE (VOIDmode, cmp_result, GEN_INT (cmp_value)); return mips_builtin_branch_and_move (condition, target, const1_rtx, const0_rtx); } /* Set SYMBOL_REF_FLAGS for the SYMBOL_REF inside RTL, which belongs to DECL. FIRST is true if this is the first time handling this decl. */ static void mips_encode_section_info (tree decl, rtx rtl, int first) { default_encode_section_info (decl, rtl, first); if (TREE_CODE (decl) == FUNCTION_DECL && lookup_attribute ("long_call", TYPE_ATTRIBUTES (TREE_TYPE (decl)))) { rtx symbol = XEXP (rtl, 0); SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_LONG_CALL; } } /* Implement TARGET_EXTRA_LIVE_ON_ENTRY. PIC_FUNCTION_ADDR_REGNUM is live on entry to a function when generating -mshared abicalls code. */ static void mips_extra_live_on_entry (bitmap regs) { if (TARGET_ABICALLS && !TARGET_ABSOLUTE_ABICALLS) bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM); } /* SImode values are represented as sign-extended to DImode. */ int mips_mode_rep_extended (enum machine_mode mode, enum machine_mode mode_rep) { if (TARGET_64BIT && mode == SImode && mode_rep == DImode) return SIGN_EXTEND; return UNKNOWN; } #include "gt-mips.h" Index: head/contrib/gcc/config/mips/mips.h =================================================================== --- head/contrib/gcc/config/mips/mips.h (revision 312898) +++ head/contrib/gcc/config/mips/mips.h (revision 312899) @@ -1,2740 +1,2743 @@ /* Definitions of target machine for GNU compiler. MIPS version. Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. Contributed by A. Lichnewsky (lich@inria.inria.fr). Changed by Michael Meissner (meissner@osf.org). 64 bit r4000 support by Ian Lance Taylor (ian@cygnus.com) and Brendan Eich (brendan@microunity.com). This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* MIPS external variables defined in mips.c. */ /* Which processor to schedule for. Since there is no difference between a R2000 and R3000 in terms of the scheduler, we collapse them into just an R3000. The elements of the enumeration must match exactly the cpu attribute in the mips.md machine description. */ enum processor_type { PROCESSOR_R3000, PROCESSOR_4KC, PROCESSOR_4KP, PROCESSOR_5KC, PROCESSOR_5KF, PROCESSOR_20KC, PROCESSOR_24K, PROCESSOR_24KX, PROCESSOR_M4K, PROCESSOR_OCTEON, PROCESSOR_R3900, PROCESSOR_R6000, PROCESSOR_R4000, PROCESSOR_R4100, PROCESSOR_R4111, PROCESSOR_R4120, PROCESSOR_R4130, PROCESSOR_R4300, PROCESSOR_R4600, PROCESSOR_R4650, PROCESSOR_R5000, PROCESSOR_R5400, PROCESSOR_R5500, PROCESSOR_R7000, PROCESSOR_R8000, PROCESSOR_R9000, PROCESSOR_SB1, PROCESSOR_SB1A, PROCESSOR_SR71000, PROCESSOR_MAX }; /* Costs of various operations on the different architectures. */ struct mips_rtx_cost_data { unsigned short fp_add; unsigned short fp_mult_sf; unsigned short fp_mult_df; unsigned short fp_div_sf; unsigned short fp_div_df; unsigned short int_mult_si; unsigned short int_mult_di; unsigned short int_div_si; unsigned short int_div_di; unsigned short branch_cost; unsigned short memory_latency; }; /* Which ABI to use. ABI_32 (original 32, or o32), ABI_N32 (n32), ABI_64 (n64) are all defined by SGI. ABI_O64 is o32 extended to work on a 64 bit machine. */ #define ABI_32 0 #define ABI_N32 1 #define ABI_64 2 #define ABI_EABI 3 #define ABI_O64 4 /* Information about one recognized processor. Defined here for the benefit of TARGET_CPU_CPP_BUILTINS. */ struct mips_cpu_info { /* The 'canonical' name of the processor as far as GCC is concerned. It's typically a manufacturer's prefix followed by a numerical designation. It should be lower case. */ const char *name; /* The internal processor number that most closely matches this entry. Several processors can have the same value, if there's no difference between them from GCC's point of view. */ enum processor_type cpu; /* The ISA level that the processor implements. */ int isa; }; #ifndef USED_FOR_TARGET extern char mips_print_operand_punct[256]; /* print_operand punctuation chars */ extern const char *current_function_file; /* filename current function is in */ extern int num_source_filenames; /* current .file # */ extern int mips_section_threshold; /* # bytes of data/sdata cutoff */ extern int sym_lineno; /* sgi next label # for each stmt */ extern int set_noreorder; /* # of nested .set noreorder's */ extern int set_nomacro; /* # of nested .set nomacro's */ extern int set_noat; /* # of nested .set noat's */ extern int set_volatile; /* # of nested .set volatile's */ extern int mips_branch_likely; /* emit 'l' after br (branch likely) */ extern int mips_dbx_regno[]; /* Map register # to debug register # */ extern bool mips_split_p[]; extern GTY(()) rtx cmp_operands[2]; extern enum processor_type mips_arch; /* which cpu to codegen for */ extern enum processor_type mips_tune; /* which cpu to schedule for */ extern int mips_isa; /* architectural level */ extern int mips_abi; /* which ABI to use */ extern int mips16_hard_float; /* mips16 without -msoft-float */ extern const struct mips_cpu_info mips_cpu_info_table[]; extern const struct mips_cpu_info *mips_arch_info; extern const struct mips_cpu_info *mips_tune_info; extern const struct mips_rtx_cost_data *mips_cost; #endif /* Macros to silence warnings about numbers being signed in traditional C and unsigned in ISO C when compiled on 32-bit hosts. */ #define BITMASK_HIGH (((unsigned long)1) << 31) /* 0x80000000 */ #define BITMASK_UPPER16 ((unsigned long)0xffff << 16) /* 0xffff0000 */ #define BITMASK_LOWER16 ((unsigned long)0xffff) /* 0x0000ffff */ /* Run-time compilation parameters selecting different hardware subsets. */ /* True if the call patterns should be split into a jalr followed by an instruction to restore $gp. This is only ever true for SVR4 PIC, in which $gp is call-clobbered. It is only safe to split the load from the call when every use of $gp is explicit. */ #define TARGET_SPLIT_CALLS \ (TARGET_EXPLICIT_RELOCS && TARGET_ABICALLS && !TARGET_NEWABI) /* True if we're generating a form of -mabicalls in which we can use operators like %hi and %lo to refer to locally-binding symbols. We can only do this for -mno-shared, and only then if we can use relocation operations instead of assembly macros. It isn't really worth using absolute sequences for 64-bit symbols because GOT accesses are so much shorter. */ #define TARGET_ABSOLUTE_ABICALLS \ (TARGET_ABICALLS \ && !TARGET_SHARED \ && TARGET_EXPLICIT_RELOCS \ && !ABI_HAS_64BIT_SYMBOLS) /* True if we can optimize sibling calls. For simplicity, we only handle cases in which call_insn_operand will reject invalid sibcall addresses. There are two cases in which this isn't true: - TARGET_MIPS16. call_insn_operand accepts constant addresses but there is no direct jump instruction. It isn't worth using sibling calls in this case anyway; they would usually be longer than normal calls. - TARGET_ABICALLS && !TARGET_EXPLICIT_RELOCS. call_insn_operand accepts global constants, but "jr $25" is the only allowed sibcall. */ #define TARGET_SIBCALLS \ (!TARGET_MIPS16 && (!TARGET_ABICALLS || TARGET_EXPLICIT_RELOCS)) /* True if .gpword or .gpdword should be used for switch tables. Although GAS does understand .gpdword, the SGI linker mishandles the relocations GAS generates (R_MIPS_GPREL32 followed by R_MIPS_64). We therefore disable GP-relative switch tables for n64 on IRIX targets. */ #define TARGET_GPWORD (TARGET_ABICALLS && !(mips_abi == ABI_64 && TARGET_IRIX)) /* Generate mips16 code */ #define TARGET_MIPS16 ((target_flags & MASK_MIPS16) != 0) /* Generate mips16e code. Default 16bit ASE for mips32/mips32r2/mips64 */ #define GENERATE_MIPS16E (TARGET_MIPS16 && mips_isa >= 32) /* Generic ISA defines. */ #define ISA_MIPS1 (mips_isa == 1) #define ISA_MIPS2 (mips_isa == 2) #define ISA_MIPS3 (mips_isa == 3) #define ISA_MIPS4 (mips_isa == 4) #define ISA_MIPS32 (mips_isa == 32) #define ISA_MIPS32R2 (mips_isa == 33) #define ISA_MIPS64 (mips_isa == 64) #define ISA_MIPS64R2 (mips_isa == 65) /* Architecture target defines. */ #define TARGET_MIPS3900 (mips_arch == PROCESSOR_R3900) #define TARGET_MIPS4000 (mips_arch == PROCESSOR_R4000) #define TARGET_MIPS4120 (mips_arch == PROCESSOR_R4120) #define TARGET_MIPS4130 (mips_arch == PROCESSOR_R4130) #define TARGET_MIPS5400 (mips_arch == PROCESSOR_R5400) #define TARGET_MIPS5500 (mips_arch == PROCESSOR_R5500) #define TARGET_MIPS7000 (mips_arch == PROCESSOR_R7000) #define TARGET_MIPS9000 (mips_arch == PROCESSOR_R9000) #define TARGET_SB1 (mips_arch == PROCESSOR_SB1 \ || mips_arch == PROCESSOR_SB1A) #define TARGET_SR71K (mips_arch == PROCESSOR_SR71000) #define TARGET_OCTEON (mips_arch == PROCESSOR_OCTEON) /* Scheduling target defines. */ #define TUNE_MIPS3000 (mips_tune == PROCESSOR_R3000) #define TUNE_MIPS3900 (mips_tune == PROCESSOR_R3900) #define TUNE_MIPS4000 (mips_tune == PROCESSOR_R4000) #define TUNE_MIPS4120 (mips_tune == PROCESSOR_R4120) #define TUNE_MIPS4130 (mips_tune == PROCESSOR_R4130) #define TUNE_MIPS5000 (mips_tune == PROCESSOR_R5000) #define TUNE_MIPS5400 (mips_tune == PROCESSOR_R5400) #define TUNE_MIPS5500 (mips_tune == PROCESSOR_R5500) #define TUNE_MIPS6000 (mips_tune == PROCESSOR_R6000) #define TUNE_MIPS7000 (mips_tune == PROCESSOR_R7000) #define TUNE_MIPS9000 (mips_tune == PROCESSOR_R9000) #define TUNE_SB1 (mips_tune == PROCESSOR_SB1 \ || mips_tune == PROCESSOR_SB1A) #define TUNE_OCTEON (mips_tune == PROCESSOR_OCTEON) /* True if the pre-reload scheduler should try to create chains of multiply-add or multiply-subtract instructions. For example, suppose we have: t1 = a * b t2 = t1 + c * d t3 = e * f t4 = t3 - g * h t1 will have a higher priority than t2 and t3 will have a higher priority than t4. However, before reload, there is no dependence between t1 and t3, and they can often have similar priorities. The scheduler will then tend to prefer: t1 = a * b t3 = e * f t2 = t1 + c * d t4 = t3 - g * h which stops us from making full use of macc/madd-style instructions. This sort of situation occurs frequently in Fourier transforms and in unrolled loops. To counter this, the TUNE_MACC_CHAINS code will reorder the ready queue so that chained multiply-add and multiply-subtract instructions appear ahead of any other instruction that is likely to clobber lo. In the example above, if t2 and t3 become ready at the same time, the code ensures that t2 is scheduled first. Multiply-accumulate instructions are a bigger win for some targets than others, so this macro is defined on an opt-in basis. */ #define TUNE_MACC_CHAINS (TUNE_MIPS5500 \ || TUNE_MIPS4120 \ || TUNE_MIPS4130) #define TARGET_OLDABI (mips_abi == ABI_32 || mips_abi == ABI_O64) #define TARGET_NEWABI (mips_abi == ABI_N32 || mips_abi == ABI_64) /* IRIX specific stuff. */ #define TARGET_IRIX 0 #define TARGET_IRIX6 0 /* Define preprocessor macros for the -march and -mtune options. PREFIX is either _MIPS_ARCH or _MIPS_TUNE, INFO is the selected processor. If INFO's canonical name is "foo", define PREFIX to be "foo", and define an additional macro PREFIX_FOO. */ #define MIPS_CPP_SET_PROCESSOR(PREFIX, INFO) \ do \ { \ char *macro, *p; \ \ macro = concat ((PREFIX), "_", (INFO)->name, NULL); \ for (p = macro; *p != 0; p++) \ - *p = TOUPPER (*p); \ + if (*p == '+') \ + *p = 'P'; \ + else \ + *p = TOUPPER (*p); \ \ builtin_define (macro); \ builtin_define_with_value ((PREFIX), (INFO)->name, 1); \ free (macro); \ } \ while (0) /* Target CPU builtins. */ #define TARGET_CPU_CPP_BUILTINS() \ do \ { \ /* Everyone but IRIX defines this to mips. */ \ if (!TARGET_IRIX) \ builtin_assert ("machine=mips"); \ \ builtin_assert ("cpu=mips"); \ builtin_define ("__mips__"); \ builtin_define ("_mips"); \ \ /* We do this here because __mips is defined below \ and so we can't use builtin_define_std. */ \ if (!flag_iso) \ builtin_define ("mips"); \ \ if (TARGET_64BIT) \ builtin_define ("__mips64"); \ \ if (!TARGET_IRIX) \ { \ /* Treat _R3000 and _R4000 like register-size \ defines, which is how they've historically \ been used. */ \ if (TARGET_64BIT) \ { \ builtin_define_std ("R4000"); \ builtin_define ("_R4000"); \ } \ else \ { \ builtin_define_std ("R3000"); \ builtin_define ("_R3000"); \ } \ } \ if (TARGET_FLOAT64) \ builtin_define ("__mips_fpr=64"); \ else \ builtin_define ("__mips_fpr=32"); \ \ if (TARGET_MIPS16) \ builtin_define ("__mips16"); \ \ if (TARGET_MIPS3D) \ builtin_define ("__mips3d"); \ \ if (TARGET_DSP) \ builtin_define ("__mips_dsp"); \ \ MIPS_CPP_SET_PROCESSOR ("_MIPS_ARCH", mips_arch_info); \ MIPS_CPP_SET_PROCESSOR ("_MIPS_TUNE", mips_tune_info); \ \ if (ISA_MIPS1) \ { \ builtin_define ("__mips=1"); \ builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS1"); \ } \ else if (ISA_MIPS2) \ { \ builtin_define ("__mips=2"); \ builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS2"); \ } \ else if (ISA_MIPS3) \ { \ builtin_define ("__mips=3"); \ builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS3"); \ } \ else if (ISA_MIPS4) \ { \ builtin_define ("__mips=4"); \ builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS4"); \ } \ else if (ISA_MIPS32) \ { \ builtin_define ("__mips=32"); \ builtin_define ("__mips_isa_rev=1"); \ builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS32"); \ } \ else if (ISA_MIPS32R2) \ { \ builtin_define ("__mips=32"); \ builtin_define ("__mips_isa_rev=2"); \ builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS32"); \ } \ else if (ISA_MIPS64) \ { \ builtin_define ("__mips=64"); \ builtin_define ("__mips_isa_rev=1"); \ builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS64"); \ } \ else if (ISA_MIPS64R2) \ { \ builtin_define ("__mips=64"); \ builtin_define ("__mips_isa_rev=2"); \ builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS64"); \ } \ \ if (TARGET_HARD_FLOAT) \ builtin_define ("__mips_hard_float"); \ else if (TARGET_SOFT_FLOAT) \ builtin_define ("__mips_soft_float"); \ \ if (TARGET_SINGLE_FLOAT) \ builtin_define ("__mips_single_float"); \ \ if (TARGET_PAIRED_SINGLE_FLOAT) \ builtin_define ("__mips_paired_single_float"); \ \ if (TARGET_BIG_ENDIAN) \ { \ builtin_define_std ("MIPSEB"); \ builtin_define ("_MIPSEB"); \ } \ else \ { \ builtin_define_std ("MIPSEL"); \ builtin_define ("_MIPSEL"); \ } \ \ /* Macros dependent on the C dialect. */ \ if (preprocessing_asm_p ()) \ { \ builtin_define_std ("LANGUAGE_ASSEMBLY"); \ builtin_define ("_LANGUAGE_ASSEMBLY"); \ } \ else if (c_dialect_cxx ()) \ { \ builtin_define ("_LANGUAGE_C_PLUS_PLUS"); \ builtin_define ("__LANGUAGE_C_PLUS_PLUS"); \ builtin_define ("__LANGUAGE_C_PLUS_PLUS__"); \ } \ else \ { \ builtin_define_std ("LANGUAGE_C"); \ builtin_define ("_LANGUAGE_C"); \ } \ if (c_dialect_objc ()) \ { \ builtin_define ("_LANGUAGE_OBJECTIVE_C"); \ builtin_define ("__LANGUAGE_OBJECTIVE_C"); \ /* Bizarre, but needed at least for Irix. */ \ builtin_define_std ("LANGUAGE_C"); \ builtin_define ("_LANGUAGE_C"); \ } \ \ if (mips_abi == ABI_EABI) \ builtin_define ("__mips_eabi"); \ \ } while (0) /* Default target_flags if no switches are specified */ #ifndef TARGET_DEFAULT #define TARGET_DEFAULT 0 #endif #ifndef TARGET_CPU_DEFAULT #define TARGET_CPU_DEFAULT 0 #endif #ifndef TARGET_ENDIAN_DEFAULT #define TARGET_ENDIAN_DEFAULT MASK_BIG_ENDIAN #endif #ifndef TARGET_FP_EXCEPTIONS_DEFAULT #define TARGET_FP_EXCEPTIONS_DEFAULT MASK_FP_EXCEPTIONS #endif /* 'from-abi' makes a good default: you get whatever the ABI requires. */ #ifndef MIPS_ISA_DEFAULT #ifndef MIPS_CPU_STRING_DEFAULT #define MIPS_CPU_STRING_DEFAULT "from-abi" #endif #endif #ifdef IN_LIBGCC2 #undef TARGET_64BIT /* Make this compile time constant for libgcc2 */ #ifdef __mips64 #define TARGET_64BIT 1 #else #define TARGET_64BIT 0 #endif #endif /* IN_LIBGCC2 */ #define TARGET_LIBGCC_SDATA_SECTION ".sdata" #ifndef MULTILIB_ENDIAN_DEFAULT #if TARGET_ENDIAN_DEFAULT == 0 #define MULTILIB_ENDIAN_DEFAULT "EL" #else #define MULTILIB_ENDIAN_DEFAULT "EB" #endif #endif #ifndef MULTILIB_ISA_DEFAULT # if MIPS_ISA_DEFAULT == 1 # define MULTILIB_ISA_DEFAULT "mips1" # else # if MIPS_ISA_DEFAULT == 2 # define MULTILIB_ISA_DEFAULT "mips2" # else # if MIPS_ISA_DEFAULT == 3 # define MULTILIB_ISA_DEFAULT "mips3" # else # if MIPS_ISA_DEFAULT == 4 # define MULTILIB_ISA_DEFAULT "mips4" # else # if MIPS_ISA_DEFAULT == 32 # define MULTILIB_ISA_DEFAULT "mips32" # else # if MIPS_ISA_DEFAULT == 33 # define MULTILIB_ISA_DEFAULT "mips32r2" # else # if MIPS_ISA_DEFAULT == 64 # define MULTILIB_ISA_DEFAULT "mips64" # else # if MIPS_ISA_DEFAULT == 65 # define MULTILIB_ISA_DEFAULT "mips64r2" # else # define MULTILIB_ISA_DEFAULT "mips1" # endif # endif # endif # endif # endif # endif # endif # endif #endif #ifndef MULTILIB_DEFAULTS #define MULTILIB_DEFAULTS \ { MULTILIB_ENDIAN_DEFAULT, MULTILIB_ISA_DEFAULT, MULTILIB_ABI_DEFAULT } #endif /* We must pass -EL to the linker by default for little endian embedded targets using linker scripts with a OUTPUT_FORMAT line. Otherwise, the linker will default to using big-endian output files. The OUTPUT_FORMAT line must be in the linker script, otherwise -EB/-EL will not work. */ #ifndef ENDIAN_SPEC #if TARGET_ENDIAN_DEFAULT == 0 #define ENDIAN_SPEC "%{!EB:%{!meb:-EL}} %{EB|meb:-EB}" #else #define ENDIAN_SPEC "%{!EL:%{!mel:-EB}} %{EL|mel:-EL}" #endif #endif /* Support for a compile-time default CPU, et cetera. The rules are: --with-arch is ignored if -march is specified or a -mips is specified (other than -mips16). --with-tune is ignored if -mtune is specified. --with-abi is ignored if -mabi is specified. --with-float is ignored if -mhard-float or -msoft-float are specified. --with-divide is ignored if -mdivide-traps or -mdivide-breaks are specified. */ #define OPTION_DEFAULT_SPECS \ {"arch", "%{!march=*:%{mips16:-march=%(VALUE)}%{!mips*:-march=%(VALUE)}}" }, \ {"tune", "%{!mtune=*:-mtune=%(VALUE)}" }, \ {"abi", "%{!mabi=*:-mabi=%(VALUE)}" }, \ {"float", "%{!msoft-float:%{!mhard-float:-m%(VALUE)-float}}" }, \ {"divide", "%{!mdivide-traps:%{!mdivide-breaks:-mdivide-%(VALUE)}}" } #define GENERATE_DIVIDE_TRAPS (TARGET_DIVIDE_TRAPS \ && ISA_HAS_COND_TRAP) #define GENERATE_BRANCHLIKELY (TARGET_BRANCHLIKELY \ && !TARGET_SR71K \ && !TARGET_MIPS16) /* Generate three-operand multiply instructions for SImode. */ #define GENERATE_MULT3_SI ((TARGET_MIPS3900 \ || TARGET_MIPS5400 \ || TARGET_MIPS5500 \ || TARGET_MIPS7000 \ || TARGET_MIPS9000 \ || TARGET_MAD \ || ISA_MIPS32 \ || ISA_MIPS32R2 \ || ISA_MIPS64 \ || ISA_MIPS64R2) \ && !TARGET_MIPS16) /* Generate three-operand multiply instructions for DImode. */ #define GENERATE_MULT3_DI ((TARGET_MIPS3900) \ && !TARGET_MIPS16) /* True if the ABI can only work with 64-bit integer registers. We generally allow ad-hoc variations for TARGET_SINGLE_FLOAT, but otherwise floating-point registers must also be 64-bit. */ #define ABI_NEEDS_64BIT_REGS (TARGET_NEWABI || mips_abi == ABI_O64) /* Likewise for 32-bit regs. */ #define ABI_NEEDS_32BIT_REGS (mips_abi == ABI_32) /* True if symbols are 64 bits wide. At present, n64 is the only ABI for which this is true. */ #define ABI_HAS_64BIT_SYMBOLS (mips_abi == ABI_64 && !TARGET_SYM32) /* ISA has instructions for managing 64 bit fp and gp regs (e.g. mips3). */ #define ISA_HAS_64BIT_REGS (ISA_MIPS3 \ || ISA_MIPS4 \ || ISA_MIPS64 \ || ISA_MIPS64R2) /* ISA has branch likely instructions (e.g. mips2). */ /* Disable branchlikely for tx39 until compare rewrite. They haven't been generated up to this point. */ #define ISA_HAS_BRANCHLIKELY (!ISA_MIPS1) /* ISA has the conditional move instructions introduced in mips4. */ #define ISA_HAS_CONDMOVE ((ISA_MIPS4 \ || ISA_MIPS32 \ || ISA_MIPS32R2 \ || ISA_MIPS64 \ || ISA_MIPS64R2) \ && !TARGET_MIPS5500 \ && !TARGET_MIPS16) /* ISA has the mips4 FP condition code instructions: FP-compare to CC, branch on CC, and move (both FP and non-FP) on CC. */ #define ISA_HAS_8CC (ISA_MIPS4 \ || ISA_MIPS32 \ || ISA_MIPS32R2 \ || ISA_MIPS64 \ || ISA_MIPS64R2) /* This is a catch all for other mips4 instructions: indexed load, the FP madd and msub instructions, and the FP recip and recip sqrt instructions. */ #define ISA_HAS_FP4 ((ISA_MIPS4 \ || ISA_MIPS64 \ || ISA_MIPS64R2) \ && !TARGET_MIPS16) /* ISA has conditional trap instructions. */ #define ISA_HAS_COND_TRAP (!ISA_MIPS1 \ && !TARGET_MIPS16) /* ISA has integer multiply-accumulate instructions, madd and msub. */ #define ISA_HAS_MADD_MSUB ((ISA_MIPS32 \ || ISA_MIPS32R2 \ || ISA_MIPS64 \ || ISA_MIPS64R2 \ ) && !TARGET_MIPS16) /* ISA has floating-point nmadd and nmsub instructions. */ #define ISA_HAS_NMADD_NMSUB ((ISA_MIPS4 \ || ISA_MIPS64 \ || ISA_MIPS64R2) \ && (!TARGET_MIPS5400 || TARGET_MAD) \ && ! TARGET_MIPS16) /* ISA has count leading zeroes/ones instruction (not implemented). */ #define ISA_HAS_CLZ_CLO ((ISA_MIPS32 \ || ISA_MIPS32R2 \ || ISA_MIPS64 \ || ISA_MIPS64R2 \ ) && !TARGET_MIPS16) /* ISA has double-word count leading zeroes/ones instruction (not implemented). */ #define ISA_HAS_DCLZ_DCLO (ISA_MIPS64 \ || ISA_MIPS64R2 \ && !TARGET_MIPS16) /* ISA has three operand multiply instructions that put the high part in an accumulator: mulhi or mulhiu. */ #define ISA_HAS_MULHI (TARGET_MIPS5400 \ || TARGET_MIPS5500 \ || TARGET_SR71K \ ) /* ISA has three operand multiply instructions that negates the result and puts the result in an accumulator. */ #define ISA_HAS_MULS (TARGET_MIPS5400 \ || TARGET_MIPS5500 \ || TARGET_SR71K \ ) /* ISA has three operand multiply instructions that subtracts the result from a 4th operand and puts the result in an accumulator. */ #define ISA_HAS_MSAC (TARGET_MIPS5400 \ || TARGET_MIPS5500 \ || TARGET_SR71K \ ) /* ISA has three operand multiply instructions that the result from a 4th operand and puts the result in an accumulator. */ #define ISA_HAS_MACC ((TARGET_MIPS4120 && !TARGET_MIPS16) \ || (TARGET_MIPS4130 && !TARGET_MIPS16) \ || TARGET_MIPS5400 \ || TARGET_MIPS5500 \ || TARGET_SR71K \ ) /* ISA has NEC VR-style MACC, MACCHI, DMACC and DMACCHI instructions. */ #define ISA_HAS_MACCHI (!TARGET_MIPS16 \ && (TARGET_MIPS4120 \ || TARGET_MIPS4130)) /* ISA has 32-bit rotate right instruction. */ #define ISA_HAS_ROTR_SI (!TARGET_MIPS16 \ && (ISA_MIPS32R2 \ || ISA_MIPS64R2 \ || TARGET_MIPS5400 \ || TARGET_MIPS5500 \ || TARGET_SR71K \ )) /* ISA has 64-bit rotate right instruction. */ #define ISA_HAS_ROTR_DI (TARGET_64BIT \ && !TARGET_MIPS16 \ && (TARGET_MIPS5400 \ || TARGET_MIPS5500 \ || TARGET_SR71K \ )) /* ISA has data prefetch instructions. This controls use of 'pref'. */ #define ISA_HAS_PREFETCH ((ISA_MIPS4 \ || ISA_MIPS32 \ || ISA_MIPS32R2 \ || ISA_MIPS64 \ || ISA_MIPS64R2) \ && !TARGET_MIPS16) /* ISA has data indexed prefetch instructions. This controls use of 'prefx', along with TARGET_HARD_FLOAT and TARGET_DOUBLE_FLOAT. (prefx is a cop1x instruction, so can only be used if FP is enabled.) */ #define ISA_HAS_PREFETCHX ((ISA_MIPS4 \ || ISA_MIPS64 \ || ISA_MIPS64R2) \ && !TARGET_MIPS16) /* True if trunc.w.s and trunc.w.d are real (not synthetic) instructions. Both require TARGET_HARD_FLOAT, and trunc.w.d also requires TARGET_DOUBLE_FLOAT. */ #define ISA_HAS_TRUNC_W (!ISA_MIPS1) /* ISA includes the MIPS32r2 seb and seh instructions. */ #define ISA_HAS_SEB_SEH (!TARGET_MIPS16 \ && (ISA_MIPS32R2 \ || ISA_MIPS64R2 \ )) /* ISA includes the MIPS32/64 rev 2 ext and ins instructions. */ #define ISA_HAS_EXT_INS (!TARGET_MIPS16 \ && (ISA_MIPS32R2 \ || ISA_MIPS64R2 \ )) /* True if the result of a load is not available to the next instruction. A nop will then be needed between instructions like "lw $4,..." and "addiu $4,$4,1". */ #define ISA_HAS_LOAD_DELAY (mips_isa == 1 \ && !TARGET_MIPS3900 \ && !TARGET_MIPS16) /* Likewise mtc1 and mfc1. */ #define ISA_HAS_XFER_DELAY (mips_isa <= 3) /* Likewise floating-point comparisons. */ #define ISA_HAS_FCMP_DELAY (mips_isa <= 3) /* True if mflo and mfhi can be immediately followed by instructions which write to the HI and LO registers. According to MIPS specifications, MIPS ISAs I, II, and III need (at least) two instructions between the reads of HI/LO and instructions which write them, and later ISAs do not. Contradicting the MIPS specifications, some MIPS IV processor user manuals (e.g. the UM for the NEC Vr5000) document needing the instructions between HI/LO reads and writes, as well. Therefore, we declare only MIPS32, MIPS64 and later ISAs to have the interlocks, plus any specific earlier-ISA CPUs for which CPU documentation declares that the instructions are really interlocked. */ #define ISA_HAS_HILO_INTERLOCKS (ISA_MIPS32 \ || ISA_MIPS32R2 \ || ISA_MIPS64 \ || ISA_MIPS64R2 \ || TARGET_MIPS5500) /* Add -G xx support. */ #undef SWITCH_TAKES_ARG #define SWITCH_TAKES_ARG(CHAR) \ (DEFAULT_SWITCH_TAKES_ARG (CHAR) || (CHAR) == 'G') #define OVERRIDE_OPTIONS override_options () #define CONDITIONAL_REGISTER_USAGE mips_conditional_register_usage () /* Show we can debug even without a frame pointer. */ #define CAN_DEBUG_WITHOUT_FP /* Tell collect what flags to pass to nm. */ #ifndef NM_FLAGS #define NM_FLAGS "-Bn" #endif #ifndef MIPS_ABI_DEFAULT #define MIPS_ABI_DEFAULT ABI_32 #endif /* Use the most portable ABI flag for the ASM specs. */ #if MIPS_ABI_DEFAULT == ABI_32 #define MULTILIB_ABI_DEFAULT "mabi=32" #endif #if MIPS_ABI_DEFAULT == ABI_O64 #define MULTILIB_ABI_DEFAULT "mabi=o64" #endif #if MIPS_ABI_DEFAULT == ABI_N32 #define MULTILIB_ABI_DEFAULT "mabi=n32" #endif #if MIPS_ABI_DEFAULT == ABI_64 #define MULTILIB_ABI_DEFAULT "mabi=64" #endif #if MIPS_ABI_DEFAULT == ABI_EABI #define MULTILIB_ABI_DEFAULT "mabi=eabi" #endif /* SUBTARGET_ASM_OPTIMIZING_SPEC handles passing optimization options to the assembler. It may be overridden by subtargets. */ #ifndef SUBTARGET_ASM_OPTIMIZING_SPEC #define SUBTARGET_ASM_OPTIMIZING_SPEC "\ %{noasmopt:-O0} \ %{!noasmopt:%{O:-O2} %{O1:-O2} %{O2:-O2} %{O3:-O3}}" #endif /* SUBTARGET_ASM_DEBUGGING_SPEC handles passing debugging options to the assembler. It may be overridden by subtargets. Beginning with gas 2.13, -mdebug must be passed to correctly handle COFF debugging info. */ #ifndef SUBTARGET_ASM_DEBUGGING_SPEC #define SUBTARGET_ASM_DEBUGGING_SPEC "\ %{g} %{g0} %{g1} %{g2} %{g3} \ %{ggdb:-g} %{ggdb0:-g0} %{ggdb1:-g1} %{ggdb2:-g2} %{ggdb3:-g3} \ %{gstabs:-g} %{gstabs0:-g0} %{gstabs1:-g1} %{gstabs2:-g2} %{gstabs3:-g3} \ %{gstabs+:-g} %{gstabs+0:-g0} %{gstabs+1:-g1} %{gstabs+2:-g2} %{gstabs+3:-g3} \ %{gcoff:-g} %{gcoff0:-g0} %{gcoff1:-g1} %{gcoff2:-g2} %{gcoff3:-g3} \ %{gcoff*:-mdebug} %{!gcoff*:-no-mdebug}" #endif /* SUBTARGET_ASM_SPEC is always passed to the assembler. It may be overridden by subtargets. */ #ifndef SUBTARGET_ASM_SPEC #define SUBTARGET_ASM_SPEC "" #endif #undef ASM_SPEC #define ASM_SPEC "\ %{G*} %(endian_spec) %{mips1} %{mips2} %{mips3} %{mips4} \ %{mips32} %{mips32r2} %{mips64} \ %{mips16:%{!mno-mips16:-mips16}} %{mno-mips16:-no-mips16} \ %{mips3d:-mips3d} \ %{mdsp} \ %{mfix-vr4120} %{mfix-vr4130} \ %(subtarget_asm_optimizing_spec) \ %(subtarget_asm_debugging_spec) \ %{mabi=*} %{!mabi*: %(asm_abi_default_spec)} \ %{mgp32} %{mgp64} %{march=*} %{mxgot:-xgot} \ %{mshared} %{mno-shared} \ %{msym32} %{mno-sym32} \ %{mtune=*} %{v} \ %(subtarget_asm_spec)" /* Extra switches sometimes passed to the linker. */ /* ??? The bestGnum will never be passed to the linker, because the gcc driver will interpret it as a -b option. */ #ifndef LINK_SPEC #define LINK_SPEC "\ %(endian_spec) \ %{G*} %{mips1} %{mips2} %{mips3} %{mips4} %{mips32} %{mips32r2} %{mips64} \ %{bestGnum} %{shared} %{non_shared}" #endif /* LINK_SPEC defined */ /* Specs for the compiler proper */ /* SUBTARGET_CC1_SPEC is passed to the compiler proper. It may be overridden by subtargets. */ #ifndef SUBTARGET_CC1_SPEC #define SUBTARGET_CC1_SPEC "" #endif /* CC1_SPEC is the set of arguments to pass to the compiler proper. */ #undef CC1_SPEC #define CC1_SPEC "\ %{gline:%{!g:%{!g0:%{!g1:%{!g2: -g1}}}}} \ %{G*} %{EB:-meb} %{EL:-mel} %{EB:%{EL:%emay not use both -EB and -EL}} \ %{save-temps: } \ %(subtarget_cc1_spec)" /* Preprocessor specs. */ /* SUBTARGET_CPP_SPEC is passed to the preprocessor. It may be overridden by subtargets. */ #ifndef SUBTARGET_CPP_SPEC #define SUBTARGET_CPP_SPEC "" #endif #define CPP_SPEC "%(subtarget_cpp_spec)" /* This macro defines names of additional specifications to put in the specs that can be used in various specifications like CC1_SPEC. Its definition is an initializer with a subgrouping for each command option. Each subgrouping contains a string constant, that defines the specification name, and a string constant that used by the GCC driver program. Do not define this macro if it does not need to do anything. */ #define EXTRA_SPECS \ { "subtarget_cc1_spec", SUBTARGET_CC1_SPEC }, \ { "subtarget_cpp_spec", SUBTARGET_CPP_SPEC }, \ { "subtarget_asm_optimizing_spec", SUBTARGET_ASM_OPTIMIZING_SPEC }, \ { "subtarget_asm_debugging_spec", SUBTARGET_ASM_DEBUGGING_SPEC }, \ { "subtarget_asm_spec", SUBTARGET_ASM_SPEC }, \ { "asm_abi_default_spec", "-" MULTILIB_ABI_DEFAULT }, \ { "endian_spec", ENDIAN_SPEC }, \ SUBTARGET_EXTRA_SPECS #ifndef SUBTARGET_EXTRA_SPECS #define SUBTARGET_EXTRA_SPECS #endif #define DBX_DEBUGGING_INFO 1 /* generate stabs (OSF/rose) */ #define MIPS_DEBUGGING_INFO 1 /* MIPS specific debugging info */ #define DWARF2_DEBUGGING_INFO 1 /* dwarf2 debugging info */ #ifndef PREFERRED_DEBUGGING_TYPE #define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG #endif #define DWARF2_ADDR_SIZE (ABI_HAS_64BIT_SYMBOLS ? 8 : 4) /* By default, turn on GDB extensions. */ #define DEFAULT_GDB_EXTENSIONS 1 /* Local compiler-generated symbols must have a prefix that the assembler understands. By default, this is $, although some targets (e.g., NetBSD-ELF) need to override this. */ #ifndef LOCAL_LABEL_PREFIX #define LOCAL_LABEL_PREFIX "$" #endif /* By default on the mips, external symbols do not have an underscore prepended, but some targets (e.g., NetBSD) require this. */ #ifndef USER_LABEL_PREFIX #define USER_LABEL_PREFIX "" #endif /* On Sun 4, this limit is 2048. We use 1500 to be safe, since the length can run past this up to a continuation point. */ #undef DBX_CONTIN_LENGTH #define DBX_CONTIN_LENGTH 1500 /* How to renumber registers for dbx and gdb. */ #define DBX_REGISTER_NUMBER(REGNO) mips_dbx_regno[ (REGNO) ] /* The mapping from gcc register number to DWARF 2 CFA column number. */ #define DWARF_FRAME_REGNUM(REG) (REG) /* The DWARF 2 CFA column which tracks the return address. */ #define DWARF_FRAME_RETURN_COLUMN (GP_REG_FIRST + 31) /* The DWARF 2 CFA column which tracks the return address from a signal handler context. */ #define SIGNAL_UNWIND_RETURN_COLUMN (FP_REG_LAST + 1) /* Before the prologue, RA lives in r31. */ #define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (VOIDmode, GP_REG_FIRST + 31) /* Describe how we implement __builtin_eh_return. */ #define EH_RETURN_DATA_REGNO(N) \ ((N) < (TARGET_MIPS16 ? 2 : 4) ? (N) + GP_ARG_FIRST : INVALID_REGNUM) #define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, GP_REG_FIRST + 3) /* Offsets recorded in opcodes are a multiple of this alignment factor. The default for this in 64-bit mode is 8, which causes problems with SFmode register saves. */ #define DWARF_CIE_DATA_ALIGNMENT -4 /* Correct the offset of automatic variables and arguments. Note that the MIPS debug format wants all automatic variables and arguments to be in terms of the virtual frame pointer (stack pointer before any adjustment in the function), while the MIPS 3.0 linker wants the frame pointer to be the stack pointer after the initial adjustment. */ #define DEBUGGER_AUTO_OFFSET(X) \ mips_debugger_offset (X, (HOST_WIDE_INT) 0) #define DEBUGGER_ARG_OFFSET(OFFSET, X) \ mips_debugger_offset (X, (HOST_WIDE_INT) OFFSET) /* Target machine storage layout */ #define BITS_BIG_ENDIAN 0 #define BYTES_BIG_ENDIAN (TARGET_BIG_ENDIAN != 0) #define WORDS_BIG_ENDIAN (TARGET_BIG_ENDIAN != 0) /* Define this to set the endianness to use in libgcc2.c, which can not depend on target_flags. */ #if !defined(MIPSEL) && !defined(__MIPSEL__) #define LIBGCC2_WORDS_BIG_ENDIAN 1 #else #define LIBGCC2_WORDS_BIG_ENDIAN 0 #endif #define MAX_BITS_PER_WORD 64 /* Width of a word, in units (bytes). */ #define UNITS_PER_WORD (TARGET_64BIT ? 8 : 4) #ifndef IN_LIBGCC2 #define MIN_UNITS_PER_WORD 4 #endif /* For MIPS, width of a floating point register. */ #define UNITS_PER_FPREG (TARGET_FLOAT64 ? 8 : 4) /* If register $f0 holds a floating-point value, $f(0 + FP_INC) is the next available register. */ #define FP_INC (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT ? 1 : 2) /* The largest size of value that can be held in floating-point registers and moved with a single instruction. */ #define UNITS_PER_HWFPVALUE (TARGET_SOFT_FLOAT ? 0 : FP_INC * UNITS_PER_FPREG) /* The largest size of value that can be held in floating-point registers. */ #define UNITS_PER_FPVALUE \ (TARGET_SOFT_FLOAT ? 0 \ : TARGET_SINGLE_FLOAT ? UNITS_PER_FPREG \ : LONG_DOUBLE_TYPE_SIZE / BITS_PER_UNIT) /* The number of bytes in a double. */ #define UNITS_PER_DOUBLE (TYPE_PRECISION (double_type_node) / BITS_PER_UNIT) #define UNITS_PER_SIMD_WORD (TARGET_PAIRED_SINGLE_FLOAT ? 8 : UNITS_PER_WORD) /* Set the sizes of the core types. */ #define SHORT_TYPE_SIZE 16 #define INT_TYPE_SIZE 32 #define LONG_TYPE_SIZE (TARGET_LONG64 ? 64 : 32) #define LONG_LONG_TYPE_SIZE 64 #define FLOAT_TYPE_SIZE 32 #define DOUBLE_TYPE_SIZE 64 #define LONG_DOUBLE_TYPE_SIZE (TARGET_NEWABI ? 128 : 64) /* long double is not a fixed mode, but the idea is that, if we support long double, we also want a 128-bit integer type. */ #define MAX_FIXED_MODE_SIZE LONG_DOUBLE_TYPE_SIZE #ifdef IN_LIBGCC2 #if (defined _ABIN32 && _MIPS_SIM == _ABIN32) \ || (defined _ABI64 && _MIPS_SIM == _ABI64) # define LIBGCC2_LONG_DOUBLE_TYPE_SIZE 128 # else # define LIBGCC2_LONG_DOUBLE_TYPE_SIZE 64 # endif #endif /* Width in bits of a pointer. */ #ifndef POINTER_SIZE #define POINTER_SIZE ((TARGET_LONG64 && TARGET_64BIT) ? 64 : 32) #endif /* Allocation boundary (in *bits*) for storing arguments in argument list. */ #define PARM_BOUNDARY BITS_PER_WORD /* Allocation boundary (in *bits*) for the code of a function. */ #define FUNCTION_BOUNDARY 32 /* Alignment of field after `int : 0' in a structure. */ #define EMPTY_FIELD_BOUNDARY 32 /* Every structure's size must be a multiple of this. */ /* 8 is observed right on a DECstation and on riscos 4.02. */ #define STRUCTURE_SIZE_BOUNDARY 8 /* There is no point aligning anything to a rounder boundary than this. */ #define BIGGEST_ALIGNMENT LONG_DOUBLE_TYPE_SIZE /* All accesses must be aligned. */ #define STRICT_ALIGNMENT 1 /* Define this if you wish to imitate the way many other C compilers handle alignment of bitfields and the structures that contain them. The behavior is that the type written for a bit-field (`int', `short', or other integer type) imposes an alignment for the entire structure, as if the structure really did contain an ordinary field of that type. In addition, the bit-field is placed within the structure so that it would fit within such a field, not crossing a boundary for it. Thus, on most machines, a bit-field whose type is written as `int' would not cross a four-byte boundary, and would force four-byte alignment for the whole structure. (The alignment used may not be four bytes; it is controlled by the other alignment parameters.) If the macro is defined, its definition should be a C expression; a nonzero value for the expression enables this behavior. */ #define PCC_BITFIELD_TYPE_MATTERS 1 /* If defined, a C expression to compute the alignment given to a constant that is being placed in memory. CONSTANT is the constant and ALIGN is the alignment that the object would ordinarily have. The value of this macro is used instead of that alignment to align the object. If this macro is not defined, then ALIGN is used. The typical use of this macro is to increase alignment for string constants to be word aligned so that `strcpy' calls that copy constants can be done inline. */ #define CONSTANT_ALIGNMENT(EXP, ALIGN) \ ((TREE_CODE (EXP) == STRING_CST || TREE_CODE (EXP) == CONSTRUCTOR) \ && (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN)) /* If defined, a C expression to compute the alignment for a static variable. TYPE is the data type, and ALIGN is the alignment that the object would ordinarily have. The value of this macro is used instead of that alignment to align the object. If this macro is not defined, then ALIGN is used. One use of this macro is to increase alignment of medium-size data to make it all fit in fewer cache lines. Another is to cause character arrays to be word-aligned so that `strcpy' calls that copy constants to character arrays can be done inline. */ #undef DATA_ALIGNMENT #define DATA_ALIGNMENT(TYPE, ALIGN) \ ((((ALIGN) < BITS_PER_WORD) \ && (TREE_CODE (TYPE) == ARRAY_TYPE \ || TREE_CODE (TYPE) == UNION_TYPE \ || TREE_CODE (TYPE) == RECORD_TYPE)) ? BITS_PER_WORD : (ALIGN)) #define PAD_VARARGS_DOWN \ (FUNCTION_ARG_PADDING (TYPE_MODE (type), type) == downward) /* Define if operations between registers always perform the operation on the full register even if a narrower mode is specified. */ #define WORD_REGISTER_OPERATIONS /* When in 64 bit mode, move insns will sign extend SImode and CCmode moves. All other references are zero extended. */ #define LOAD_EXTEND_OP(MODE) \ (TARGET_64BIT && ((MODE) == SImode || (MODE) == CCmode) \ ? SIGN_EXTEND : ZERO_EXTEND) /* Define this macro if it is advisable to hold scalars in registers in a wider mode than that declared by the program. In such cases, the value is constrained to be within the bounds of the declared type, but kept valid in the wider mode. The signedness of the extension may differ from that of the type. */ #define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \ if (GET_MODE_CLASS (MODE) == MODE_INT \ && GET_MODE_SIZE (MODE) < UNITS_PER_WORD) \ { \ if ((MODE) == SImode) \ (UNSIGNEDP) = 0; \ (MODE) = Pmode; \ } /* Define if loading short immediate values into registers sign extends. */ #define SHORT_IMMEDIATES_SIGN_EXTEND /* The [d]clz instructions have the natural values at 0. */ #define CLZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) \ ((VALUE) = GET_MODE_BITSIZE (MODE), true) /* Standard register usage. */ /* Number of hardware registers. We have: - 32 integer registers - 32 floating point registers - 8 condition code registers - 2 accumulator registers (hi and lo) - 32 registers each for coprocessors 0, 2 and 3 - 3 fake registers: - ARG_POINTER_REGNUM - FRAME_POINTER_REGNUM - FAKE_CALL_REGNO (see the comment above load_callsi for details) - 3 dummy entries that were used at various times in the past. - 6 DSP accumulator registers (3 hi-lo pairs) for MIPS DSP ASE - 6 DSP control registers */ #define FIRST_PSEUDO_REGISTER 188 /* By default, fix the kernel registers ($26 and $27), the global pointer ($28) and the stack pointer ($29). This can change depending on the command-line options. Regarding coprocessor registers: without evidence to the contrary, it's best to assume that each coprocessor register has a unique use. This can be overridden, in, e.g., override_options() or CONDITIONAL_REGISTER_USAGE should the assumption be inappropriate for a particular target. */ #define FIXED_REGISTERS \ { \ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, \ /* COP0 registers */ \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ /* COP2 registers */ \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ /* COP3 registers */ \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ /* 6 DSP accumulator registers & 6 control registers */ \ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 \ } /* Set up this array for o32 by default. Note that we don't mark $31 as a call-clobbered register. The idea is that it's really the call instructions themselves which clobber $31. We don't care what the called function does with it afterwards. This approach makes it easier to implement sibcalls. Unlike normal calls, sibcalls don't clobber $31, so the register reaches the called function in tact. EPILOGUE_USES says that $31 is useful to the called function. */ #define CALL_USED_REGISTERS \ { \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ /* COP0 registers */ \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ /* COP2 registers */ \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ /* COP3 registers */ \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ /* 6 DSP accumulator registers & 6 control registers */ \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 \ } /* Define this since $28, though fixed, is call-saved in many ABIs. */ #define CALL_REALLY_USED_REGISTERS \ { /* General registers. */ \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, \ /* Floating-point registers. */ \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ /* Others. */ \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ /* COP0 registers */ \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ /* COP2 registers */ \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ /* COP3 registers */ \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ /* 6 DSP accumulator registers & 6 control registers */ \ 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 \ } /* Internal macros to classify a register number as to whether it's a general purpose register, a floating point register, a multiply/divide register, or a status register. */ #define GP_REG_FIRST 0 #define GP_REG_LAST 31 #define GP_REG_NUM (GP_REG_LAST - GP_REG_FIRST + 1) #define GP_DBX_FIRST 0 #define FP_REG_FIRST 32 #define FP_REG_LAST 63 #define FP_REG_NUM (FP_REG_LAST - FP_REG_FIRST + 1) #define FP_DBX_FIRST ((write_symbols == DBX_DEBUG) ? 38 : 32) #define MD_REG_FIRST 64 #define MD_REG_LAST 65 #define MD_REG_NUM (MD_REG_LAST - MD_REG_FIRST + 1) #define MD_DBX_FIRST (FP_DBX_FIRST + FP_REG_NUM) #define ST_REG_FIRST 67 #define ST_REG_LAST 74 #define ST_REG_NUM (ST_REG_LAST - ST_REG_FIRST + 1) /* FIXME: renumber. */ #define COP0_REG_FIRST 80 #define COP0_REG_LAST 111 #define COP0_REG_NUM (COP0_REG_LAST - COP0_REG_FIRST + 1) #define COP2_REG_FIRST 112 #define COP2_REG_LAST 143 #define COP2_REG_NUM (COP2_REG_LAST - COP2_REG_FIRST + 1) #define COP3_REG_FIRST 144 #define COP3_REG_LAST 175 #define COP3_REG_NUM (COP3_REG_LAST - COP3_REG_FIRST + 1) /* ALL_COP_REG_NUM assumes that COP0,2,and 3 are numbered consecutively. */ #define ALL_COP_REG_NUM (COP3_REG_LAST - COP0_REG_FIRST + 1) #define DSP_ACC_REG_FIRST 176 #define DSP_ACC_REG_LAST 181 #define DSP_ACC_REG_NUM (DSP_ACC_REG_LAST - DSP_ACC_REG_FIRST + 1) #define AT_REGNUM (GP_REG_FIRST + 1) #define HI_REGNUM (MD_REG_FIRST + 0) #define LO_REGNUM (MD_REG_FIRST + 1) #define AC1HI_REGNUM (DSP_ACC_REG_FIRST + 0) #define AC1LO_REGNUM (DSP_ACC_REG_FIRST + 1) #define AC2HI_REGNUM (DSP_ACC_REG_FIRST + 2) #define AC2LO_REGNUM (DSP_ACC_REG_FIRST + 3) #define AC3HI_REGNUM (DSP_ACC_REG_FIRST + 4) #define AC3LO_REGNUM (DSP_ACC_REG_FIRST + 5) /* FPSW_REGNUM is the single condition code used if !ISA_HAS_8CC. If ISA_HAS_8CC, it should not be used, and an arbitrary ST_REG should be used instead. */ #define FPSW_REGNUM ST_REG_FIRST #define GP_REG_P(REGNO) \ ((unsigned int) ((int) (REGNO) - GP_REG_FIRST) < GP_REG_NUM) #define M16_REG_P(REGNO) \ (((REGNO) >= 2 && (REGNO) <= 7) || (REGNO) == 16 || (REGNO) == 17) #define FP_REG_P(REGNO) \ ((unsigned int) ((int) (REGNO) - FP_REG_FIRST) < FP_REG_NUM) #define MD_REG_P(REGNO) \ ((unsigned int) ((int) (REGNO) - MD_REG_FIRST) < MD_REG_NUM) #define ST_REG_P(REGNO) \ ((unsigned int) ((int) (REGNO) - ST_REG_FIRST) < ST_REG_NUM) #define COP0_REG_P(REGNO) \ ((unsigned int) ((int) (REGNO) - COP0_REG_FIRST) < COP0_REG_NUM) #define COP2_REG_P(REGNO) \ ((unsigned int) ((int) (REGNO) - COP2_REG_FIRST) < COP2_REG_NUM) #define COP3_REG_P(REGNO) \ ((unsigned int) ((int) (REGNO) - COP3_REG_FIRST) < COP3_REG_NUM) #define ALL_COP_REG_P(REGNO) \ ((unsigned int) ((int) (REGNO) - COP0_REG_FIRST) < ALL_COP_REG_NUM) /* Test if REGNO is one of the 6 new DSP accumulators. */ #define DSP_ACC_REG_P(REGNO) \ ((unsigned int) ((int) (REGNO) - DSP_ACC_REG_FIRST) < DSP_ACC_REG_NUM) /* Test if REGNO is hi, lo, or one of the 6 new DSP accumulators. */ #define ACC_REG_P(REGNO) \ (MD_REG_P (REGNO) || DSP_ACC_REG_P (REGNO)) /* Test if REGNO is HI or the first register of 3 new DSP accumulator pairs. */ #define ACC_HI_REG_P(REGNO) \ ((REGNO) == HI_REGNUM || (REGNO) == AC1HI_REGNUM || (REGNO) == AC2HI_REGNUM \ || (REGNO) == AC3HI_REGNUM) #define FP_REG_RTX_P(X) (REG_P (X) && FP_REG_P (REGNO (X))) /* True if X is (const (unspec [(const_int 0)] UNSPEC_GP)). This is used to initialize the mips16 gp pseudo register. */ #define CONST_GP_P(X) \ (GET_CODE (X) == CONST \ && GET_CODE (XEXP (X, 0)) == UNSPEC \ && XINT (XEXP (X, 0), 1) == UNSPEC_GP) /* Return coprocessor number from register number. */ #define COPNUM_AS_CHAR_FROM_REGNUM(REGNO) \ (COP0_REG_P (REGNO) ? '0' : COP2_REG_P (REGNO) ? '2' \ : COP3_REG_P (REGNO) ? '3' : '?') #define HARD_REGNO_NREGS(REGNO, MODE) mips_hard_regno_nregs (REGNO, MODE) /* To make the code simpler, HARD_REGNO_MODE_OK just references an array built in override_options. Because machmodes.h is not yet included before this file is processed, the MODE bound can't be expressed here. */ extern char mips_hard_regno_mode_ok[][FIRST_PSEUDO_REGISTER]; #define HARD_REGNO_MODE_OK(REGNO, MODE) \ mips_hard_regno_mode_ok[ (int)(MODE) ][ (REGNO) ] /* Value is 1 if it is a good idea to tie two pseudo registers when one has mode MODE1 and one has mode MODE2. If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2, for any hard reg, then this must be 0 for correct output. */ #define MODES_TIEABLE_P(MODE1, MODE2) \ ((GET_MODE_CLASS (MODE1) == MODE_FLOAT || \ GET_MODE_CLASS (MODE1) == MODE_COMPLEX_FLOAT) \ == (GET_MODE_CLASS (MODE2) == MODE_FLOAT || \ GET_MODE_CLASS (MODE2) == MODE_COMPLEX_FLOAT)) /* Register to use for pushing function arguments. */ #define STACK_POINTER_REGNUM (GP_REG_FIRST + 29) /* These two registers don't really exist: they get eliminated to either the stack or hard frame pointer. */ #define ARG_POINTER_REGNUM 77 #define FRAME_POINTER_REGNUM 78 /* $30 is not available on the mips16, so we use $17 as the frame pointer. */ #define HARD_FRAME_POINTER_REGNUM \ (TARGET_MIPS16 ? GP_REG_FIRST + 17 : GP_REG_FIRST + 30) /* Value should be nonzero if functions must have frame pointers. Zero means the frame pointer need not be set up (and parms may be accessed via the stack pointer) in functions that seem suitable. This is computed in `reload', in reload1.c. */ #define FRAME_POINTER_REQUIRED (current_function_calls_alloca) /* Register in which static-chain is passed to a function. */ #define STATIC_CHAIN_REGNUM (GP_REG_FIRST + 2) /* Registers used as temporaries in prologue/epilogue code. If we're generating mips16 code, these registers must come from the core set of 8. The prologue register mustn't conflict with any incoming arguments, the static chain pointer, or the frame pointer. The epilogue temporary mustn't conflict with the return registers, the frame pointer, the EH stack adjustment, or the EH data registers. */ #define MIPS_PROLOGUE_TEMP_REGNUM (GP_REG_FIRST + 3) #define MIPS_EPILOGUE_TEMP_REGNUM (GP_REG_FIRST + (TARGET_MIPS16 ? 6 : 8)) #define MIPS_PROLOGUE_TEMP(MODE) gen_rtx_REG (MODE, MIPS_PROLOGUE_TEMP_REGNUM) #define MIPS_EPILOGUE_TEMP(MODE) gen_rtx_REG (MODE, MIPS_EPILOGUE_TEMP_REGNUM) /* Define this macro if it is as good or better to call a constant function address than to call an address kept in a register. */ #define NO_FUNCTION_CSE 1 /* The ABI-defined global pointer. Sometimes we use a different register in leaf functions: see PIC_OFFSET_TABLE_REGNUM. */ #define GLOBAL_POINTER_REGNUM (GP_REG_FIRST + 28) /* We normally use $28 as the global pointer. However, when generating n32/64 PIC, it is better for leaf functions to use a call-clobbered register instead. They can then avoid saving and restoring $28 and perhaps avoid using a frame at all. When a leaf function uses something other than $28, mips_expand_prologue will modify pic_offset_table_rtx in place. Take the register number from there after reload. */ #define PIC_OFFSET_TABLE_REGNUM \ (reload_completed ? REGNO (pic_offset_table_rtx) : GLOBAL_POINTER_REGNUM) #define PIC_FUNCTION_ADDR_REGNUM (GP_REG_FIRST + 25) /* Define the classes of registers for register constraints in the machine description. Also define ranges of constants. One of the classes must always be named ALL_REGS and include all hard regs. If there is more than one class, another class must be named NO_REGS and contain no registers. The name GENERAL_REGS must be the name of a class (or an alias for another name such as ALL_REGS). This is the class of registers that is allowed by "g" or "r" in a register constraint. Also, registers outside this class are allocated only when instructions express preferences for them. The classes must be numbered in nondecreasing order; that is, a larger-numbered class must never be contained completely in a smaller-numbered class. For any two classes, it is very desirable that there be another class that represents their union. */ enum reg_class { NO_REGS, /* no registers in set */ M16_NA_REGS, /* mips16 regs not used to pass args */ M16_REGS, /* mips16 directly accessible registers */ T_REG, /* mips16 T register ($24) */ M16_T_REGS, /* mips16 registers plus T register */ PIC_FN_ADDR_REG, /* SVR4 PIC function address register */ V1_REG, /* Register $v1 ($3) used for TLS access. */ LEA_REGS, /* Every GPR except $25 */ GR_REGS, /* integer registers */ FP_REGS, /* floating point registers */ HI_REG, /* hi register */ LO_REG, /* lo register */ MD_REGS, /* multiply/divide registers (hi/lo) */ COP0_REGS, /* generic coprocessor classes */ COP2_REGS, COP3_REGS, HI_AND_GR_REGS, /* union classes */ LO_AND_GR_REGS, HI_AND_FP_REGS, COP0_AND_GR_REGS, COP2_AND_GR_REGS, COP3_AND_GR_REGS, ALL_COP_REGS, ALL_COP_AND_GR_REGS, ST_REGS, /* status registers (fp status) */ DSP_ACC_REGS, /* DSP accumulator registers */ ACC_REGS, /* Hi/Lo and DSP accumulator registers */ ALL_REGS, /* all registers */ LIM_REG_CLASSES /* max value + 1 */ }; #define N_REG_CLASSES (int) LIM_REG_CLASSES #define GENERAL_REGS GR_REGS /* An initializer containing the names of the register classes as C string constants. These names are used in writing some of the debugging dumps. */ #define REG_CLASS_NAMES \ { \ "NO_REGS", \ "M16_NA_REGS", \ "M16_REGS", \ "T_REG", \ "M16_T_REGS", \ "PIC_FN_ADDR_REG", \ "V1_REG", \ "LEA_REGS", \ "GR_REGS", \ "FP_REGS", \ "HI_REG", \ "LO_REG", \ "MD_REGS", \ /* coprocessor registers */ \ "COP0_REGS", \ "COP2_REGS", \ "COP3_REGS", \ "HI_AND_GR_REGS", \ "LO_AND_GR_REGS", \ "HI_AND_FP_REGS", \ "COP0_AND_GR_REGS", \ "COP2_AND_GR_REGS", \ "COP3_AND_GR_REGS", \ "ALL_COP_REGS", \ "ALL_COP_AND_GR_REGS", \ "ST_REGS", \ "DSP_ACC_REGS", \ "ACC_REGS", \ "ALL_REGS" \ } /* An initializer containing the contents of the register classes, as integers which are bit masks. The Nth integer specifies the contents of class N. The way the integer MASK is interpreted is that register R is in the class if `MASK & (1 << R)' is 1. When the machine has more than 32 registers, an integer does not suffice. Then the integers are replaced by sub-initializers, braced groupings containing several integers. Each sub-initializer must be suitable as an initializer for the type `HARD_REG_SET' which is defined in `hard-reg-set.h'. */ #define REG_CLASS_CONTENTS \ { \ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* no registers */ \ { 0x0003000c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* mips16 nonarg regs */\ { 0x000300fc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* mips16 registers */ \ { 0x01000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* mips16 T register */ \ { 0x010300fc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* mips16 and T regs */ \ { 0x02000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* SVR4 PIC function address register */ \ { 0x00000008, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* only $v1 */ \ { 0xfdffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* Every other GPR except $25 */ \ { 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* integer registers */ \ { 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* floating registers*/ \ { 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000 }, /* hi register */ \ { 0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0x00000000 }, /* lo register */ \ { 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000000 }, /* mul/div registers */ \ { 0x00000000, 0x00000000, 0xffff0000, 0x0000ffff, 0x00000000, 0x00000000 }, /* cop0 registers */ \ { 0x00000000, 0x00000000, 0x00000000, 0xffff0000, 0x0000ffff, 0x00000000 }, /* cop2 registers */ \ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffff0000, 0x0000ffff }, /* cop3 registers */ \ { 0xffffffff, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000 }, /* union classes */ \ { 0xffffffff, 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0x00000000 }, \ { 0x00000000, 0xffffffff, 0x00000001, 0x00000000, 0x00000000, 0x00000000 }, \ { 0xffffffff, 0x00000000, 0xffff0000, 0x0000ffff, 0x00000000, 0x00000000 }, \ { 0xffffffff, 0x00000000, 0x00000000, 0xffff0000, 0x0000ffff, 0x00000000 }, \ { 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffff0000, 0x0000ffff }, \ { 0x00000000, 0x00000000, 0xffff0000, 0xffffffff, 0xffffffff, 0x0000ffff }, \ { 0xffffffff, 0x00000000, 0xffff0000, 0xffffffff, 0xffffffff, 0x0000ffff }, \ { 0x00000000, 0x00000000, 0x000007f8, 0x00000000, 0x00000000, 0x00000000 }, /* status registers */ \ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x003f0000 }, /* dsp accumulator registers */ \ { 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x003f0000 }, /* hi/lo and dsp accumulator registers */ \ { 0xffffffff, 0xffffffff, 0xffff07ff, 0xffffffff, 0xffffffff, 0x0fffffff } /* all registers */ \ } /* A C expression whose value is a register class containing hard register REGNO. In general there is more that one such class; choose a class which is "minimal", meaning that no smaller class also contains the register. */ extern const enum reg_class mips_regno_to_class[]; #define REGNO_REG_CLASS(REGNO) mips_regno_to_class[ (REGNO) ] /* A macro whose definition is the name of the class to which a valid base register must belong. A base register is one used in an address which is the register value plus a displacement. */ #define BASE_REG_CLASS (TARGET_MIPS16 ? M16_REGS : GR_REGS) /* A macro whose definition is the name of the class to which a valid index register must belong. An index register is one used in an address where its value is either multiplied by a scale factor or added to another register (as well as added to a displacement). */ #define INDEX_REG_CLASS NO_REGS /* When SMALL_REGISTER_CLASSES is nonzero, the compiler allows registers explicitly used in the rtl to be used as spill registers but prevents the compiler from extending the lifetime of these registers. */ #define SMALL_REGISTER_CLASSES (TARGET_MIPS16) /* This macro is used later on in the file. */ #define GR_REG_CLASS_P(CLASS) \ ((CLASS) == GR_REGS || (CLASS) == M16_REGS || (CLASS) == T_REG \ || (CLASS) == M16_T_REGS || (CLASS) == M16_NA_REGS \ || (CLASS) == V1_REG \ || (CLASS) == PIC_FN_ADDR_REG || (CLASS) == LEA_REGS) /* This macro is also used later on in the file. */ #define COP_REG_CLASS_P(CLASS) \ ((CLASS) == COP0_REGS || (CLASS) == COP2_REGS || (CLASS) == COP3_REGS) /* REG_ALLOC_ORDER is to order in which to allocate registers. This is the default value (allocate the registers in numeric order). We define it just so that we can override it for the mips16 target in ORDER_REGS_FOR_LOCAL_ALLOC. */ #define REG_ALLOC_ORDER \ { 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, 29, 30, 31, \ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, \ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, \ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, \ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, \ 96, 97, 98, 99, 100,101,102,103,104,105,106,107,108,109,110,111, \ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, \ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, \ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, \ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, \ 176,177,178,179,180,181,182,183,184,185,186,187 \ } /* ORDER_REGS_FOR_LOCAL_ALLOC is a macro which permits reg_alloc_order to be rearranged based on a particular function. On the mips16, we want to allocate $24 (T_REG) before other registers for instructions for which it is possible. */ #define ORDER_REGS_FOR_LOCAL_ALLOC mips_order_regs_for_local_alloc () /* True if VALUE is an unsigned 6-bit number. */ #define UIMM6_OPERAND(VALUE) \ (((VALUE) & ~(unsigned HOST_WIDE_INT) 0x3f) == 0) /* True if VALUE is a signed 10-bit number. */ #define IMM10_OPERAND(VALUE) \ ((unsigned HOST_WIDE_INT) (VALUE) + 0x200 < 0x400) /* True if VALUE is a signed 16-bit number. */ #define SMALL_OPERAND(VALUE) \ ((unsigned HOST_WIDE_INT) (VALUE) + 0x8000 < 0x10000) /* True if VALUE is an unsigned 16-bit number. */ #define SMALL_OPERAND_UNSIGNED(VALUE) \ (((VALUE) & ~(unsigned HOST_WIDE_INT) 0xffff) == 0) /* True if VALUE can be loaded into a register using LUI. */ #define LUI_OPERAND(VALUE) \ (((VALUE) | 0x7fff0000) == 0x7fff0000 \ || ((VALUE) | 0x7fff0000) + 0x10000 == 0) /* Return a value X with the low 16 bits clear, and such that VALUE - X is a signed 16-bit value. */ #define CONST_HIGH_PART(VALUE) \ (((VALUE) + 0x8000) & ~(unsigned HOST_WIDE_INT) 0xffff) #define CONST_LOW_PART(VALUE) \ ((VALUE) - CONST_HIGH_PART (VALUE)) #define SMALL_INT(X) SMALL_OPERAND (INTVAL (X)) #define SMALL_INT_UNSIGNED(X) SMALL_OPERAND_UNSIGNED (INTVAL (X)) #define LUI_INT(X) LUI_OPERAND (INTVAL (X)) #define PREFERRED_RELOAD_CLASS(X,CLASS) \ mips_preferred_reload_class (X, CLASS) /* Certain machines have the property that some registers cannot be copied to some other registers without using memory. Define this macro on those machines to be a C expression that is nonzero if objects of mode MODE in registers of CLASS1 can only be copied to registers of class CLASS2 by storing a register of CLASS1 into memory and loading that memory location into a register of CLASS2. Do not define this macro if its value would always be zero. */ #if 0 #define SECONDARY_MEMORY_NEEDED(CLASS1, CLASS2, MODE) \ ((!TARGET_DEBUG_H_MODE \ && GET_MODE_CLASS (MODE) == MODE_INT \ && ((CLASS1 == FP_REGS && GR_REG_CLASS_P (CLASS2)) \ || (GR_REG_CLASS_P (CLASS1) && CLASS2 == FP_REGS))) \ || (TARGET_FLOAT64 && !TARGET_64BIT && (MODE) == DFmode \ && ((GR_REG_CLASS_P (CLASS1) && CLASS2 == FP_REGS) \ || (GR_REG_CLASS_P (CLASS2) && CLASS1 == FP_REGS)))) #endif /* The HI and LO registers can only be reloaded via the general registers. Condition code registers can only be loaded to the general registers, and from the floating point registers. */ #define SECONDARY_INPUT_RELOAD_CLASS(CLASS, MODE, X) \ mips_secondary_reload_class (CLASS, MODE, X, 1) #define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, X) \ mips_secondary_reload_class (CLASS, MODE, X, 0) /* Return the maximum number of consecutive registers needed to represent mode MODE in a register of class CLASS. */ #define CLASS_MAX_NREGS(CLASS, MODE) mips_class_max_nregs (CLASS, MODE) #define CANNOT_CHANGE_MODE_CLASS(FROM, TO, CLASS) \ mips_cannot_change_mode_class (FROM, TO, CLASS) /* Stack layout; function entry, exit and calling. */ #define STACK_GROWS_DOWNWARD /* The offset of the first local variable from the beginning of the frame. See compute_frame_size for details about the frame layout. ??? If flag_profile_values is true, and we are generating 32-bit code, then we assume that we will need 16 bytes of argument space. This is because the value profiling code may emit calls to cmpdi2 in leaf functions. Without this hack, the local variables will start at sp+8 and the gp save area will be at sp+16, and thus they will overlap. compute_frame_size is OK because it uses STARTING_FRAME_OFFSET to compute cprestore_size, which will end up as 24 instead of 8. This won't be needed if profiling code is inserted before virtual register instantiation. */ #define STARTING_FRAME_OFFSET \ ((flag_profile_values && ! TARGET_64BIT \ ? MAX (REG_PARM_STACK_SPACE(NULL), current_function_outgoing_args_size) \ : current_function_outgoing_args_size) \ + (TARGET_ABICALLS && !TARGET_NEWABI \ ? MIPS_STACK_ALIGN (UNITS_PER_WORD) : 0)) #define RETURN_ADDR_RTX mips_return_addr /* Since the mips16 ISA mode is encoded in the least-significant bit of the address, mask it off return addresses for purposes of finding exception handling regions. */ #define MASK_RETURN_ADDR GEN_INT (-2) /* Similarly, don't use the least-significant bit to tell pointers to code from vtable index. */ #define TARGET_PTRMEMFUNC_VBIT_LOCATION ptrmemfunc_vbit_in_delta /* The eliminations to $17 are only used for mips16 code. See the definition of HARD_FRAME_POINTER_REGNUM. */ #define ELIMINABLE_REGS \ {{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ { ARG_POINTER_REGNUM, GP_REG_FIRST + 30}, \ { ARG_POINTER_REGNUM, GP_REG_FIRST + 17}, \ { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ { FRAME_POINTER_REGNUM, GP_REG_FIRST + 30}, \ { FRAME_POINTER_REGNUM, GP_REG_FIRST + 17}} /* We can always eliminate to the hard frame pointer. We can eliminate to the stack pointer unless a frame pointer is needed. In mips16 mode, we need a frame pointer for a large frame; otherwise, reload may be unable to compute the address of a local variable, since there is no way to add a large constant to the stack pointer without using a temporary register. */ #define CAN_ELIMINATE(FROM, TO) \ ((TO) == HARD_FRAME_POINTER_REGNUM \ || ((TO) == STACK_POINTER_REGNUM && !frame_pointer_needed \ && (!TARGET_MIPS16 \ || compute_frame_size (get_frame_size ()) < 32768))) #define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ (OFFSET) = mips_initial_elimination_offset ((FROM), (TO)) /* Allocate stack space for arguments at the beginning of each function. */ #define ACCUMULATE_OUTGOING_ARGS 1 /* The argument pointer always points to the first argument. */ #define FIRST_PARM_OFFSET(FNDECL) 0 /* o32 and o64 reserve stack space for all argument registers. */ #define REG_PARM_STACK_SPACE(FNDECL) \ (TARGET_OLDABI \ ? (MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD) \ : 0) /* Define this if it is the responsibility of the caller to allocate the area reserved for arguments passed in registers. If `ACCUMULATE_OUTGOING_ARGS' is also defined, the only effect of this macro is to determine whether the space is included in `current_function_outgoing_args_size'. */ #define OUTGOING_REG_PARM_STACK_SPACE #define STACK_BOUNDARY (TARGET_NEWABI ? 128 : 64) #define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0 /* Symbolic macros for the registers used to return integer and floating point values. */ #define GP_RETURN (GP_REG_FIRST + 2) #define FP_RETURN ((TARGET_SOFT_FLOAT) ? GP_RETURN : (FP_REG_FIRST + 0)) #define MAX_ARGS_IN_REGISTERS (TARGET_OLDABI ? 4 : 8) /* Symbolic macros for the first/last argument registers. */ #define GP_ARG_FIRST (GP_REG_FIRST + 4) #define GP_ARG_LAST (GP_ARG_FIRST + MAX_ARGS_IN_REGISTERS - 1) #define FP_ARG_FIRST (FP_REG_FIRST + 12) #define FP_ARG_LAST (FP_ARG_FIRST + MAX_ARGS_IN_REGISTERS - 1) #define LIBCALL_VALUE(MODE) \ mips_function_value (NULL_TREE, NULL, (MODE)) #define FUNCTION_VALUE(VALTYPE, FUNC) \ mips_function_value ((VALTYPE), (FUNC), VOIDmode) /* 1 if N is a possible register number for a function value. On the MIPS, R2 R3 and F0 F2 are the only register thus used. Currently, R2 and F0 are only implemented here (C has no complex type) */ #define FUNCTION_VALUE_REGNO_P(N) ((N) == GP_RETURN || (N) == FP_RETURN \ || (LONG_DOUBLE_TYPE_SIZE == 128 && FP_RETURN != GP_RETURN \ && (N) == FP_RETURN + 2)) /* 1 if N is a possible register number for function argument passing. We have no FP argument registers when soft-float. When FP registers are 32 bits, we can't directly reference the odd numbered ones. */ #define FUNCTION_ARG_REGNO_P(N) \ ((IN_RANGE((N), GP_ARG_FIRST, GP_ARG_LAST) \ || (IN_RANGE((N), FP_ARG_FIRST, FP_ARG_LAST))) \ && !fixed_regs[N]) /* This structure has to cope with two different argument allocation schemes. Most MIPS ABIs view the arguments as a structure, of which the first N words go in registers and the rest go on the stack. If I < N, the Ith word might go in Ith integer argument register or in a floating-point register. For these ABIs, we only need to remember the offset of the current argument into the structure. The EABI instead allocates the integer and floating-point arguments separately. The first N words of FP arguments go in FP registers, the rest go on the stack. Likewise, the first N words of the other arguments go in integer registers, and the rest go on the stack. We need to maintain three counts: the number of integer registers used, the number of floating-point registers used, and the number of words passed on the stack. We could keep separate information for the two ABIs (a word count for the standard ABIs, and three separate counts for the EABI). But it seems simpler to view the standard ABIs as forms of EABI that do not allocate floating-point registers. So for the standard ABIs, the first N words are allocated to integer registers, and function_arg decides on an argument-by-argument basis whether that argument should really go in an integer register, or in a floating-point one. */ typedef struct mips_args { /* Always true for varargs functions. Otherwise true if at least one argument has been passed in an integer register. */ int gp_reg_found; /* The number of arguments seen so far. */ unsigned int arg_number; /* The number of integer registers used so far. For all ABIs except EABI, this is the number of words that have been added to the argument structure, limited to MAX_ARGS_IN_REGISTERS. */ unsigned int num_gprs; /* For EABI, the number of floating-point registers used so far. */ unsigned int num_fprs; /* The number of words passed on the stack. */ unsigned int stack_words; /* On the mips16, we need to keep track of which floating point arguments were passed in general registers, but would have been passed in the FP regs if this were a 32 bit function, so that we can move them to the FP regs if we wind up calling a 32 bit function. We record this information in fp_code, encoded in base four. A zero digit means no floating point argument, a one digit means an SFmode argument, and a two digit means a DFmode argument, and a three digit is not used. The low order digit is the first argument. Thus 6 == 1 * 4 + 2 means a DFmode argument followed by an SFmode argument. ??? A more sophisticated approach will be needed if MIPS_ABI != ABI_32. */ int fp_code; /* True if the function has a prototype. */ int prototype; } CUMULATIVE_ARGS; /* Initialize a variable CUM of type CUMULATIVE_ARGS for a call to a function whose data type is FNTYPE. For a library call, FNTYPE is 0. */ #define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \ init_cumulative_args (&CUM, FNTYPE, LIBNAME) \ /* Update the data in CUM to advance over an argument of mode MODE and data type TYPE. (TYPE is null for libcalls where that information may not be available.) */ #define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ function_arg_advance (&CUM, MODE, TYPE, NAMED) /* Determine where to put an argument to a function. Value is zero to push the argument on the stack, or a hard register in which to store the argument. MODE is the argument's machine mode. TYPE is the data type of the argument (as a tree). This is null for libcalls where that information may not be available. CUM is a variable of type CUMULATIVE_ARGS which gives info about the preceding args and about the function being called. NAMED is nonzero if this argument is a named parameter (otherwise it is an extra parameter matching an ellipsis). */ #define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ function_arg( &CUM, MODE, TYPE, NAMED) #define FUNCTION_ARG_BOUNDARY function_arg_boundary #define FUNCTION_ARG_PADDING(MODE, TYPE) \ (mips_pad_arg_upward (MODE, TYPE) ? upward : downward) #define BLOCK_REG_PADDING(MODE, TYPE, FIRST) \ (mips_pad_reg_upward (MODE, TYPE) ? upward : downward) /* True if using EABI and varargs can be passed in floating-point registers. Under these conditions, we need a more complex form of va_list, which tracks GPR, FPR and stack arguments separately. */ #define EABI_FLOAT_VARARGS_P \ (mips_abi == ABI_EABI && UNITS_PER_FPVALUE >= UNITS_PER_DOUBLE) /* Say that the epilogue uses the return address register. Note that in the case of sibcalls, the values "used by the epilogue" are considered live at the start of the called function. */ #define EPILOGUE_USES(REGNO) ((REGNO) == 31) /* Treat LOC as a byte offset from the stack pointer and round it up to the next fully-aligned offset. */ #define MIPS_STACK_ALIGN(LOC) \ (TARGET_NEWABI ? ((LOC) + 15) & -16 : ((LOC) + 7) & -8) /* Implement `va_start' for varargs and stdarg. */ #define EXPAND_BUILTIN_VA_START(valist, nextarg) \ mips_va_start (valist, nextarg) /* Output assembler code to FILE to increment profiler label # LABELNO for profiling a function entry. */ #define FUNCTION_PROFILER(FILE, LABELNO) \ { \ if (TARGET_MIPS16) \ sorry ("mips16 function profiling"); \ fprintf (FILE, "\t.set\tnoat\n"); \ fprintf (FILE, "\tmove\t%s,%s\t\t# save current return address\n", \ reg_names[GP_REG_FIRST + 1], reg_names[GP_REG_FIRST + 31]); \ if (!TARGET_NEWABI) \ { \ fprintf (FILE, \ "\t%s\t%s,%s,%d\t\t# _mcount pops 2 words from stack\n", \ TARGET_64BIT ? "dsubu" : "subu", \ reg_names[STACK_POINTER_REGNUM], \ reg_names[STACK_POINTER_REGNUM], \ Pmode == DImode ? 16 : 8); \ } \ fprintf (FILE, "\tjal\t_mcount\n"); \ fprintf (FILE, "\t.set\tat\n"); \ } /* No mips port has ever used the profiler counter word, so don't emit it or the label for it. */ #define NO_PROFILE_COUNTERS 1 /* Define this macro if the code for function profiling should come before the function prologue. Normally, the profiling code comes after. */ /* #define PROFILE_BEFORE_PROLOGUE */ /* EXIT_IGNORE_STACK should be nonzero if, when returning from a function, the stack pointer does not matter. The value is tested only in functions that have frame pointers. No definition is equivalent to always zero. */ #define EXIT_IGNORE_STACK 1 /* A C statement to output, on the stream FILE, assembler code for a block of data that contains the constant parts of a trampoline. This code should not include a label--the label is taken care of automatically. */ #define TRAMPOLINE_TEMPLATE(STREAM) \ { \ if (ptr_mode == DImode) \ fprintf (STREAM, "\t.word\t0x03e0082d\t\t# dmove $1,$31\n"); \ else \ fprintf (STREAM, "\t.word\t0x03e00821\t\t# move $1,$31\n"); \ fprintf (STREAM, "\t.word\t0x04110001\t\t# bgezal $0,.+8\n"); \ fprintf (STREAM, "\t.word\t0x00000000\t\t# nop\n"); \ if (ptr_mode == DImode) \ { \ fprintf (STREAM, "\t.word\t0xdfe30014\t\t# ld $3,20($31)\n"); \ fprintf (STREAM, "\t.word\t0xdfe2001c\t\t# ld $2,28($31)\n"); \ fprintf (STREAM, "\t.word\t0x0060c82d\t\t# dmove $25,$3\n"); \ } \ else \ { \ fprintf (STREAM, "\t.word\t0x8fe30014\t\t# lw $3,20($31)\n"); \ fprintf (STREAM, "\t.word\t0x8fe20018\t\t# lw $2,24($31)\n"); \ fprintf (STREAM, "\t.word\t0x0060c821\t\t# move $25,$3\n"); \ } \ fprintf (STREAM, "\t.word\t0x00600008\t\t# jr $3\n"); \ if (ptr_mode == DImode) \ { \ fprintf (STREAM, "\t.word\t0x0020f82d\t\t# dmove $31,$1\n"); \ fprintf (STREAM, "\t.dword\t0x00000000\t\t# \n"); \ fprintf (STREAM, "\t.dword\t0x00000000\t\t# \n"); \ } \ else \ { \ fprintf (STREAM, "\t.word\t0x0020f821\t\t# move $31,$1\n"); \ fprintf (STREAM, "\t.word\t0x00000000\t\t# \n"); \ fprintf (STREAM, "\t.word\t0x00000000\t\t# \n"); \ } \ } /* A C expression for the size in bytes of the trampoline, as an integer. */ #define TRAMPOLINE_SIZE (32 + GET_MODE_SIZE (ptr_mode) * 2) /* Alignment required for trampolines, in bits. */ #define TRAMPOLINE_ALIGNMENT GET_MODE_BITSIZE (ptr_mode) /* INITIALIZE_TRAMPOLINE calls this library function to flush program and data caches. */ #ifndef CACHE_FLUSH_FUNC #define CACHE_FLUSH_FUNC "_flush_cache" #endif /* A C statement to initialize the variable parts of a trampoline. ADDR is an RTX for the address of the trampoline; FNADDR is an RTX for the address of the nested function; STATIC_CHAIN is an RTX for the static chain value that should be passed to the function when it is called. */ #define INITIALIZE_TRAMPOLINE(ADDR, FUNC, CHAIN) \ { \ rtx func_addr, chain_addr; \ \ func_addr = plus_constant (ADDR, 32); \ chain_addr = plus_constant (func_addr, GET_MODE_SIZE (ptr_mode)); \ emit_move_insn (gen_rtx_MEM (ptr_mode, func_addr), FUNC); \ emit_move_insn (gen_rtx_MEM (ptr_mode, chain_addr), CHAIN); \ \ /* Flush both caches. We need to flush the data cache in case \ the system has a write-back cache. */ \ /* ??? Should check the return value for errors. */ \ if (mips_cache_flush_func && mips_cache_flush_func[0]) \ emit_library_call (gen_rtx_SYMBOL_REF (Pmode, mips_cache_flush_func), \ 0, VOIDmode, 3, ADDR, Pmode, \ GEN_INT (TRAMPOLINE_SIZE), TYPE_MODE (integer_type_node),\ GEN_INT (3), TYPE_MODE (integer_type_node)); \ } /* Addressing modes, and classification of registers for them. */ #define REGNO_OK_FOR_INDEX_P(REGNO) 0 #define REGNO_MODE_OK_FOR_BASE_P(REGNO, MODE) \ mips_regno_mode_ok_for_base_p (REGNO, MODE, 1) /* The macros REG_OK_FOR..._P assume that the arg is a REG rtx and check its validity for a certain class. We have two alternate definitions for each of them. The usual definition accepts all pseudo regs; the other rejects them all. The symbol REG_OK_STRICT causes the latter definition to be used. Most source files want to accept pseudo regs in the hope that they will get allocated to the class that the insn wants them to be in. Some source files that are used after register allocation need to be strict. */ #ifndef REG_OK_STRICT #define REG_MODE_OK_FOR_BASE_P(X, MODE) \ mips_regno_mode_ok_for_base_p (REGNO (X), MODE, 0) #else #define REG_MODE_OK_FOR_BASE_P(X, MODE) \ mips_regno_mode_ok_for_base_p (REGNO (X), MODE, 1) #endif #define REG_OK_FOR_INDEX_P(X) 0 /* Maximum number of registers that can appear in a valid memory address. */ #define MAX_REGS_PER_ADDRESS 1 #ifdef REG_OK_STRICT #define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ { \ if (mips_legitimate_address_p (MODE, X, 1)) \ goto ADDR; \ } #else #define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ { \ if (mips_legitimate_address_p (MODE, X, 0)) \ goto ADDR; \ } #endif /* Check for constness inline but use mips_legitimate_address_p to check whether a constant really is an address. */ #define CONSTANT_ADDRESS_P(X) \ (CONSTANT_P (X) && mips_legitimate_address_p (SImode, X, 0)) #define LEGITIMATE_CONSTANT_P(X) (mips_const_insns (X) > 0) #define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) \ do { \ if (mips_legitimize_address (&(X), MODE)) \ goto WIN; \ } while (0) /* A C statement or compound statement with a conditional `goto LABEL;' executed if memory address X (an RTX) can have different meanings depending on the machine mode of the memory reference it is used for. Autoincrement and autodecrement addresses typically have mode-dependent effects because the amount of the increment or decrement is the size of the operand being addressed. Some machines have other mode-dependent addresses. Many RISC machines have no mode-dependent addresses. You may assume that ADDR is a valid address for the machine. */ #define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) {} /* This handles the magic '..CURRENT_FUNCTION' symbol, which means 'the start of the function that this code is output in'. */ #define ASM_OUTPUT_LABELREF(FILE,NAME) \ if (strcmp (NAME, "..CURRENT_FUNCTION") == 0) \ asm_fprintf ((FILE), "%U%s", \ XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); \ else \ asm_fprintf ((FILE), "%U%s", (NAME)) /* Flag to mark a function decl symbol that requires a long call. */ #define SYMBOL_FLAG_LONG_CALL (SYMBOL_FLAG_MACH_DEP << 0) #define SYMBOL_REF_LONG_CALL_P(X) \ ((SYMBOL_REF_FLAGS (X) & SYMBOL_FLAG_LONG_CALL) != 0) /* Specify the machine mode that this machine uses for the index in the tablejump instruction. ??? Using HImode in mips16 mode can cause overflow. */ #define CASE_VECTOR_MODE \ (TARGET_MIPS16 ? HImode : ptr_mode) /* Define as C expression which evaluates to nonzero if the tablejump instruction expects the table to contain offsets from the address of the table. Do not define this if the table should contain absolute addresses. */ #define CASE_VECTOR_PC_RELATIVE (TARGET_MIPS16) /* Define this as 1 if `char' should by default be signed; else as 0. */ #ifndef DEFAULT_SIGNED_CHAR #define DEFAULT_SIGNED_CHAR 1 #endif /* Max number of bytes we can move from memory to memory in one reasonably fast instruction. */ #define MOVE_MAX (TARGET_64BIT ? 8 : 4) #define MAX_MOVE_MAX 8 /* Define this macro as a C expression which is nonzero if accessing less than a word of memory (i.e. a `char' or a `short') is no faster than accessing a word of memory, i.e., if such access require more than one instruction or if there is no difference in cost between byte and (aligned) word loads. On RISC machines, it tends to generate better code to define this as 1, since it avoids making a QI or HI mode register. */ #define SLOW_BYTE_ACCESS 1 /* Define this to be nonzero if shift instructions ignore all but the low-order few bits. */ #define SHIFT_COUNT_TRUNCATED 1 /* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits is done just by pretending it is already truncated. */ #define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) \ (TARGET_64BIT ? ((INPREC) <= 32 || (OUTPREC) > 32) : 1) /* Specify the machine mode that pointers have. After generation of rtl, the compiler makes no further distinction between pointers and any other objects of this machine mode. */ #ifndef Pmode #define Pmode (TARGET_64BIT && TARGET_LONG64 ? DImode : SImode) #endif /* Give call MEMs SImode since it is the "most permissive" mode for both 32-bit and 64-bit targets. */ #define FUNCTION_MODE SImode /* The cost of loading values from the constant pool. It should be larger than the cost of any constant we want to synthesize in-line. */ #define CONSTANT_POOL_COST COSTS_N_INSNS (8) /* A C expression for the cost of moving data from a register in class FROM to one in class TO. The classes are expressed using the enumeration values such as `GENERAL_REGS'. A value of 2 is the default; other values are interpreted relative to that. It is not required that the cost always equal 2 when FROM is the same as TO; on some machines it is expensive to move between registers if they are not general registers. If reload sees an insn consisting of a single `set' between two hard registers, and if `REGISTER_MOVE_COST' applied to their classes returns a value of 2, reload does not check to ensure that the constraints of the insn are met. Setting a cost of other than 2 will allow reload to verify that the constraints are met. You should do this if the `movM' pattern's constraints do not allow such copying. */ #define REGISTER_MOVE_COST(MODE, FROM, TO) \ mips_register_move_cost (MODE, FROM, TO) #define MEMORY_MOVE_COST(MODE,CLASS,TO_P) \ (mips_cost->memory_latency \ + memory_move_secondary_cost ((MODE), (CLASS), (TO_P))) /* Define if copies to/from condition code registers should be avoided. This is needed for the MIPS because reload_outcc is not complete; it needs to handle cases where the source is a general or another condition code register. */ #define AVOID_CCMODE_COPIES /* A C expression for the cost of a branch instruction. A value of 1 is the default; other values are interpreted relative to that. */ #define BRANCH_COST mips_cost->branch_cost #define LOGICAL_OP_NON_SHORT_CIRCUIT 0 /* If defined, modifies the length assigned to instruction INSN as a function of the context in which it is used. LENGTH is an lvalue that contains the initially computed length of the insn and should be updated with the correct length of the insn. */ #define ADJUST_INSN_LENGTH(INSN, LENGTH) \ ((LENGTH) = mips_adjust_insn_length ((INSN), (LENGTH))) /* Return the asm template for a non-MIPS16 conditional branch instruction. OPCODE is the opcode's mnemonic and OPERANDS is the asm template for its operands. */ #define MIPS_BRANCH(OPCODE, OPERANDS) \ "%*" OPCODE "%?\t" OPERANDS "%/" /* Return the asm template for a call. INSN is the instruction's mnemonic ("j" or "jal"), OPERANDS are its operands, and OPNO is the operand number of the target. When generating -mabicalls without explicit relocation operators, all calls should use assembly macros. Otherwise, all indirect calls should use "jr" or "jalr"; we will arrange to restore $gp afterwards if necessary. Finally, we can only generate direct calls for -mabicalls by temporarily switching to non-PIC mode. */ #define MIPS_CALL(INSN, OPERANDS, OPNO) \ (TARGET_ABICALLS && !TARGET_EXPLICIT_RELOCS \ ? "%*" INSN "\t%" #OPNO "%/" \ : REG_P (OPERANDS[OPNO]) \ ? "%*" INSN "r\t%" #OPNO "%/" \ : TARGET_ABICALLS \ ? (".option\tpic0\n\t" \ "%*" INSN "\t%" #OPNO "%/\n\t" \ ".option\tpic2") \ : "%*" INSN "\t%" #OPNO "%/") /* Control the assembler format that we output. */ /* Output to assembler file text saying following lines may contain character constants, extra white space, comments, etc. */ #ifndef ASM_APP_ON #define ASM_APP_ON " #APP\n" #endif /* Output to assembler file text saying following lines no longer contain unusual constructs. */ #ifndef ASM_APP_OFF #define ASM_APP_OFF " #NO_APP\n" #endif #define REGISTER_NAMES \ { "$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", "", "$fcc0","$fcc1","$fcc2","$fcc3","$fcc4", \ "$fcc5","$fcc6","$fcc7","", "", "$arg", "$frame", "$fakec", \ "$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", \ "$ac1hi","$ac1lo","$ac2hi","$ac2lo","$ac3hi","$ac3lo","$dsp_po","$dsp_sc", \ "$dsp_ca","$dsp_ou","$dsp_cc","$dsp_ef" } /* List the "software" names for each register. Also list the numerical names for $fp and $sp. */ #define ADDITIONAL_REGISTER_NAMES \ { \ { "$29", 29 + GP_REG_FIRST }, \ { "$30", 30 + GP_REG_FIRST }, \ { "at", 1 + GP_REG_FIRST }, \ { "v0", 2 + GP_REG_FIRST }, \ { "v1", 3 + GP_REG_FIRST }, \ { "a0", 4 + GP_REG_FIRST }, \ { "a1", 5 + GP_REG_FIRST }, \ { "a2", 6 + GP_REG_FIRST }, \ { "a3", 7 + GP_REG_FIRST }, \ { "t0", 8 + GP_REG_FIRST }, \ { "t1", 9 + GP_REG_FIRST }, \ { "t2", 10 + GP_REG_FIRST }, \ { "t3", 11 + GP_REG_FIRST }, \ { "t4", 12 + GP_REG_FIRST }, \ { "t5", 13 + GP_REG_FIRST }, \ { "t6", 14 + GP_REG_FIRST }, \ { "t7", 15 + GP_REG_FIRST }, \ { "s0", 16 + GP_REG_FIRST }, \ { "s1", 17 + GP_REG_FIRST }, \ { "s2", 18 + GP_REG_FIRST }, \ { "s3", 19 + GP_REG_FIRST }, \ { "s4", 20 + GP_REG_FIRST }, \ { "s5", 21 + GP_REG_FIRST }, \ { "s6", 22 + GP_REG_FIRST }, \ { "s7", 23 + GP_REG_FIRST }, \ { "t8", 24 + GP_REG_FIRST }, \ { "t9", 25 + GP_REG_FIRST }, \ { "k0", 26 + GP_REG_FIRST }, \ { "k1", 27 + GP_REG_FIRST }, \ { "gp", 28 + GP_REG_FIRST }, \ { "sp", 29 + GP_REG_FIRST }, \ { "fp", 30 + GP_REG_FIRST }, \ { "ra", 31 + GP_REG_FIRST }, \ ALL_COP_ADDITIONAL_REGISTER_NAMES \ } /* This is meant to be redefined in the host dependent files. It is a set of alternative names and regnums for mips coprocessors. */ #define ALL_COP_ADDITIONAL_REGISTER_NAMES /* A C compound statement to output to stdio stream STREAM the assembler syntax for an instruction operand X. X is an RTL expression. CODE is a value that can be used to specify one of several ways of printing the operand. It is used when identical operands must be printed differently depending on the context. CODE comes from the `%' specification that was used to request printing of the operand. If the specification was just `%DIGIT' then CODE is 0; if the specification was `%LTR DIGIT' then CODE is the ASCII code for LTR. If X is a register, this macro should print the register's name. The names can be found in an array `reg_names' whose type is `char *[]'. `reg_names' is initialized from `REGISTER_NAMES'. When the machine description has a specification `%PUNCT' (a `%' followed by a punctuation character), this macro is called with a null pointer for X and the punctuation character for CODE. See mips.c for the MIPS specific codes. */ #define PRINT_OPERAND(FILE, X, CODE) print_operand (FILE, X, CODE) /* A C expression which evaluates to true if CODE is a valid punctuation character for use in the `PRINT_OPERAND' macro. If `PRINT_OPERAND_PUNCT_VALID_P' is not defined, it means that no punctuation characters (except for the standard one, `%') are used in this way. */ #define PRINT_OPERAND_PUNCT_VALID_P(CODE) mips_print_operand_punct[CODE] /* A C compound statement to output to stdio stream STREAM the assembler syntax for an instruction operand that is a memory reference whose address is ADDR. ADDR is an RTL expression. */ #define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address (FILE, ADDR) /* A C statement, to be executed after all slot-filler instructions have been output. If necessary, call `dbr_sequence_length' to determine the number of slots filled in a sequence (zero if not currently outputting a sequence), to decide how many no-ops to output, or whatever. Don't define this macro if it has nothing to do, but it is helpful in reading assembly output if the extent of the delay sequence is made explicit (e.g. with white space). Note that output routines for instructions with delay slots must be prepared to deal with not being output as part of a sequence (i.e. when the scheduling pass is not run, or when no slot fillers could be found.) The variable `final_sequence' is null when not processing a sequence, otherwise it contains the `sequence' rtx being output. */ #define DBR_OUTPUT_SEQEND(STREAM) \ do \ { \ if (set_nomacro > 0 && --set_nomacro == 0) \ fputs ("\t.set\tmacro\n", STREAM); \ \ if (set_noreorder > 0 && --set_noreorder == 0) \ fputs ("\t.set\treorder\n", STREAM); \ \ fputs ("\n", STREAM); \ } \ while (0) /* How to tell the debugger about changes of source files. */ #define ASM_OUTPUT_SOURCE_FILENAME(STREAM, NAME) \ mips_output_filename (STREAM, NAME) /* mips-tfile does not understand .stabd directives. */ #define DBX_OUTPUT_SOURCE_LINE(STREAM, LINE, COUNTER) do { \ dbxout_begin_stabn_sline (LINE); \ dbxout_stab_value_internal_label ("LM", &COUNTER); \ } while (0) /* Use .loc directives for SDB line numbers. */ #define SDB_OUTPUT_SOURCE_LINE(STREAM, LINE) \ fprintf (STREAM, "\t.loc\t%d %d\n", num_source_filenames, LINE) /* The MIPS implementation uses some labels for its own purpose. The following lists what labels are created, and are all formed by the pattern $L[a-z].*. The machine independent portion of GCC creates labels matching: $L[A-Z][0-9]+ and $L[0-9]+. LM[0-9]+ Silicon Graphics/ECOFF stabs label before each stmt. $Lb[0-9]+ Begin blocks for MIPS debug support $Lc[0-9]+ Label for use in s operation. $Le[0-9]+ End blocks for MIPS debug support */ #undef ASM_DECLARE_OBJECT_NAME #define ASM_DECLARE_OBJECT_NAME(STREAM, NAME, DECL) \ mips_declare_object (STREAM, NAME, "", ":\n", 0) /* Globalizing directive for a label. */ #define GLOBAL_ASM_OP "\t.globl\t" /* This says how to define a global common symbol. */ #define ASM_OUTPUT_ALIGNED_DECL_COMMON mips_output_aligned_decl_common /* This says how to define a local common symbol (i.e., not visible to linker). */ #ifndef ASM_OUTPUT_ALIGNED_LOCAL #define ASM_OUTPUT_ALIGNED_LOCAL(STREAM, NAME, SIZE, ALIGN) \ mips_declare_common_object (STREAM, NAME, "\n\t.lcomm\t", SIZE, ALIGN, false) #endif /* This says how to output an external. It would be possible not to output anything and let undefined symbol become external. However the assembler uses length information on externals to allocate in data/sdata bss/sbss, thereby saving exec time. */ #define ASM_OUTPUT_EXTERNAL(STREAM,DECL,NAME) \ mips_output_external(STREAM,DECL,NAME) /* This is how to declare a function name. The actual work of emitting the label is moved to function_prologue, so that we can get the line number correctly emitted before the .ent directive, and after any .file directives. Define as empty so that the function is not declared before the .ent directive elsewhere. */ #undef ASM_DECLARE_FUNCTION_NAME #define ASM_DECLARE_FUNCTION_NAME(STREAM,NAME,DECL) #ifndef FUNCTION_NAME_ALREADY_DECLARED #define FUNCTION_NAME_ALREADY_DECLARED 0 #endif /* This is how to store into the string LABEL the symbol_ref name of an internal numbered label where PREFIX is the class of label and NUM is the number within the class. This is suitable for output with `assemble_name'. */ #undef ASM_GENERATE_INTERNAL_LABEL #define ASM_GENERATE_INTERNAL_LABEL(LABEL,PREFIX,NUM) \ sprintf ((LABEL), "*%s%s%ld", (LOCAL_LABEL_PREFIX), (PREFIX), (long)(NUM)) /* This is how to output an element of a case-vector that is absolute. */ #define ASM_OUTPUT_ADDR_VEC_ELT(STREAM, VALUE) \ fprintf (STREAM, "\t%s\t%sL%d\n", \ ptr_mode == DImode ? ".dword" : ".word", \ LOCAL_LABEL_PREFIX, \ VALUE) /* This is how to output an element of a case-vector. We can make the entries PC-relative in MIPS16 code and GP-relative when .gp(d)word is supported. */ #define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM, BODY, VALUE, REL) \ do { \ if (TARGET_MIPS16) \ fprintf (STREAM, "\t.half\t%sL%d-%sL%d\n", \ LOCAL_LABEL_PREFIX, VALUE, LOCAL_LABEL_PREFIX, REL); \ else if (TARGET_GPWORD) \ fprintf (STREAM, "\t%s\t%sL%d\n", \ ptr_mode == DImode ? ".gpdword" : ".gpword", \ LOCAL_LABEL_PREFIX, VALUE); \ else \ fprintf (STREAM, "\t%s\t%sL%d\n", \ ptr_mode == DImode ? ".dword" : ".word", \ LOCAL_LABEL_PREFIX, VALUE); \ } while (0) /* When generating MIPS16 code, we want the jump table to be in the text section so that we can load its address using a PC-relative addition. */ #define JUMP_TABLES_IN_TEXT_SECTION TARGET_MIPS16 /* This is how to output an assembler line that says to advance the location counter to a multiple of 2**LOG bytes. */ #define ASM_OUTPUT_ALIGN(STREAM,LOG) \ fprintf (STREAM, "\t.align\t%d\n", (LOG)) /* This is how to output an assembler line to advance the location counter by SIZE bytes. */ #undef ASM_OUTPUT_SKIP #define ASM_OUTPUT_SKIP(STREAM,SIZE) \ fprintf (STREAM, "\t.space\t"HOST_WIDE_INT_PRINT_UNSIGNED"\n", (SIZE)) /* This is how to output a string. */ #undef ASM_OUTPUT_ASCII #define ASM_OUTPUT_ASCII(STREAM, STRING, LEN) \ mips_output_ascii (STREAM, STRING, LEN, "\t.ascii\t") /* Output #ident as a in the read-only data section. */ #undef ASM_OUTPUT_IDENT #define ASM_OUTPUT_IDENT(FILE, STRING) \ { \ const char *p = STRING; \ int size = strlen (p) + 1; \ switch_to_section (readonly_data_section); \ assemble_string (p, size); \ } /* Default to -G 8 */ #ifndef MIPS_DEFAULT_GVALUE #define MIPS_DEFAULT_GVALUE 8 #endif /* Define the strings to put out for each section in the object file. */ #define TEXT_SECTION_ASM_OP "\t.text" /* instructions */ #define DATA_SECTION_ASM_OP "\t.data" /* large data */ #undef READONLY_DATA_SECTION_ASM_OP #define READONLY_DATA_SECTION_ASM_OP "\t.rdata" /* read-only data */ #define ASM_OUTPUT_REG_PUSH(STREAM,REGNO) \ do \ { \ fprintf (STREAM, "\t%s\t%s,%s,8\n\t%s\t%s,0(%s)\n", \ TARGET_64BIT ? "dsubu" : "subu", \ reg_names[STACK_POINTER_REGNUM], \ reg_names[STACK_POINTER_REGNUM], \ TARGET_64BIT ? "sd" : "sw", \ reg_names[REGNO], \ reg_names[STACK_POINTER_REGNUM]); \ } \ while (0) #define ASM_OUTPUT_REG_POP(STREAM,REGNO) \ do \ { \ if (! set_noreorder) \ fprintf (STREAM, "\t.set\tnoreorder\n"); \ \ fprintf (STREAM, "\t%s\t%s,0(%s)\n\t%s\t%s,%s,8\n", \ TARGET_64BIT ? "ld" : "lw", \ reg_names[REGNO], \ reg_names[STACK_POINTER_REGNUM], \ TARGET_64BIT ? "daddu" : "addu", \ reg_names[STACK_POINTER_REGNUM], \ reg_names[STACK_POINTER_REGNUM]); \ \ if (! set_noreorder) \ fprintf (STREAM, "\t.set\treorder\n"); \ } \ while (0) /* How to start an assembler comment. The leading space is important (the mips native assembler requires it). */ #ifndef ASM_COMMENT_START #define ASM_COMMENT_START " #" #endif /* Default definitions for size_t and ptrdiff_t. We must override the definitions from ../svr4.h on mips-*-linux-gnu. */ #undef SIZE_TYPE #define SIZE_TYPE (POINTER_SIZE == 64 ? "long unsigned int" : "unsigned int") #undef PTRDIFF_TYPE #define PTRDIFF_TYPE (POINTER_SIZE == 64 ? "long int" : "int") #ifndef __mips16 /* Since the bits of the _init and _fini function is spread across many object files, each potentially with its own GP, we must assume we need to load our GP. We don't preserve $gp or $ra, since each init/fini chunk is supposed to initialize $gp, and crti/crtn already take care of preserving $ra and, when appropriate, $gp. */ #if (defined _ABIO32 && _MIPS_SIM == _ABIO32) #define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \ asm (SECTION_OP "\n\ .set noreorder\n\ bal 1f\n\ nop\n\ 1: .cpload $31\n\ .set reorder\n\ jal " USER_LABEL_PREFIX #FUNC "\n\ " TEXT_SECTION_ASM_OP); #endif /* Switch to #elif when we're no longer limited by K&R C. */ #if (defined _ABIN32 && _MIPS_SIM == _ABIN32) \ || (defined _ABI64 && _MIPS_SIM == _ABI64) #define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \ asm (SECTION_OP "\n\ .set noreorder\n\ bal 1f\n\ nop\n\ 1: .set reorder\n\ .cpsetup $31, $2, 1b\n\ jal " USER_LABEL_PREFIX #FUNC "\n\ " TEXT_SECTION_ASM_OP); #endif #endif #ifndef HAVE_AS_TLS #define HAVE_AS_TLS 0 #endif