| /* tc-nds32.c -- Assemble for the nds32 |
| Copyright (C) 2012-2025 Free Software Foundation, Inc. |
| Contributed by Andes Technology Corporation. |
| |
| This file is part of GAS, the GNU Assembler. |
| |
| 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 3, 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 "safe-ctype.h" |
| #include "subsegs.h" |
| #include "symcat.h" |
| #include "dwarf2dbg.h" |
| #include "dw2gencfi.h" |
| #include "opcodes/nds32-asm.h" |
| #include "elf/nds32.h" |
| #include "bfd/elf32-nds32.h" |
| #include "hash.h" |
| #include "sb.h" |
| #include "macro.h" |
| #include "opcode/nds32.h" |
| |
| #include <stdio.h> |
| #include <errno.h> |
| #include <limits.h> |
| |
| /* GAS definitions. */ |
| |
| /* Characters which start a comment. */ |
| const char comment_chars[] = "!"; |
| /* Characters which start a comment when they appear at the start of a line. */ |
| const char line_comment_chars[] = "#!"; |
| /* Characters which separate lines (null and newline are by default). */ |
| const char line_separator_chars[] = ";"; |
| /* Characters which may be used as the exponent character |
| in a floating point number. */ |
| const char EXP_CHARS[] = "eE"; |
| /* Characters which may be used to indicate a floating point constant. */ |
| const char FLT_CHARS[] = "dDfF"; |
| |
| static int enable_16bit = 1; |
| /* Save for md_assemble to distinguish if this instruction is |
| expanded from the pseudo instruction. */ |
| static bool pseudo_opcode = false; |
| static struct nds32_relocs_pattern *relocs_list = NULL; |
| /* Save instruction relation to inserting relaxation relocation. */ |
| struct nds32_relocs_pattern |
| { |
| segT seg; |
| fragS *frag; |
| frchainS *frchain; |
| symbolS *sym; |
| fixS* fixP; |
| struct nds32_opcode *opcode; |
| char *where; |
| struct nds32_relocs_pattern *next; |
| /* Assembled instruction bytes. */ |
| uint32_t insn; |
| }; |
| |
| /* Suffix name and relocation. */ |
| struct suffix_name |
| { |
| const char *suffix; |
| short unsigned int reloc; |
| }; |
| static int vec_size = 0; |
| /* If the assembly code is generated by compiler, it is supposed to have |
| ".flag verbatim" at beginning of the content. We have |
| 'nds32_flag' to parse it and set this field to be non-zero. */ |
| static int verbatim = 0; |
| static htab_t nds32_gprs_hash; |
| static htab_t nds32_hint_hash; |
| #define TLS_REG "$r27" |
| #define GOT_NAME "_GLOBAL_OFFSET_TABLE_" |
| |
| /* Generate relocation for relax or not, and the default is true. */ |
| static int enable_relax_relocs = 1; |
| /* Save option -O for performance. */ |
| static int optimize = 0; |
| /* Save option -Os for code size. */ |
| static int optimize_for_space = 0; |
| /* Flag to save label exist. */ |
| static int label_exist = 0; |
| /* Flag to save state in omit_fp region. */ |
| static int in_omit_fp = 0; |
| /* Tag there is relax relocation having to link. */ |
| static bool relaxing = false; |
| /* ICT model. */ |
| enum ict_option { |
| ICT_NONE = 0, |
| ICT_SMALL, |
| ICT_LARGE |
| }; |
| static enum ict_option ict_flag = ICT_NONE; |
| |
| |
| static htab_t nds32_relax_info_hash; |
| |
| /* Branch patterns. */ |
| static relax_info_t relax_table[] = |
| { |
| { |
| .opcode = "jal", |
| .br_range = BR_RANGE_S16M, |
| .cond_field = |
| { |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_JAL /* jal label */ |
| }, |
| .relax_code_size[BR_RANGE_S256] = 4, |
| .relax_branch_isize[BR_RANGE_S256] = 4, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_JAL /* jal label */ |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_JAL /* jal label */ |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 4, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_JAL /* jal label */ |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 4, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JRAL_TA /* jral $ta */ |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 12, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGCALL4}, |
| {4, 4, NDS32_HINT | NDS32_FIX, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {8, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {8, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "bgezal", |
| .br_range = BR_RANGE_S64K, |
| .cond_field = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BGEZAL /* bgezal $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S256] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S256] = 4, |
| .relax_branch_isize[BR_RANGE_S256] = 4, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_BGEZAL /* bgezal $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BGEZAL /* bgezal $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S64K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 4, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BLTZ, /* bltz $rt, $1 */ |
| INSN_JAL /* jal label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16M] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGCALL5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BLTZ, /* bltz $rt, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JRAL_TA /* jral $ta */ |
| }, |
| .relax_code_condition[BR_RANGE_U4G] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGCALL6}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {8, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "bltzal", |
| .br_range = BR_RANGE_S64K, |
| .cond_field = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BLTZAL /* bltzal $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S256] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S256] = 4, |
| .relax_branch_isize[BR_RANGE_S256] = 4, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_BLTZAL /* bltzal $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BLTZAL /* bltzal $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S64K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 4, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BGEZ, /* bgez $rt, $1 */ |
| INSN_JAL /* jal label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16M] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGCALL5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BGEZ, /* bgez $rt, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JRAL_TA /* jral $ta */ |
| }, |
| .relax_code_condition[BR_RANGE_U4G] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGCALL6}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {8, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "j", |
| .br_range = BR_RANGE_S16M, |
| .cond_field = |
| { |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| (INSN_J8 << 16) /* j8 label */ |
| }, |
| .relax_code_size[BR_RANGE_S256] = 2, |
| .relax_branch_isize[BR_RANGE_S256] = 2, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 2, NDS32_PCREL, BFD_RELOC_NDS32_9_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_J /* j label */ |
| }, |
| . relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_J /* j label */ |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 4, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_J /* j label */ |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 4, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 12, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP4}, |
| {4, 4, NDS32_HINT | NDS32_FIX, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {8, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {8, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "j8", |
| .br_range = BR_RANGE_S256, |
| .cond_field = |
| { |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| (INSN_J8 << 16) /* j8 label */ |
| }, |
| .relax_code_size[BR_RANGE_S256] = 2, |
| .relax_branch_isize[BR_RANGE_S256] = 2, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 2, NDS32_PCREL, BFD_RELOC_NDS32_9_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_J /* j label */ |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_J /* j label */ |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 4, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_J /* j label */ |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 4, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 12, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP4}, |
| {4, 4, NDS32_HINT | NDS32_FIX, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {8, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {8, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "beqz", |
| .br_range = BR_RANGE_S64K, |
| .cond_field = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| /* We do not use beqz38 and beqzs8 here directly because we |
| don't want to check register number for specail condition. */ |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BEQZ /* beqz $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S256] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S256] = 4, |
| .relax_branch_isize[BR_RANGE_S256] = 4, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 4, NDS32_INSN16 , BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_BEQZ /* beqz $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BEQZ /* beqz $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S64K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 4, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BNEZ, /* bnez $rt, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16M] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| /* bnez range is 17 pcrel, but it use 15 pcrel here since link time |
| relaxtion. If 17 pcrel can reach, it do not have to use S16M. |
| Therefore, 15 pcrel is just for linker to distinguish LONGJUMP5 |
| and LONGJUMP6. */ |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BNEZ, /* bnez $rt, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_condition[BR_RANGE_U4G] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "bgez", |
| .br_range = BR_RANGE_S64K, |
| .cond_field = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BGEZ /* bgez $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S256] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S256] = 4, |
| .relax_branch_isize[BR_RANGE_S256] = 4, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_BGEZ /* bgez $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BGEZ /* bgez $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S64K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 4, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BLTZ, /* bltz $rt, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16M] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BLTZ, /* bltz $rt, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_condition[BR_RANGE_U4G] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "bnez", |
| .br_range = BR_RANGE_S64K, |
| .cond_field = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BNEZ /* bnez $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S256] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S256] = 4, |
| .relax_branch_isize[BR_RANGE_S256] = 4, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 4, NDS32_INSN16, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_BNEZ /* bnez $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BNEZ /* bnez $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S64K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 4, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BEQZ, /* beqz $rt, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16M] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BEQZ, /* beqz $rt, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_condition[BR_RANGE_U4G] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "bgtz", |
| .br_range = BR_RANGE_S64K, |
| .cond_field = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BGTZ /* bgtz $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S256] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S256] = 4, |
| .relax_branch_isize[BR_RANGE_S256] = 4, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_BGTZ /* bgtz $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BGTZ /* bgtz $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S64K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 4, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BLEZ, /* blez $rt, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16M] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BLEZ, /* blez $rt, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_condition[BR_RANGE_U4G] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "blez", |
| .br_range = BR_RANGE_S64K, |
| .cond_field = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BLEZ /* blez $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S256] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S256] = 4, |
| .relax_branch_isize[BR_RANGE_S256] = 4, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_BLEZ /* blez $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BLEZ /* blez $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S64K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 4, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BGTZ, /* bgtz $rt, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16M] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BGTZ, /* bgtz $rt, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_condition[BR_RANGE_U4G] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "bltz", |
| .br_range = BR_RANGE_S64K, |
| .cond_field = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BLTZ /* bltz $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S256] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S256] = 4, |
| .relax_branch_isize[BR_RANGE_S256] = 4, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_BLTZ /* bltz $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BLTZ /* bltz $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S64K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 4, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BGEZ, /* bgez $rt, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16M] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BGEZ, /* bgez $rt, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_condition[BR_RANGE_U4G] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false}, |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "beq", |
| .br_range = BR_RANGE_S16K, |
| .cond_field = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 15, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BEQ /* beq $rt, $ra, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S256] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 15, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S256] = 4, |
| .relax_branch_isize[BR_RANGE_S256] = 4, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_BEQ /* beq $rt, $ra, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 15, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BNE, /* bne $rt, $ra, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S64K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 15, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 8, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BNE, /* bne $rt, $ra, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16M] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 15, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BNE, /* bne $rt, $ra, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_condition[BR_RANGE_U4G] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 15, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "bne", |
| .br_range = BR_RANGE_S16K, |
| .cond_field = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 15, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BNE /* bne $rt, $ra, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S256] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 15, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S256] = 4, |
| .relax_branch_isize[BR_RANGE_S256] = 4, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_BNE /* bne $rt, $ra, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 15, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BEQ, /* beq $rt, $ra, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S64K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 15, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 8, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BEQ, /* beq $rt, $ra, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16M] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 15, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BEQ, /* beq $rt, $ra, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_condition[BR_RANGE_U4G] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 15, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "beqz38", |
| .br_range = BR_RANGE_S256, |
| .cond_field = |
| { |
| {0, 8, 0x7, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BEQZ38 << 16 /* beqz $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S256] = |
| { |
| {0, 8, 0x7, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S256] = 2, |
| .relax_branch_isize[BR_RANGE_S256] = 2, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 2, NDS32_PCREL, BFD_RELOC_NDS32_9_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_BEQZ /* beqz $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BEQZ /* beqz $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S64K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 4, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BNEZ, /* bnez $rt, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16M] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BNEZ, /* bnez $rt, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_condition[BR_RANGE_U4G] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "bnez38", |
| .br_range = BR_RANGE_S256, |
| .cond_field = |
| { |
| {0, 8, 0x7, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BNEZ38 << 16 /* bnez $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S256] = |
| { |
| {0, 8, 0x7, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S256] = 2, |
| .relax_branch_isize[BR_RANGE_S256] = 2, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 2, NDS32_PCREL, BFD_RELOC_NDS32_9_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_BNEZ /* bnez $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BNEZ /* bnez $rt, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S64K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 4, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BEQZ, /* beqz $rt, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16M] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BEQZ, /* beqz $rt, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_condition[BR_RANGE_U4G] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "beqzs8", |
| .br_range = BR_RANGE_S256, |
| .cond_field = |
| { |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BEQZS8 << 16 /* beqz $r15, label */ |
| }, |
| .relax_code_size[BR_RANGE_S256] = 2, |
| .relax_branch_isize[BR_RANGE_S256] = 2, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 2, NDS32_PCREL, BFD_RELOC_NDS32_9_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_BEQZ_TA /* beqz $r15, label */ |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BEQZ_TA /* beqz $r15, label */ |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 4, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BNEZ_TA, /* bnez $r15, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BNEZ_TA, /* bnez $r15, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "bnezs8", |
| .br_range = BR_RANGE_S256, |
| .cond_field = |
| { |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BNEZS8 << 16 /* bnez $r15, label */ |
| }, |
| .relax_code_size[BR_RANGE_S256] = 2, |
| .relax_branch_isize[BR_RANGE_S256] = 2, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 2, NDS32_PCREL, BFD_RELOC_NDS32_9_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_BNEZ_TA /* bnez $r15, label */ |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BNEZ_TA /* bnez $r15, label */ |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 4, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_17_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BEQZ_TA, /* beqz $r15, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BEQZ_TA, /* beqz $r15, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "bnes38", |
| .br_range = BR_RANGE_S256, |
| .cond_field = |
| { |
| {0, 8, 0x7, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BNES38 << 16 /* bne $rt, $r5, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S256] = |
| { |
| {0, 8, 0x7, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S256] = 2, |
| .relax_branch_isize[BR_RANGE_S256] = 2, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 2, NDS32_PCREL, BFD_RELOC_NDS32_9_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_BNE_R5 /* bne $rt, $r5, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BEQ_R5, /* beq $rt, $r5, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S64K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 8, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BEQ_R5, /* beq $rt, $r5, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16M] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BEQ_R5, /* beq $rt, $r5, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_condition[BR_RANGE_U4G] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "beqs38", |
| .br_range = BR_RANGE_S256, |
| .cond_field = |
| { |
| {0, 8, 0x7, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BEQS38 << 16 /* beq $rt, $r5, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S256] = |
| { |
| {0, 8, 0x7, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S256] = 2, |
| .relax_branch_isize[BR_RANGE_S256] = 2, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 2, NDS32_PCREL, BFD_RELOC_NDS32_9_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_BEQ_R5 /* beq $rt, $r5, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 4, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BNE_R5, /* bne $rt, $r5, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S64K] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 8, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BNE_R5, /* bne $rt, $r5, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16M] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BNE_R5, /* bne $rt, $r5, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_condition[BR_RANGE_U4G] = |
| { |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY}, |
| {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "beqc", |
| .br_range = BR_RANGE_S256, |
| .cond_field = |
| { |
| {0, 8, 0x7FF, true}, |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BEQC /* beqc $rt, imm11s, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S256] = |
| { |
| {0, 8, 0x7FF, false}, |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S256] = 4, |
| .relax_branch_isize[BR_RANGE_S256] = 4, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_WORD_9_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_MOVI_TA, /* movi $ta, imm11s */ |
| INSN_BEQ_TA /* beq $rt, $ta, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16K] = |
| { |
| {0, 0, 0xFFFFF, false}, |
| {4, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 8, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP7}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BNEC, /* bnec $rt, imm11s, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S64K] = |
| { |
| {0, 8, 0x7FF, false}, |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 8, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_WORD_9_PCREL}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BNEC, /* bnec $rt, imm11s, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16M] = |
| { |
| {0, 8, 0x7FF, false}, |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_WORD_9_PCREL}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BNEC, /* bnec $rt, imm11s, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_condition[BR_RANGE_U4G] = |
| { |
| {0, 8, 0x7FF, false}, |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_WORD_9_PCREL}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {12, 4, NDS32_INSN16, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = "bnec", |
| .br_range = BR_RANGE_S256, |
| .cond_field = |
| { |
| {0, 8, 0x7FF, true}, |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_seq[BR_RANGE_S256] = |
| { |
| INSN_BNEC /* bnec $rt, imm11s, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S256] = |
| { |
| {0, 8, 0x7FF, false}, |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S256] = 4, |
| .relax_branch_isize[BR_RANGE_S256] = 4, |
| .relax_fixup[BR_RANGE_S256] = |
| { |
| {0, 4, NDS32_PCREL, BFD_RELOC_NDS32_WORD_9_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16K] = |
| { |
| INSN_MOVI_TA, /* movi $ta, imm11s */ |
| INSN_BNE_TA /* bne $rt, $ta, label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16K] = |
| { |
| {0, 0, 0xFFFFF, false}, |
| {4, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S16K] = 8, |
| .relax_branch_isize[BR_RANGE_S16K] = 4, |
| .relax_fixup[BR_RANGE_S16K] = |
| { |
| {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16}, |
| {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP7}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_15_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S64K] = |
| { |
| INSN_BEQC, /* beqc $rt, imm11s, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S64K] = |
| { |
| {0, 8, 0x7FF, false}, |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S64K] = 8, |
| .relax_branch_isize[BR_RANGE_S64K] = 4, |
| .relax_fixup[BR_RANGE_S64K] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_WORD_9_PCREL}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_S16M] = |
| { |
| INSN_BEQC, /* beqc $rt, imm11s, $1 */ |
| INSN_J /* j label */ |
| }, |
| .relax_code_condition[BR_RANGE_S16M] = |
| { |
| {0, 8, 0x7FF, false}, |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_S16M] = 8, |
| .relax_branch_isize[BR_RANGE_S16M] = 4, |
| .relax_fixup[BR_RANGE_S16M] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_WORD_9_PCREL}, |
| {4, 4, NDS32_PCREL, BFD_RELOC_NDS32_25_PCREL}, |
| {0, 0, 0, 0} |
| }, |
| |
| .relax_code_seq[BR_RANGE_U4G] = |
| { |
| INSN_BEQC, /* beqc $rt, imm11s, $1 */ |
| INSN_SETHI_TA, /* sethi $ta, label */ |
| INSN_ORI_TA, /* ori $ta, $ta, label */ |
| INSN_JR_TA /* jr $ta */ |
| }, |
| .relax_code_condition[BR_RANGE_U4G] = |
| { |
| {0, 8, 0x7FF, false}, |
| {0, 20, 0x1F, false}, |
| {0, 0, 0, false} |
| }, |
| .relax_code_size[BR_RANGE_U4G] = 16, |
| .relax_branch_isize[BR_RANGE_U4G] = 4, |
| .relax_fixup[BR_RANGE_U4G] = |
| { |
| {0, 4, NDS32_CREATE_LABEL | NDS32_PCREL, BFD_RELOC_NDS32_WORD_9_PCREL}, |
| {4, 4, 0, BFD_RELOC_NDS32_HI20}, |
| {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, |
| {12, 4, NDS32_INSN16, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| }, |
| }, |
| { |
| .opcode = NULL, |
| }, |
| }; |
| |
| |
| /* GAS definitions for command-line options. */ |
| enum options |
| { |
| OPTION_BIG = OPTION_MD_BASE, |
| OPTION_LITTLE, |
| OPTION_TURBO, |
| OPTION_PIC, |
| OPTION_RELAX_FP_AS_GP_OFF, |
| OPTION_RELAX_B2BB_ON, |
| OPTION_RELAX_ALL_OFF, |
| OPTION_OPTIMIZE, |
| OPTION_OPTIMIZE_SPACE |
| }; |
| |
| const char md_shortopts[] = "m:O:"; |
| const struct option md_longopts[] = |
| { |
| {"O1", no_argument, NULL, OPTION_OPTIMIZE}, |
| {"Os", no_argument, NULL, OPTION_OPTIMIZE_SPACE}, |
| {"big", no_argument, NULL, OPTION_BIG}, |
| {"little", no_argument, NULL, OPTION_LITTLE}, |
| {"EB", no_argument, NULL, OPTION_BIG}, |
| {"EL", no_argument, NULL, OPTION_LITTLE}, |
| {"meb", no_argument, NULL, OPTION_BIG}, |
| {"mel", no_argument, NULL, OPTION_LITTLE}, |
| {"mall-ext", no_argument, NULL, OPTION_TURBO}, |
| {"mext-all", no_argument, NULL, OPTION_TURBO}, |
| {"mpic", no_argument, NULL, OPTION_PIC}, |
| /* Relaxation related options. */ |
| {"mno-fp-as-gp-relax", no_argument, NULL, OPTION_RELAX_FP_AS_GP_OFF}, |
| {"mb2bb", no_argument, NULL, OPTION_RELAX_B2BB_ON}, |
| {"mno-all-relax", no_argument, NULL, OPTION_RELAX_ALL_OFF}, |
| {NULL, no_argument, NULL, 0} |
| }; |
| |
| const size_t md_longopts_size = sizeof (md_longopts); |
| |
| struct nds32_parse_option_table |
| { |
| const char *name; /* Option string. */ |
| const char *help; /* Help description. */ |
| int (*func) (const char *arg); /* How to parse it. */ |
| }; |
| |
| |
| /* The value `-1' represents this option has *NOT* been set. */ |
| #ifdef NDS32_DEFAULT_ARCH_NAME |
| static const char* nds32_arch_name = NDS32_DEFAULT_ARCH_NAME; |
| #else |
| static const char* nds32_arch_name = "v3"; |
| #endif |
| static int nds32_baseline = -1; |
| static int nds32_gpr16 = -1; |
| static int nds32_fpu_sp_ext = -1; |
| static int nds32_fpu_dp_ext = -1; |
| static int nds32_freg = -1; |
| static int nds32_abi = -1; |
| |
| /* Record ELF flags */ |
| static int nds32_elf_flags = 0; |
| static int nds32_fpu_com = 0; |
| |
| static int nds32_parse_arch (const char *str); |
| static int nds32_parse_baseline (const char *str); |
| static int nds32_parse_freg (const char *str); |
| static int nds32_parse_abi (const char *str); |
| static void add_mapping_symbol (enum mstate state, |
| unsigned int padding_byte, |
| unsigned int align); |
| |
| static struct nds32_parse_option_table parse_opts [] = |
| { |
| {"arch=", N_("<arch name>\t Assemble for architecture <arch name>\n\ |
| <arch name> could be\n\ |
| v3, v3j, v3m, v3f, v3s, "\ |
| "v2, v2j, v2f, v2s"), nds32_parse_arch}, |
| {"baseline=", N_("<baseline>\t Assemble for baseline <baseline>\n\ |
| <baseline> could be v2, v3, v3m"), |
| nds32_parse_baseline}, |
| {"fpu-freg=", N_("<freg>\t Specify a FPU configuration\n\ |
| <freg>\n\ |
| 0: 8 SP / 4 DP registers\n\ |
| 1: 16 SP / 8 DP registers\n\ |
| 2: 32 SP / 16 DP registers\n\ |
| 3: 32 SP / 32 DP registers"), nds32_parse_freg}, |
| {"abi=", N_("<abi>\t Specify a abi version\n\ |
| <abi> could be v1, v2, v2fp, v2fpp"), nds32_parse_abi}, |
| {NULL, NULL, NULL} |
| }; |
| |
| static int nds32_mac = 1; |
| static int nds32_div = 1; |
| static int nds32_16bit_ext = 1; |
| static int nds32_dx_regs = NDS32_DEFAULT_DX_REGS; |
| static int nds32_perf_ext = NDS32_DEFAULT_PERF_EXT; |
| static int nds32_perf_ext2 = NDS32_DEFAULT_PERF_EXT2; |
| static int nds32_string_ext = NDS32_DEFAULT_STRING_EXT; |
| static int nds32_audio_ext = NDS32_DEFAULT_AUDIO_EXT; |
| static int nds32_dsp_ext = NDS32_DEFAULT_DSP_EXT; |
| static int nds32_zol_ext = NDS32_DEFAULT_ZOL_EXT; |
| static int nds32_fpu_fma = 0; |
| static int nds32_pic = 0; |
| static int nds32_relax_fp_as_gp = 1; |
| static int nds32_relax_b2bb = 0; |
| static int nds32_relax_all = 1; |
| struct nds32_set_option_table |
| { |
| const char *name; /* Option string. */ |
| const char *help; /* Help description. */ |
| int *var; /* Variable to be set. */ |
| int value; /* Value to set. */ |
| }; |
| |
| /* The option in this group has both Enable/Disable settings. |
| Just list on here. */ |
| |
| static struct nds32_set_option_table toggle_opts [] = |
| { |
| {"mac", N_("Multiply instructions support"), &nds32_mac, 1}, |
| {"div", N_("Divide instructions support"), &nds32_div, 1}, |
| {"16bit-ext", N_("16-bit extension"), &nds32_16bit_ext, 1}, |
| {"dx-regs", N_("d0/d1 registers"), &nds32_dx_regs, 1}, |
| {"perf-ext", N_("Performance extension"), &nds32_perf_ext, 1}, |
| {"perf2-ext", N_("Performance extension 2"), &nds32_perf_ext2, 1}, |
| {"string-ext", N_("String extension"), &nds32_string_ext, 1}, |
| {"reduced-regs", N_("Reduced Register configuration (GPR16) option"), &nds32_gpr16, 1}, |
| {"audio-isa-ext", N_("AUDIO ISA extension"), &nds32_audio_ext, 1}, |
| {"fpu-sp-ext", N_("FPU SP extension"), &nds32_fpu_sp_ext, 1}, |
| {"fpu-dp-ext", N_("FPU DP extension"), &nds32_fpu_dp_ext, 1}, |
| {"fpu-fma", N_("FPU fused-multiply-add instructions"), &nds32_fpu_fma, 1}, |
| {"dsp-ext", N_("DSP extension"), &nds32_dsp_ext, 1}, |
| {"zol-ext", N_("hardware loop extension"), &nds32_zol_ext, 1}, |
| {NULL, NULL, NULL, 0} |
| }; |
| |
| |
| /* GAS declarations. */ |
| |
| /* This is the callback for nds32-asm.c to parse operands. */ |
| int |
| nds32_asm_parse_operand (struct nds32_asm_desc *pdesc, |
| struct nds32_asm_insn *pinsn, |
| char **pstr, int64_t *value); |
| |
| |
| static struct nds32_asm_desc asm_desc; |
| |
| /* md_after_parse_args () |
| |
| GAS will call md_after_parse_args whenever it is defined. |
| This function checks any conflicting options specified. */ |
| |
| void |
| nds32_after_parse_args (void) |
| { |
| /* If -march option is not used in command-line, set the value of option |
| variable according to NDS32_DEFAULT_ARCH_NAME. */ |
| nds32_parse_arch (nds32_arch_name); |
| } |
| |
| /* This function is called when printing usage message (--help). */ |
| |
| void |
| md_show_usage (FILE *stream) |
| { |
| struct nds32_parse_option_table *coarse_tune; |
| struct nds32_set_option_table *fine_tune; |
| |
| fprintf (stream, _("\n NDS32-specific assembler options:\n")); |
| fprintf (stream, _("\ |
| -O1, Optimize for performance\n\ |
| -Os Optimize for space\n")); |
| fprintf (stream, _("\ |
| -EL, -mel or -little Produce little endian output\n\ |
| -EB, -meb or -big Produce big endian output\n\ |
| -mpic Generate PIC\n\ |
| -mno-fp-as-gp-relax Suppress fp-as-gp relaxation for this file\n\ |
| -mb2bb-relax Back-to-back branch optimization\n\ |
| -mno-all-relax Suppress all relaxation for this file\n")); |
| |
| for (coarse_tune = parse_opts; coarse_tune->name != NULL; coarse_tune++) |
| { |
| if (coarse_tune->help != NULL) |
| fprintf (stream, _(" -m%s%s\n"), |
| coarse_tune->name, _(coarse_tune->help)); |
| } |
| |
| for (fine_tune = toggle_opts; fine_tune->name != NULL; fine_tune++) |
| { |
| if (fine_tune->help != NULL) |
| fprintf (stream, _(" -m[no-]%-17sEnable/Disable %s\n"), |
| fine_tune->name, _(fine_tune->help)); |
| } |
| |
| fprintf (stream, _("\ |
| -mall-ext Turn on all extensions and instructions support\n")); |
| } |
| |
| void |
| nds32_frag_init (fragS *fragp) |
| { |
| fragp->tc_frag_data.flag = 0; |
| fragp->tc_frag_data.opcode = NULL; |
| fragp->tc_frag_data.fixup = NULL; |
| } |
| |
| |
| |
| /* This function reads an expression from a C string and returns a pointer past |
| the end of the expression. */ |
| |
| static char * |
| parse_expression (char *str, expressionS *exp) |
| { |
| char *s; |
| char *tmp; |
| |
| tmp = input_line_pointer; /* Save line pointer. */ |
| input_line_pointer = str; |
| expression (exp); |
| resolve_register (exp); |
| s = input_line_pointer; |
| input_line_pointer = tmp; /* Restore line pointer. */ |
| |
| return s; /* Return pointer to where parsing stopped. */ |
| } |
| |
| void |
| nds32_start_line_hook (void) |
| { |
| } |
| |
| /* |
| * Pseudo opcodes |
| */ |
| |
| typedef void (*nds32_pseudo_opcode_func) (int argc, char *argv[], unsigned int pv); |
| struct nds32_pseudo_opcode |
| { |
| const char *opcode; |
| int argc; |
| nds32_pseudo_opcode_func proc; |
| unsigned int pseudo_val; |
| |
| /* Some instructions are not pseudo opcode, but they might still be |
| expanded or changed with other instruction combination for some |
| conditions. We also apply this structure to assist such work. |
| |
| For example, if the distance of branch target '.L0' is larger than |
| imm8s<<1 range, |
| |
| the instruction: |
| |
| beqzs8 .L0 |
| |
| will be transformed into: |
| |
| bnezs8 .LCB0 |
| j .L0 |
| .LCB0: |
| |
| However, sometimes we do not want assembler to do such changes |
| because compiler knows how to generate corresponding instruction sequence. |
| Use this field to indicate that this opcode is also a physical instruction. |
| If the flag 'verbatim' is nozero and this opcode |
| is a physical instruction, we should not expand it. */ |
| int physical_op; |
| }; |
| #define PV_DONT_CARE 0 |
| |
| static htab_t nds32_pseudo_opcode_hash = NULL; |
| |
| static int |
| builtin_isreg (const char *s, const char *x ATTRIBUTE_UNUSED) |
| { |
| if (s [0] == '$' && str_hash_find (nds32_gprs_hash, (s + 1))) |
| return 1; |
| return 0; |
| } |
| |
| static int |
| builtin_regnum (const char *s, const char *x ATTRIBUTE_UNUSED) |
| { |
| struct nds32_keyword *k; |
| if (*s != '$') |
| return -1; |
| s++; |
| k = str_hash_find (nds32_gprs_hash, s); |
| |
| if (k == NULL) |
| return -1; |
| |
| return k->value; |
| } |
| |
| static int |
| builtin_addend (const char *s, char *x ATTRIBUTE_UNUSED) |
| { |
| const char *ptr = s; |
| |
| while (*ptr != '+' && *ptr != '-' && *ptr) |
| ++ptr; |
| |
| if (*ptr == 0) |
| return 0; |
| else |
| return strtol (ptr, NULL, 0); |
| } |
| |
| static void |
| md_assemblef (const char *format, ...) |
| { |
| /* FIXME: hope this is long enough. */ |
| char line[1024]; |
| va_list ap; |
| unsigned int r; |
| |
| va_start (ap, format); |
| r = vsnprintf (line, sizeof (line), format, ap); |
| md_assemble (line); |
| |
| gas_assert (r < sizeof (line)); |
| } |
| |
| /* Some prototypes here, since some op may use another op. */ |
| static void do_pseudo_li_internal (const char *rt, int imm32s); |
| static void do_pseudo_move_reg_internal (char *dst, char *src); |
| |
| static void |
| do_pseudo_b (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| char *arg_label = argv[0]; |
| relaxing = true; |
| /* b label */ |
| if (nds32_pic) |
| { |
| md_assemblef ("sethi $ta,hi20(%s)", arg_label); |
| md_assemblef ("ori $ta,$ta,lo12(%s)", arg_label); |
| md_assemble ((char *) "add $ta,$ta,$gp"); |
| md_assemble ((char *) "jr $ta"); |
| } |
| else |
| { |
| md_assemblef ("j %s", arg_label); |
| } |
| relaxing = false; |
| } |
| |
| static void |
| do_pseudo_bal (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| char *arg_label = argv[0]; |
| relaxing = true; |
| /* bal|call label */ |
| if (nds32_pic) |
| { |
| md_assemblef ("sethi $ta,hi20(%s)", arg_label); |
| md_assemblef ("ori $ta,$ta,lo12(%s)", arg_label); |
| md_assemble ((char *) "add $ta,$ta,$gp"); |
| md_assemble ((char *) "jral $ta"); |
| } |
| else |
| { |
| md_assemblef ("jal %s", arg_label); |
| } |
| relaxing = false; |
| } |
| |
| static void |
| do_pseudo_bge (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| /* rt5, ra5, label */ |
| md_assemblef ("slt $ta,%s,%s", argv[0], argv[1]); |
| md_assemblef ("beqz $ta,%s", argv[2]); |
| } |
| |
| static void |
| do_pseudo_bges (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| /* rt5, ra5, label */ |
| md_assemblef ("slts $ta,%s,%s", argv[0], argv[1]); |
| md_assemblef ("beqz $ta,%s", argv[2]); |
| } |
| |
| static void |
| do_pseudo_bgt (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| /* bgt rt5, ra5, label */ |
| md_assemblef ("slt $ta,%s,%s", argv[1], argv[0]); |
| md_assemblef ("bnez $ta,%s", argv[2]); |
| } |
| |
| static void |
| do_pseudo_bgts (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| /* bgt rt5, ra5, label */ |
| md_assemblef ("slts $ta,%s,%s", argv[1], argv[0]); |
| md_assemblef ("bnez $ta,%s", argv[2]); |
| } |
| |
| static void |
| do_pseudo_ble (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| /* bgt rt5, ra5, label */ |
| md_assemblef ("slt $ta,%s,%s", argv[1], argv[0]); |
| md_assemblef ("beqz $ta,%s", argv[2]); |
| } |
| |
| static void |
| do_pseudo_bles (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| /* bgt rt5, ra5, label */ |
| md_assemblef ("slts $ta,%s,%s", argv[1], argv[0]); |
| md_assemblef ("beqz $ta,%s", argv[2]); |
| } |
| |
| static void |
| do_pseudo_blt (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| /* rt5, ra5, label */ |
| md_assemblef ("slt $ta,%s,%s", argv[0], argv[1]); |
| md_assemblef ("bnez $ta,%s", argv[2]); |
| } |
| |
| static void |
| do_pseudo_blts (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| /* rt5, ra5, label */ |
| md_assemblef ("slts $ta,%s,%s", argv[0], argv[1]); |
| md_assemblef ("bnez $ta,%s", argv[2]); |
| } |
| |
| static void |
| do_pseudo_br (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| md_assemblef ("jr %s", argv[0]); |
| } |
| |
| static void |
| do_pseudo_bral (int argc, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| if (argc == 1) |
| md_assemblef ("jral $lp,%s", argv[0]); |
| else |
| md_assemblef ("jral %s,%s", argv[0], argv[1]); |
| } |
| |
| static void |
| do_pseudo_la_internal (const char *arg_reg, char *arg_label, |
| const char *line) |
| { |
| expressionS exp; |
| |
| parse_expression (arg_label, &exp); |
| if (exp.X_op != O_symbol) |
| { |
| as_bad (_("la must use with symbol. '%s'"), line); |
| return; |
| } |
| |
| relaxing = true; |
| /* rt, label */ |
| if (!nds32_pic && !strstr (arg_label, "@")) |
| { |
| md_assemblef ("sethi %s,hi20(%s)", arg_reg, arg_label); |
| md_assemblef ("ori %s,%s,lo12(%s)", arg_reg, arg_reg, arg_label); |
| } |
| else if (strstr (arg_label, "@TPOFF")) |
| { |
| /* la $rt, sym@TPOFF */ |
| md_assemblef ("sethi $ta,hi20(%s)", arg_label); |
| md_assemblef ("ori $ta,$ta,lo12(%s)", arg_label); |
| md_assemblef ("add %s,$ta,%s", arg_reg, TLS_REG); |
| } |
| else if (strstr(arg_label, "@GOTTPOFF")) |
| { |
| /* la $rt, sym@GOTTPOFF*/ |
| md_assemblef ("sethi $ta,hi20(%s)", arg_label); |
| md_assemblef ("lwi $ta,[$ta+lo12(%s)]", arg_label); |
| md_assemblef ("add %s,$ta,%s", arg_reg, TLS_REG); |
| } |
| else if (nds32_pic && ((strstr (arg_label, "@PLT") |
| || strstr (arg_label, "@GOTOFF")))) |
| { |
| md_assemblef ("sethi $ta,hi20(%s)", arg_label); |
| md_assemblef ("ori $ta,$ta,lo12(%s)", arg_label); |
| md_assemblef ("add %s,$ta,$gp", arg_reg); |
| } |
| else if (nds32_pic && strstr (arg_label, "@GOT")) |
| { |
| long addend = builtin_addend (arg_label, NULL); |
| |
| md_assemblef ("sethi $ta,hi20(%s)", arg_label); |
| md_assemblef ("ori $ta,$ta,lo12(%s)", arg_label); |
| md_assemblef ("lw %s,[$gp+$ta]", arg_reg); |
| if (addend != 0) |
| { |
| if (addend < 0x4000 && addend >= -0x4000) |
| { |
| md_assemblef ("addi %s,%s,%d", arg_reg, arg_reg, addend); |
| } |
| else |
| { |
| do_pseudo_li_internal ("$ta", addend); |
| md_assemblef ("add %s,$ta,%s", arg_reg, arg_reg); |
| } |
| } |
| } |
| else |
| as_bad (_("need PIC qualifier with symbol. '%s'"), line); |
| relaxing = false; |
| } |
| |
| static void |
| do_pseudo_la (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| do_pseudo_la_internal (argv[0], argv[1], argv[argc]); |
| } |
| |
| static void |
| do_pseudo_li_internal (const char *rt, int imm32s) |
| { |
| if (enable_16bit && imm32s <= 0xf && imm32s >= -0x10) |
| md_assemblef ("movi55 %s,%d", rt, imm32s); |
| else if (imm32s <= 0x7ffff && imm32s >= -0x80000) |
| md_assemblef ("movi %s,%d", rt, imm32s); |
| else if ((imm32s & 0xfff) == 0) |
| md_assemblef ("sethi %s,hi20(%d)", rt, imm32s); |
| else |
| { |
| md_assemblef ("sethi %s,hi20(%d)", rt, imm32s); |
| md_assemblef ("ori %s,%s,lo12(%d)", rt, rt, imm32s); |
| } |
| } |
| |
| static void |
| do_pseudo_li (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| /* Validate argv[1] for constant expression. */ |
| expressionS exp; |
| |
| parse_expression (argv[1], &exp); |
| if (exp.X_op != O_constant) |
| { |
| as_bad (_("Operand is not a constant. `%s'"), argv[argc]); |
| return; |
| } |
| |
| do_pseudo_li_internal (argv[0], exp.X_add_number); |
| } |
| |
| static void |
| do_pseudo_ls_bhw (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv) |
| { |
| char ls = 'r'; |
| char size = 'x'; |
| const char *sign = ""; |
| |
| /* Prepare arguments for various load/store. */ |
| sign = (pv & 0x10) ? "s" : ""; |
| ls = (pv & 0x80000000) ? 's' : 'l'; |
| switch (pv & 0x3) |
| { |
| case 0: size = 'b'; break; |
| case 1: size = 'h'; break; |
| case 2: size = 'w'; break; |
| } |
| |
| if (ls == 's' || size == 'w') |
| sign = ""; |
| |
| if (builtin_isreg (argv[1], NULL)) |
| { |
| /* lwi */ |
| md_assemblef ("%c%ci %s,[%s]", ls, size, argv[0], argv[1]); |
| } |
| else if (!nds32_pic) |
| { |
| relaxing = true; |
| if (strstr (argv[1], "@TPOFF")) |
| { |
| /* ls.w $rt, sym@TPOFF */ |
| md_assemblef ("sethi $ta,hi20(%s)", argv[1]); |
| md_assemblef ("ori $ta,$ta,lo12(%s)", argv[1]); |
| md_assemblef ("%c%c%s %s,[$ta+%s]", ls, size, sign, argv[0], TLS_REG); |
| } |
| else if (strstr (argv[1], "@GOTTPOFF")) |
| { |
| /* ls.w $rt, sym@GOTTPOFF */ |
| md_assemblef ("sethi $ta,hi20(%s)", argv[1]); |
| md_assemblef ("lwi $ta,[$ta+lo12(%s)]", argv[1]); |
| md_assemblef ("%c%c%s %s,[$ta+%s]", ls, size, sign, argv[0], TLS_REG); |
| } |
| else |
| { |
| /* lwi */ |
| md_assemblef ("sethi $ta,hi20(%s)", argv[1]); |
| md_assemblef ("%c%c%si %s,[$ta+lo12(%s)]", ls, size, sign, argv[0], argv[1]); |
| } |
| relaxing = false; |
| } |
| else |
| { |
| relaxing = true; |
| /* PIC code. */ |
| if (strstr (argv[1], "@GOTOFF")) |
| { |
| /* lw */ |
| md_assemblef ("sethi $ta,hi20(%s)", argv[1]); |
| md_assemblef ("ori $ta,$ta,lo12(%s)", argv[1]); |
| md_assemblef ("%c%c%s %s,[$ta+$gp]", ls, size, sign, argv[0]); |
| } |
| else if (strstr (argv[1], "@GOT")) |
| { |
| long addend = builtin_addend (argv[1], NULL); |
| /* lw */ |
| md_assemblef ("sethi $ta,hi20(%s)", argv[1]); |
| md_assemblef ("ori $ta,$ta,lo12(%s)", argv[1]); |
| md_assemble ((char *) "lw $ta,[$gp+$ta]"); /* Load address word. */ |
| if (addend < 0x10000 && addend >= -0x10000) |
| { |
| md_assemblef ("%c%c%si %s,[$ta+(%d)]", ls, size, sign, argv[0], addend); |
| } |
| else |
| { |
| /* lw */ |
| do_pseudo_li_internal (argv[0], addend); |
| md_assemblef ("%c%c%s %s,[$ta+%s]", ls, size, sign, argv[0], argv[0]); |
| } |
| } |
| else |
| { |
| as_bad (_("needs @GOT or @GOTOFF. %s"), argv[argc]); |
| } |
| relaxing = false; |
| } |
| } |
| |
| static void |
| do_pseudo_ls_bhwp (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv) |
| { |
| char *arg_rt = argv[0]; |
| char *arg_label = argv[1]; |
| char *arg_inc = argv[2]; |
| char ls = 'r'; |
| char size = 'x'; |
| const char *sign = ""; |
| |
| /* Prepare arguments for various load/store. */ |
| sign = (pv & 0x10) ? "s" : ""; |
| ls = (pv & 0x80000000) ? 's' : 'l'; |
| switch (pv & 0x3) |
| { |
| case 0: size = 'b'; break; |
| case 1: size = 'h'; break; |
| case 2: size = 'w'; break; |
| } |
| |
| if (ls == 's' || size == 'w') |
| sign = ""; |
| |
| do_pseudo_la_internal ("$ta", arg_label, argv[argc]); |
| md_assemblef ("%c%c%si.bi %s,[$ta],%s", ls, size, sign, arg_rt, arg_inc); |
| } |
| |
| static void |
| do_pseudo_ls_bhwpc (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv) |
| { |
| char *arg_rt = argv[0]; |
| char *arg_inc = argv[1]; |
| char ls = 'r'; |
| char size = 'x'; |
| const char *sign = ""; |
| |
| /* Prepare arguments for various load/store. */ |
| sign = (pv & 0x10) ? "s" : ""; |
| ls = (pv & 0x80000000) ? 's' : 'l'; |
| switch (pv & 0x3) |
| { |
| case 0: size = 'b'; break; |
| case 1: size = 'h'; break; |
| case 2: size = 'w'; break; |
| } |
| |
| if (ls == 's' || size == 'w') |
| sign = ""; |
| |
| md_assemblef ("%c%c%si.bi %s,[$ta],%s", ls, size, sign, arg_rt, arg_inc); |
| } |
| |
| static void |
| do_pseudo_ls_bhwi (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv) |
| { |
| char ls = 'r'; |
| char size = 'x'; |
| const char *sign = ""; |
| |
| /* Prepare arguments for various load/store. */ |
| sign = (pv & 0x10) ? "s" : ""; |
| ls = (pv & 0x80000000) ? 's' : 'l'; |
| switch (pv & 0x3) |
| { |
| case 0: size = 'b'; break; |
| case 1: size = 'h'; break; |
| case 2: size = 'w'; break; |
| } |
| |
| if (ls == 's' || size == 'w') |
| sign = ""; |
| |
| md_assemblef ("%c%c%si.bi %s,%s,%s", |
| ls, size, sign, argv[0], argv[1], argv[2]); |
| } |
| |
| static void |
| do_pseudo_move_reg_internal (char *dst, char *src) |
| { |
| if (enable_16bit) |
| md_assemblef ("mov55 %s,%s", dst, src); |
| else |
| md_assemblef ("ori %s,%s,0", dst, src); |
| } |
| |
| static void |
| do_pseudo_move (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| expressionS exp; |
| |
| if (builtin_isreg (argv[1], NULL)) |
| do_pseudo_move_reg_internal (argv[0], argv[1]); |
| else |
| { |
| parse_expression (argv[1], &exp); |
| if (exp.X_op == O_constant) |
| /* move $rt, imm -> li $rt, imm */ |
| do_pseudo_li_internal (argv[0], exp.X_add_number); |
| else |
| /* l.w $rt, var -> l.w $rt, var */ |
| do_pseudo_ls_bhw (argc, argv, 2); |
| } |
| } |
| |
| static void |
| do_pseudo_neg (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| /* Instead of "subri". */ |
| md_assemblef ("subri %s,%s,0", argv[0], argv[1]); |
| } |
| |
| static void |
| do_pseudo_not (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| md_assemblef ("nor %s,%s,%s", argv[0], argv[1], argv[1]); |
| } |
| |
| static void |
| do_pseudo_pushpopm (int argc, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| /* posh/pop $ra, $rb */ |
| /* SMW.{b | a}{i | d}{m?} Rb, [Ra], Re, Enable4 */ |
| int rb, re, ra, en4; |
| int i; |
| const char *opc = "pushpopm"; |
| |
| if (argc == 3) |
| as_bad ("'pushm/popm $ra5, $rb5, $label' is deprecated. " |
| "Only 'pushm/popm $ra5' is supported now. %s", argv[argc]); |
| else if (argc == 1) |
| as_bad ("'pushm/popm $ra5, $rb5'. %s\n", argv[argc]); |
| |
| if (strstr (argv[argc], "pop") == argv[argc]) |
| opc = "lmw.bim"; |
| else if (strstr (argv[argc], "push") == argv[argc]) |
| opc = "smw.adm"; |
| else |
| as_fatal ("nds32-as internal error. %s", argv[argc]); |
| |
| rb = builtin_regnum (argv[0], NULL); |
| re = builtin_regnum (argv[1], NULL); |
| |
| if (re < rb) |
| { |
| as_warn ("$rb should not be smaller than $ra. %s", argv[argc]); |
| /* Swap to right order. */ |
| ra = re; |
| re = rb; |
| rb = ra; |
| } |
| |
| /* Build enable4 mask. */ |
| en4 = 0; |
| if (re >= 28 || rb >= 28) |
| { |
| for (i = (rb >= 28? rb: 28); i <= re; i++) |
| en4 |= 1 << (3 - (i - 28)); |
| } |
| |
| /* Adjust $re, $rb. */ |
| if (rb >= 28) |
| rb = re = 31; |
| else if (nds32_gpr16 != 1 && re >= 28) |
| re = 27; |
| |
| /* Reduce register. */ |
| if (nds32_gpr16 && re > 10 && !(rb == 31 && re == 31)) |
| { |
| if (re >= 15 && strstr (opc, "smw") != NULL) |
| md_assemblef ("%s $r15,[$sp],$r15,%d", opc, en4); |
| if (rb <= 10) |
| md_assemblef ("%s $r%d,[$sp],$r10, 0x0", opc, rb); |
| if (re >= 15 && strstr (opc, "lmw") != NULL) |
| md_assemblef ("%s $r15,[$sp],$r15,%d", opc, en4); |
| } |
| else |
| md_assemblef ("%s $r%d,[$sp],$r%d,%d", opc, rb, re, en4); |
| } |
| |
| static void |
| do_pseudo_pushpop (int argc, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| /* push/pop $ra5, $label=$sp */ |
| char *argvm[3]; |
| |
| if (argc == 2) |
| as_bad ("'push/pop $ra5, rb5' is deprecated. " |
| "Only 'push/pop $ra5' is supported now. %s", argv[argc]); |
| |
| argvm[0] = argv[0]; |
| argvm[1] = argv[0]; |
| argvm[2] = argv[argc]; |
| do_pseudo_pushpopm (2, argvm, PV_DONT_CARE); |
| } |
| |
| static void |
| do_pseudo_v3push (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| md_assemblef ("push25 %s,%s", argv[0], argv[1]); |
| } |
| |
| static void |
| do_pseudo_v3pop (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| md_assemblef ("pop25 %s,%s", argv[0], argv[1]); |
| } |
| |
| /* pv == 0, parsing "push.s" pseudo instruction operands. |
| pv != 0, parsing "pop.s" pseudo instruction operands. */ |
| |
| static void |
| do_pseudo_pushpop_stack (int argc, char *argv[], |
| unsigned int pv) |
| { |
| /* push.s Rb,Re,{$fp $gp $lp $sp} ==> smw.adm Rb,[$sp],Re,Eable4 */ |
| /* pop.s Rb,Re,{$fp $gp $lp $sp} ==> lmw.bim Rb,[$sp],Re,Eable4 */ |
| |
| int rb, re; |
| int en4; |
| int last_arg_index; |
| const char *opc = (pv == 0) ? "smw.adm" : "lmw.bim"; |
| |
| rb = re = 0; |
| |
| if (argc == 1) |
| { |
| /* argc=1, operands pattern: { $fp $gp $lp $sp } */ |
| |
| /* Set register number Rb = Re = $sp = $r31. */ |
| rb = re = 31; |
| } |
| else if (argc == 2 || argc == 3) |
| { |
| /* argc=2, operands pattern: Rb, Re */ |
| /* argc=3, operands pattern: Rb, Re, { $fp $gp $lp $sp } */ |
| |
| /* Get register number in integer. */ |
| rb = builtin_regnum (argv[0], NULL); |
| re = builtin_regnum (argv[1], NULL); |
| |
| /* Rb should be equal/less than Re. */ |
| if (rb > re) |
| as_bad ("The first operand (%s) should be equal to or smaller than " |
| "second operand (%s).", argv[0], argv[1]); |
| |
| /* forbid using $fp|$gp|$lp|$sp in Rb or Re |
| r28 r29 r30 r31 */ |
| if (rb >= 28) |
| as_bad ("Cannot use $fp, $gp, $lp, or $sp at first operand !!"); |
| if (re >= 28) |
| as_bad ("Cannot use $fp, $gp, $lp, or $sp at second operand !!"); |
| } |
| else |
| { |
| as_bad ("Invalid operands pattern !!"); |
| } |
| |
| /* Build Enable4 mask. */ |
| /* Using last_arg_index for argc=1|2|3 is safe, because $fp, $gp, $lp, |
| and $sp only appear in argc=1 or argc=3 if argc=2, en4 remains 0, |
| which is also valid for code generation. */ |
| en4 = 0; |
| last_arg_index = argc - 1; |
| if (strstr (argv[last_arg_index], "$fp")) |
| en4 |= 8; |
| if (strstr (argv[last_arg_index], "$gp")) |
| en4 |= 4; |
| if (strstr (argv[last_arg_index], "$lp")) |
| en4 |= 2; |
| if (strstr (argv[last_arg_index], "$sp")) |
| en4 |= 1; |
| |
| md_assemblef ("%s $r%d,[$sp],$r%d,%d", opc, rb, re, en4); |
| } |
| |
| static void |
| do_pseudo_push_bhwd (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| char size = 'x'; |
| /* If users omit push location, use $sp as default value. */ |
| char location[8] = "$sp"; /* 8 is enough for register name. */ |
| |
| switch (pv & 0x3) |
| { |
| case 0: size = 'b'; break; |
| case 1: size = 'h'; break; |
| case 2: size = 'w'; break; |
| case 3: size = 'w'; break; |
| } |
| |
| if (argc == 2) |
| { |
| strncpy (location, argv[1], sizeof (location) - 1); |
| location[sizeof (location) - 1] = '\0'; |
| } |
| |
| md_assemblef ("l.%c $ta,%s", size, argv[0]); |
| md_assemblef ("smw.adm $ta,[%s],$ta", location); |
| |
| if ((pv & 0x3) == 0x3) /* double-word */ |
| { |
| md_assemblef ("l.w $ta,%s+4", argv[0]); |
| md_assemblef ("smw.adm $ta,[%s],$ta", location); |
| } |
| } |
| |
| static void |
| do_pseudo_pop_bhwd (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| char size = 'x'; |
| /* If users omit pop location, use $sp as default value. */ |
| char location[8] = "$sp"; /* 8 is enough for register name. */ |
| |
| switch (pv & 0x3) |
| { |
| case 0: size = 'b'; break; |
| case 1: size = 'h'; break; |
| case 2: size = 'w'; break; |
| case 3: size = 'w'; break; |
| } |
| |
| if (argc == 3) |
| { |
| strncpy (location, argv[2], sizeof (location) - 1); |
| location[sizeof (location) - 1] = '\0'; |
| } |
| |
| if ((pv & 0x3) == 0x3) /* double-word */ |
| { |
| md_assemblef ("lmw.bim %s,[%s],%s", argv[1], location, argv[1]); |
| md_assemblef ("s.w %s,%s+4", argv[1], argv[0]); |
| } |
| |
| md_assemblef ("lmw.bim %s,[%s],%s", argv[1], location, argv[1]); |
| md_assemblef ("s.%c %s,%s", size, argv[1], argv[0]); |
| } |
| |
| static void |
| do_pseudo_pusha (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| /* If users omit push location, use $sp as default value. */ |
| char location[8] = "$sp"; /* 8 is enough for register name. */ |
| |
| if (argc == 2) |
| { |
| strncpy (location, argv[1], sizeof (location) - 1); |
| location[sizeof (location) - 1] = '\0'; |
| } |
| |
| md_assemblef ("la $ta,%s", argv[0]); |
| md_assemblef ("smw.adm $ta,[%s],$ta", location); |
| } |
| |
| static void |
| do_pseudo_pushi (int argc ATTRIBUTE_UNUSED, char *argv[], |
| unsigned int pv ATTRIBUTE_UNUSED) |
| { |
| /* If users omit push location, use $sp as default value. */ |
| char location[8] = "$sp"; /* 8 is enough for register name. */ |
| |
| if (argc == 2) |
| { |
| strncpy (location, argv[1], sizeof (location) - 1); |
| location[sizeof (location) - 1] = '\0'; |
| } |
| |
| md_assemblef ("li $ta,%s", argv[0]); |
| md_assemblef ("smw.adm $ta,[%s],$ta", location); |
| } |
| |
| static struct nds32_pseudo_opcode nds32_pseudo_opcode_table[] = |
| { |
| {"b", 1, do_pseudo_b, 0, 0}, |
| {"bal", 1, do_pseudo_bal, 0, 0}, |
| |
| {"bge", 3, do_pseudo_bge, 0, 0}, |
| {"bges", 3, do_pseudo_bges, 0, 0}, |
| |
| {"bgt", 3, do_pseudo_bgt, 0, 0}, |
| {"bgts", 3, do_pseudo_bgts, 0, 0}, |
| |
| {"ble", 3, do_pseudo_ble, 0, 0}, |
| {"bles", 3, do_pseudo_bles, 0, 0}, |
| |
| {"blt", 3, do_pseudo_blt, 0, 0}, |
| {"blts", 3, do_pseudo_blts, 0, 0}, |
| |
| {"br", 1, do_pseudo_br, 0, 0}, |
| {"bral", 1, do_pseudo_bral, 0, 0}, |
| |
| {"call", 1, do_pseudo_bal, 0, 0}, |
| |
| {"la", 2, do_pseudo_la, 0, 0}, |
| {"li", 2, do_pseudo_li, 0, 0}, |
| |
| {"l.b", 2, do_pseudo_ls_bhw, 0, 0}, |
| {"l.h", 2, do_pseudo_ls_bhw, 1, 0}, |
| {"l.w", 2, do_pseudo_ls_bhw, 2, 0}, |
| {"l.bs", 2, do_pseudo_ls_bhw, 0 | 0x10, 0}, |
| {"l.hs", 2, do_pseudo_ls_bhw, 1 | 0x10, 0}, |
| {"s.b", 2, do_pseudo_ls_bhw, 0 | 0x80000000, 0}, |
| {"s.h", 2, do_pseudo_ls_bhw, 1 | 0x80000000, 0}, |
| {"s.w", 2, do_pseudo_ls_bhw, 2 | 0x80000000, 0}, |
| |
| {"l.bp", 3, do_pseudo_ls_bhwp, 0, 0}, |
| {"l.bpc", 3, do_pseudo_ls_bhwpc, 0, 0}, |
| {"l.hp", 3, do_pseudo_ls_bhwp, 1, 0}, |
| {"l.hpc", 3, do_pseudo_ls_bhwpc, 1, 0}, |
| {"l.wp", 3, do_pseudo_ls_bhwp, 2, 0}, |
| {"l.wpc", 3, do_pseudo_ls_bhwpc, 2, 0}, |
| {"l.bsp", 3, do_pseudo_ls_bhwp, 0 | 0x10, 0}, |
| {"l.bspc", 3, do_pseudo_ls_bhwpc, 0 | 0x10, 0}, |
| {"l.hsp", 3, do_pseudo_ls_bhwp, 1 | 0x10, 0}, |
| {"l.hspc", 3, do_pseudo_ls_bhwpc, 1 | 0x10, 0}, |
| {"s.bp", 3, do_pseudo_ls_bhwp, 0 | 0x80000000, 0}, |
| {"s.bpc", 3, do_pseudo_ls_bhwpc, 0 | 0x80000000, 0}, |
| {"s.hp", 3, do_pseudo_ls_bhwp, 1 | 0x80000000, 0}, |
| {"s.hpc", 3, do_pseudo_ls_bhwpc, 1 | 0x80000000, 0}, |
| {"s.wp", 3, do_pseudo_ls_bhwp, 2 | 0x80000000, 0}, |
| {"s.wpc", 3, do_pseudo_ls_bhwpc, 2 | 0x80000000, 0}, |
| {"s.bsp", 3, do_pseudo_ls_bhwp, 0 | 0x80000000 | 0x10, 0}, |
| {"s.hsp", 3, do_pseudo_ls_bhwp, 1 | 0x80000000 | 0x10, 0}, |
| |
| {"lbi.p", 3, do_pseudo_ls_bhwi, 0, 0}, |
| {"lhi.p", 3, do_pseudo_ls_bhwi, 1, 0}, |
| {"lwi.p", 3, do_pseudo_ls_bhwi, 2, 0}, |
| {"sbi.p", 3, do_pseudo_ls_bhwi, 0 | 0x80000000, 0}, |
| {"shi.p", 3, do_pseudo_ls_bhwi, 1 | 0x80000000, 0}, |
| {"swi.p", 3, do_pseudo_ls_bhwi, 2 | 0x80000000, 0}, |
| {"lbsi.p", 3, do_pseudo_ls_bhwi, 0 | 0x10, 0}, |
| {"lhsi.p", 3, do_pseudo_ls_bhwi, 1 | 0x10, 0}, |
| {"lwsi.p", 3, do_pseudo_ls_bhwi, 2 | 0x10, 0}, |
| |
| {"move", 2, do_pseudo_move, 0, 0}, |
| {"neg", 2, do_pseudo_neg, 0, 0}, |
| {"not", 2, do_pseudo_not, 0, 0}, |
| |
| {"pop", 2, do_pseudo_pushpop, 0, 0}, |
| {"push", 2, do_pseudo_pushpop, 0, 0}, |
| {"popm", 2, do_pseudo_pushpopm, 0, 0}, |
| {"pushm", 3, do_pseudo_pushpopm, 0, 0}, |
| |
| {"v3push", 2, do_pseudo_v3push, 0, 0}, |
| {"v3pop", 2, do_pseudo_v3pop, 0, 0}, |
| |
| /* Support pseudo instructions of pushing/poping registers into/from stack |
| push.s Rb, Re, { $fp $gp $lp $sp } ==> smw.adm Rb,[$sp],Re,Enable4 |
| pop.s Rb, Re, { $fp $gp $lp $sp } ==> lmw.bim Rb,[$sp],Re,Enable4 */ |
| { "push.s", 3, do_pseudo_pushpop_stack, 0, 0 }, |
| { "pop.s", 3, do_pseudo_pushpop_stack, 1, 0 }, |
| { "push.b", 2, do_pseudo_push_bhwd, 0, 0 }, |
| { "push.h", 2, do_pseudo_push_bhwd, 1, 0 }, |
| { "push.w", 2, do_pseudo_push_bhwd, 2, 0 }, |
| { "push.d", 2, do_pseudo_push_bhwd, 3, 0 }, |
| { "pop.b", 3, do_pseudo_pop_bhwd, 0, 0 }, |
| { "pop.h", 3, do_pseudo_pop_bhwd, 1, 0 }, |
| { "pop.w", 3, do_pseudo_pop_bhwd, 2, 0 }, |
| { "pop.d", 3, do_pseudo_pop_bhwd, 3, 0 }, |
| { "pusha", 2, do_pseudo_pusha, 0, 0 }, |
| { "pushi", 2, do_pseudo_pushi, 0, 0 }, |
| |
| {NULL, 0, NULL, 0, 0} |
| }; |
| |
| static void |
| nds32_init_nds32_pseudo_opcodes (void) |
| { |
| struct nds32_pseudo_opcode *opcode; |
| |
| nds32_pseudo_opcode_hash = str_htab_create (); |
| for (opcode = nds32_pseudo_opcode_table; opcode->opcode; opcode++) |
| if (str_hash_insert (nds32_pseudo_opcode_hash, opcode->opcode, opcode, 0)) |
| as_fatal (_("duplicate %s"), opcode->opcode); |
| } |
| |
| static struct nds32_pseudo_opcode * |
| nds32_lookup_pseudo_opcode (const char *str) |
| { |
| struct nds32_pseudo_opcode *result; |
| int i = 0; |
| |
| /* (*op) is the first word of current source line (*str) */ |
| int maxlen = strlen (str); |
| char *op = xmalloc (maxlen + 1); |
| |
| for (i = 0; i < maxlen; i++) |
| { |
| if (is_whitespace (op[i] = str[i])) |
| break; |
| } |
| op[i] = '\0'; |
| |
| result = str_hash_find (nds32_pseudo_opcode_hash, op); |
| free (op); |
| return result; |
| } |
| |
| static void |
| nds32_pseudo_opcode_wrapper (char *line, struct nds32_pseudo_opcode *opcode) |
| { |
| int argc = 0; |
| char *argv[8] = {NULL}; |
| char *s; |
| char *str = xstrdup (line); |
| |
| /* Parse arguments for opcode. */ |
| s = str + strlen (opcode->opcode); |
| |
| if (!s[0]) |
| goto end; |
| |
| /* Dummy comma to ease separate arguments as below. */ |
| s[0] = ','; |
| do |
| { |
| if (s[0] == ',') |
| { |
| if (argc >= opcode->argc |
| || (argc >= (int)ARRAY_SIZE (argv) - 1)) |
| as_bad (_("Too many argument. `%s'"), line); |
| |
| argv[argc] = s + 1; |
| argc ++; |
| s[0] = '\0'; |
| } |
| ++s; |
| } while (s[0] != '\0'); |
| end: |
| /* Put the origin line for debugging. */ |
| argv[argc] = line; |
| opcode->proc (argc, argv, opcode->pseudo_val); |
| free (str); |
| } |
| |
| /* This function will be invoked from function `nds32_after_parse_args'. |
| Thus, if the value of option has been set, keep the value the way it is. */ |
| |
| static int |
| nds32_parse_arch (const char *str) |
| { |
| static const struct nds32_arch |
| { |
| const char *name; |
| int baseline; |
| int reduced_reg; |
| int fpu_sp_ext; |
| int fpu_dp_ext; |
| int fpu_freg; |
| int abi; |
| } archs[] = |
| { |
| {"v3m", ISA_V3M, 1, 0, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_AABI}, |
| {"v3j", ISA_V3, 1, 0, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_AABI}, |
| {"v3s", ISA_V3, 0, 1, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_V2FP_PLUS}, |
| {"v3f", ISA_V3, 0, 1, 1, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_V2FP_PLUS}, |
| {"v3", ISA_V3, 0, 0, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_AABI}, |
| {"v2j", ISA_V2, 1, 0, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_AABI}, |
| {"v2s", ISA_V2, 0, 1, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_V2FP_PLUS}, |
| {"v2f", ISA_V2, 0, 1, 1, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_V2FP_PLUS}, |
| {"v2", ISA_V2, 0, 0, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_AABI}, |
| }; |
| size_t i; |
| |
| for (i = 0; i < ARRAY_SIZE (archs); i++) |
| { |
| if (strcmp (str, archs[i].name) != 0) |
| continue; |
| |
| /* The value `-1' represents this option has *NOT* been set. */ |
| nds32_baseline = (-1 != nds32_baseline) ? nds32_baseline : archs[i].baseline; |
| nds32_gpr16 = (-1 != nds32_gpr16) ? nds32_gpr16 : archs[i].reduced_reg; |
| nds32_fpu_sp_ext = (-1 != nds32_fpu_sp_ext) ? nds32_fpu_sp_ext : archs[i].fpu_sp_ext; |
| nds32_fpu_dp_ext = (-1 != nds32_fpu_dp_ext) ? nds32_fpu_dp_ext : archs[i].fpu_dp_ext; |
| nds32_freg = (-1 != nds32_freg) ? nds32_freg : archs[i].fpu_freg; |
| nds32_abi = (-1 != nds32_abi) ? nds32_abi : archs[i].abi; |
| |
| return 1; |
| } |
| |
| /* Logic here rejects the input arch name. */ |
| as_bad (_("unknown arch name `%s'\n"), str); |
| |
| return 1; |
| } |
| |
| /* This function parses "baseline" specified. */ |
| |
| static int |
| nds32_parse_baseline (const char *str) |
| { |
| if (strcmp (str, "v3") == 0) |
| nds32_baseline = ISA_V3; |
| else if (strcmp (str, "v3m") == 0) |
| nds32_baseline = ISA_V3M; |
| else if (strcmp (str, "v2") == 0) |
| nds32_baseline = ISA_V2; |
| else |
| { |
| /* Logic here rejects the input baseline. */ |
| as_bad (_("unknown baseline `%s'\n"), str); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| /* This function parses "fpu-freg" specified. */ |
| |
| static int |
| nds32_parse_freg (const char *str) |
| { |
| if (strcmp (str, "2") == 0) |
| nds32_freg = E_NDS32_FPU_REG_32SP_16DP; |
| else if (strcmp (str, "3") == 0) |
| nds32_freg = E_NDS32_FPU_REG_32SP_32DP; |
| else if (strcmp (str, "1") == 0) |
| nds32_freg = E_NDS32_FPU_REG_16SP_8DP; |
| else if (strcmp (str, "0") == 0) |
| nds32_freg = E_NDS32_FPU_REG_8SP_4DP; |
| else |
| { |
| /* Logic here rejects the input FPU configuration. */ |
| as_bad (_("unknown FPU configuration `%s'\n"), str); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| /* This function parse "abi=" specified. */ |
| |
| static int |
| nds32_parse_abi (const char *str) |
| { |
| if (strcmp (str, "v2") == 0) |
| nds32_abi = E_NDS_ABI_AABI; |
| /* Obsolete. */ |
| else if (strcmp (str, "v2fp") == 0) |
| nds32_abi = E_NDS_ABI_V2FP; |
| else if (strcmp (str, "v1") == 0) |
| nds32_abi = E_NDS_ABI_V1; |
| else if (strcmp (str,"v2fpp") == 0) |
| nds32_abi = E_NDS_ABI_V2FP_PLUS; |
| else |
| { |
| /* Logic here rejects the input abi version. */ |
| as_bad (_("unknown ABI version`%s'\n"), str); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| /* This function turn on all extensions and instructions support. */ |
| |
| static int |
| nds32_all_ext (void) |
| { |
| nds32_mac = 1; |
| nds32_div = 1; |
| nds32_dx_regs = 1; |
| nds32_16bit_ext = 1; |
| nds32_perf_ext = 1; |
| nds32_perf_ext2 = 1; |
| nds32_string_ext = 1; |
| nds32_audio_ext = 1; |
| nds32_fpu_fma = 1; |
| nds32_fpu_sp_ext = 1; |
| nds32_fpu_dp_ext = 1; |
| nds32_dsp_ext = 1; |
| nds32_zol_ext = 1; |
| /* Turn off reduced register. */ |
| nds32_gpr16 = 0; |
| |
| return 1; |
| } |
| |
| /* GAS will call md_parse_option whenever getopt returns an unrecognized code, |
| presumably indicating a special code value which appears in md_longopts. |
| This function should return non-zero if it handled the option and zero |
| otherwise. There is no need to print a message about an option not being |
| recognized. This will be handled by the generic code. */ |
| |
| int |
| nds32_parse_option (int c, const char *arg) |
| { |
| struct nds32_parse_option_table *coarse_tune; |
| struct nds32_set_option_table *fine_tune; |
| const char *ptr_arg = NULL; |
| |
| switch (c) |
| { |
| case OPTION_OPTIMIZE: |
| optimize = 1; |
| optimize_for_space = 0; |
| break; |
| case OPTION_OPTIMIZE_SPACE: |
| optimize = 0; |
| optimize_for_space = 1; |
| break; |
| case OPTION_BIG: |
| target_big_endian = 1; |
| break; |
| case OPTION_LITTLE: |
| target_big_endian = 0; |
| break; |
| case OPTION_TURBO: |
| nds32_all_ext (); |
| break; |
| case OPTION_PIC: |
| nds32_pic = 1; |
| break; |
| case OPTION_RELAX_FP_AS_GP_OFF: |
| nds32_relax_fp_as_gp = 0; |
| break; |
| case OPTION_RELAX_B2BB_ON: |
| nds32_relax_b2bb = 1; |
| break; |
| case OPTION_RELAX_ALL_OFF: |
| nds32_relax_all = 0; |
| break; |
| default: |
| /* Determination of which option table to search for to save time. */ |
| if (!arg) |
| return 0; |
| |
| ptr_arg = strchr (arg, '='); |
| |
| if (ptr_arg) |
| { |
| /* Find the value after '='. */ |
| if (ptr_arg != NULL) |
| ptr_arg++; |
| for (coarse_tune = parse_opts; coarse_tune->name != NULL; coarse_tune++) |
| { |
| if (strncmp (arg, coarse_tune->name, (ptr_arg - arg)) == 0) |
| { |
| coarse_tune->func (ptr_arg); |
| return 1; |
| } |
| } |
| } |
| else |
| { |
| int disable = 0; |
| |
| /* Filter out the Disable option first. */ |
| if (startswith (arg, "no-")) |
| { |
| disable = 1; |
| arg += 3; |
| } |
| |
| for (fine_tune = toggle_opts; fine_tune->name != NULL; fine_tune++) |
| { |
| if (strcmp (arg, fine_tune->name) == 0) |
| { |
| if (fine_tune->var != NULL) |
| *fine_tune->var = (disable) ? 0 : 1; |
| return 1; |
| } |
| } |
| } |
| /* Nothing match. */ |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| /* tc_check_label */ |
| |
| void |
| nds32_check_label (symbolS *label ATTRIBUTE_UNUSED) |
| { |
| /* The code used to create BB is move to frob_label. |
| They should go there. */ |
| } |
| |
| static void |
| set_endian_little (int on) |
| { |
| target_big_endian = !on; |
| } |
| |
| /* These functions toggles the generation of 16-bit. First encounter signals |
| the beginning of not generating 16-bit instructions and next encounter |
| signals the restoring back to default behavior. */ |
| |
| static void |
| trigger_16bit (int trigger) |
| { |
| enable_16bit = trigger; |
| } |
| |
| static int backup_16bit_mode; |
| static void |
| restore_16bit (int no_use ATTRIBUTE_UNUSED) |
| { |
| enable_16bit = backup_16bit_mode; |
| } |
| |
| static void |
| off_16bit (int no_use ATTRIBUTE_UNUSED) |
| { |
| backup_16bit_mode = enable_16bit; |
| enable_16bit = 0; |
| } |
| |
| /* Built-in segments for small object. */ |
| typedef struct nds32_seg_entryT |
| { |
| segT s; |
| const char *name; |
| flagword flags; |
| } nds32_seg_entry; |
| |
| nds32_seg_entry nds32_seg_table[] = |
| { |
| {NULL, ".sdata_f", SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA |
| | SEC_HAS_CONTENTS | SEC_SMALL_DATA}, |
| {NULL, ".sdata_b", SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA |
| | SEC_HAS_CONTENTS | SEC_SMALL_DATA}, |
| {NULL, ".sdata_h", SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA |
| | SEC_HAS_CONTENTS | SEC_SMALL_DATA}, |
| {NULL, ".sdata_w", SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA |
| | SEC_HAS_CONTENTS | SEC_SMALL_DATA}, |
| {NULL, ".sdata_d", SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA |
| | SEC_HAS_CONTENTS | SEC_SMALL_DATA}, |
| {NULL, ".sbss_f", SEC_ALLOC | SEC_SMALL_DATA}, |
| {NULL, ".sbss_b", SEC_ALLOC | SEC_SMALL_DATA}, |
| {NULL, ".sbss_h", SEC_ALLOC | SEC_SMALL_DATA}, |
| {NULL, ".sbss_w", SEC_ALLOC | SEC_SMALL_DATA}, |
| {NULL, ".sbss_d", SEC_ALLOC | SEC_SMALL_DATA} |
| }; |
| |
| /* Indexes to nds32_seg_table[]. */ |
| enum NDS32_SECTIONS_ENUM |
| { |
| SDATA_F_SECTION = 0, |
| SDATA_B_SECTION = 1, |
| SDATA_H_SECTION = 2, |
| SDATA_W_SECTION = 3, |
| SDATA_D_SECTION = 4, |
| SBSS_F_SECTION = 5, |
| SBSS_B_SECTION = 6, |
| SBSS_H_SECTION = 7, |
| SBSS_W_SECTION = 8, |
| SBSS_D_SECTION = 9 |
| }; |
| |
| /* The following code is borrowed from v850_seg. Revise this is needed. */ |
| |
| static void |
| do_nds32_seg (int i, subsegT sub) |
| { |
| nds32_seg_entry *seg = nds32_seg_table + i; |
| |
| obj_elf_section_change_hook (); |
| |
| if (seg->s != NULL) |
| subseg_set (seg->s, sub); |
| else |
| { |
| seg->s = subseg_new (seg->name, sub); |
| if (OUTPUT_FLAVOR == bfd_target_elf_flavour) |
| { |
| bfd_set_section_flags (seg->s, seg->flags); |
| if ((seg->flags & SEC_LOAD) == 0) |
| seg_info (seg->s)->bss = 1; |
| } |
| } |
| } |
| |
| static void |
| nds32_seg (int i) |
| { |
| subsegT sub = get_absolute_expression (); |
| |
| do_nds32_seg (i, sub); |
| demand_empty_rest_of_line (); |
| } |
| |
| /* Set if label adjustment is needed. I should not adjust .xbyte in dwarf. */ |
| static symbolS *nds32_last_label; /* Last label for alignment. */ |
| |
| static void |
| add_mapping_symbol_for_align (int shift, valueT addr, int is_data_align) |
| { |
| if ((shift > 1) && (addr & 1)) |
| { |
| int n = (1 << shift) - 1; |
| if (!is_data_align) |
| add_mapping_symbol (MAP_CODE, 1, 0); |
| else if ((int) (addr & n) != n) |
| add_mapping_symbol (MAP_CODE, 1, 0); |
| } |
| else if ((shift > 1) && ((int) (addr & 1) == 0)) |
| add_mapping_symbol (MAP_CODE, 0, 0); |
| } |
| |
| /* This code is referred from D30V for adjust label to be with pending |
| alignment. For example, |
| LBYTE: .byte 0x12 |
| LHALF: .half 0x12 |
| LWORD: .word 0x12 |
| Without this, the above label will not attach to incoming data. */ |
| |
| static void |
| nds32_adjust_label (int n) |
| { |
| /* FIXME: I think adjust label and alignment is |
| the programmer's obligation. Sadly, VLSI team doesn't |
| properly use .align for their test cases. |
| So I re-implement cons_align and auto adjust labels, again. |
| |
| I think d30v's implementation is simple and good enough. */ |
| |
| symbolS *label = nds32_last_label; |
| nds32_last_label = NULL; |
| |
| /* SEC_ALLOC is used to eliminate .debug_ sections. |
| SEC_CODE is used to include section for ILM. */ |
| if (((now_seg->flags & SEC_ALLOC) == 0 && (now_seg->flags & SEC_CODE) == 0) |
| || strcmp (now_seg->name, ".eh_frame") == 0 |
| || strcmp (now_seg->name, ".gcc_except_table") == 0) |
| return; |
| |
| /* Only frag by alignment when needed. |
| Otherwise, it will fail to optimize labels on 4-byte boundary. (bug8454) |
| See md_convert_frag () and RELAX_SET_RELAXABLE (frag) for details. */ |
| if (frag_now_fix () & ((1 << n) -1 )) |
| { |
| if (subseg_text_p (now_seg)) |
| { |
| add_mapping_symbol_for_align (n, frag_now_fix (), 1); |
| frag_align_code (n, 0); |
| } |
| else |
| frag_align (n, 0, 0); |
| |
| /* Record the minimum alignment for this segment. */ |
| record_alignment (now_seg, n - OCTETS_PER_BYTE_POWER); |
| } |
| |
| if (label != NULL) |
| { |
| symbolS *sym; |
| int label_seen = false; |
| struct frag *old_frag; |
| valueT old_value, new_value; |
| |
| gas_assert (S_GET_SEGMENT (label) == now_seg); |
| |
| old_frag = symbol_get_frag (label); |
| old_value = S_GET_VALUE (label); |
| new_value = (valueT) frag_now_fix (); |
| |
| /* Multiple labels may be on the same address. And the last symbol |
| may not be a label at all, e.g., register name, external function names, |
| so I have to track the last label in tc_frob_label instead of |
| just using symbol_lastP. */ |
| for (sym = symbol_lastP; sym != NULL; sym = symbol_previous (sym)) |
| { |
| if (symbol_get_frag (sym) == old_frag |
| && S_GET_VALUE (sym) == old_value) |
| { |
| /* Warning HERE! */ |
| label_seen = true; |
| symbol_set_frag (sym, frag_now); |
| S_SET_VALUE (sym, new_value); |
| } |
| else if (label_seen && symbol_get_frag (sym) != old_frag) |
| break; |
| } |
| } |
| } |
| |
| void |
| nds32_cons_align (int size ATTRIBUTE_UNUSED) |
| { |
| /* Do nothing here. |
| This is called before `md_flush_pending_output' is called by `cons'. |
| |
| There are two things should be done for auto-adjust-label. |
| 1. Align data/instructions and adjust label to be attached to them. |
| 2. Clear auto-adjust state, so incoming data/instructions will not |
| adjust the label. |
| |
| For example, |
| .byte 0x1 |
| .L0: |
| .word 0x2 |
| .word 0x3 |
| in this case, '.word 0x2' will adjust the label, .L0, but '.word 0x3' should not. |
| |
| I think `md_flush_pending_output' is a good place to clear the auto-adjust state, |
| but it is also called by `cons' before this function. |
| To simplify the code, instead of overriding .zero, .fill, .space, etc, |
| I think we should just adjust label in `nds32_aligned_X_cons' instead of here. */ |
| } |
| |
| static void |
| make_mapping_symbol (enum mstate state, valueT value, fragS * frag, unsigned int align) |
| { |
| symbolS *symbol_p = NULL; |
| const char *symbol_name = NULL; |
| switch (state) |
| { |
| case MAP_DATA: |
| if (align == 0) |
| symbol_name = "$d0"; |
| else if (align == 1) |
| symbol_name = "$d1"; |
| else if (align == 2) |
| symbol_name = "$d2"; |
| else if (align == 3) |
| symbol_name = "$d3"; |
| else if (align == 4) |
| symbol_name = "$d4"; |
| break; |
| case MAP_CODE: |
| symbol_name = "$c"; |
| break; |
| default: |
| abort (); |
| } |
| |
| symbol_p = symbol_new (symbol_name, now_seg, frag, value); |
| /* local scope attribute */ |
| symbol_get_bfdsym (symbol_p)->flags |= BSF_NO_FLAGS | BSF_LOCAL; |
| } |
| |
| static void |
| add_mapping_symbol (enum mstate state, unsigned int padding_byte, |
| unsigned int align) |
| { |
| enum mstate current_mapping_state = |
| seg_info (now_seg)->tc_segment_info_data.mapstate; |
| |
| if (state == MAP_CODE |
| && current_mapping_state == state) |
| return; |
| |
| if (!SEG_NORMAL (now_seg) |
| || !subseg_text_p (now_seg)) |
| return; |
| |
| /* start adding mapping symbol */ |
| seg_info (now_seg)->tc_segment_info_data.mapstate = state; |
| make_mapping_symbol (state, (valueT) frag_now_fix () + padding_byte, |
| frag_now, align); |
| } |
| |
| static void |
| nds32_aligned_cons (int idx) |
| { |
| nds32_adjust_label (idx); |
| add_mapping_symbol (MAP_DATA, 0, idx); |
| /* Call default handler. */ |
| cons (1 << idx); |
| if (now_seg->flags & SEC_CODE |
| && now_seg->flags & SEC_ALLOC && now_seg->flags & SEC_RELOC) |
| { |
| /* Use BFD_RELOC_NDS32_DATA to avoid linker |
| optimization replacing data. */ |
| expressionS exp; |
| |
| exp.X_add_number = 0; |
| exp.X_op = O_constant; |
| fix_new_exp (frag_now, frag_now_fix () - (1 << idx), 1 << idx, |
| &exp, 0, BFD_RELOC_NDS32_DATA); |
| } |
| } |
| |
| /* `.double' directive. */ |
| |
| static void |
| nds32_aligned_float_cons (int type) |
| { |
| switch (type) |
| { |
| case 'f': |
| case 'F': |
| case 's': |
| case 'S': |
| nds32_adjust_label (2); |
| break; |
| case 'd': |
| case 'D': |
| case 'r': |
| case 'R': |
| nds32_adjust_label (4); |
| break; |
| default: |
| as_bad ("Unrecognized float type, %c\n", (char)type); |
| } |
| /* Call default handler. */ |
| float_cons (type); |
| } |
| |
| static void |
| nds32_enable_pic (int ignore ATTRIBUTE_UNUSED) |
| { |
| /* Another way to do -mpic. |
| This is for GCC internal use and should always be first line |
| of code, otherwise, the effect is not determined. */ |
| nds32_pic = 1; |
| } |
| |
| static void |
| nds32_set_abi (int ver) |
| { |
| nds32_abi = ver; |
| } |
| |
| /* Relax directive to set relocation R_NDS32_RELAX_ENTRY value. */ |
| |
| static void |
| nds32_relax_relocs (int relax) |
| { |
| char saved_char; |
| char *name; |
| int i; |
| const char *subtype_relax[] = |
| {"", "",}; |
| |
| name = input_line_pointer; |
| while (*input_line_pointer && !is_whitespace (*input_line_pointer)) |
| input_line_pointer++; |
| saved_char = *input_line_pointer; |
| *input_line_pointer = 0; |
| |
| for (i = 0; i < (int) ARRAY_SIZE (subtype_relax); i++) |
| { |
| if (strcmp (name, subtype_relax[i]) == 0) |
| { |
| switch (i) |
| { |
| case 0: |
| case 1: |
| enable_relax_relocs = relax & enable_relax_relocs; |
| break; |
| default: |
| break; |
| } |
| break; |
| } |
| } |
| *input_line_pointer = saved_char; |
| ignore_rest_of_line (); |
| } |
| |
| /* Record which arguments register($r0 ~ $r5) is not used in callee. |
| bit[i] for $ri */ |
| |
| static void |
| nds32_set_hint_func_args (int ignore ATTRIBUTE_UNUSED) |
| { |
| ignore_rest_of_line (); |
| } |
| |
| /* Insert relocations to mark the begin and end of a fp-omitted function, |
| for further relaxation use. |
| bit[i] for $ri */ |
| |
| static void |
| nds32_omit_fp_begin (int mode) |
| { |
| expressionS exp; |
| |
| if (nds32_relax_fp_as_gp == 0) |
| return; |
| exp.X_op = O_symbol; |
| exp.X_add_symbol = abs_section_sym; |
| if (mode == 1) |
| { |
| in_omit_fp = 1; |
| exp.X_add_number = R_NDS32_RELAX_REGION_OMIT_FP_FLAG; |
| fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0, |
| BFD_RELOC_NDS32_RELAX_REGION_BEGIN); |
| } |
| else |
| { |
| in_omit_fp = 0; |
| exp.X_add_number = R_NDS32_RELAX_REGION_OMIT_FP_FLAG; |
| fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0, |
| BFD_RELOC_NDS32_RELAX_REGION_END); |
| } |
| } |
| |
| static void |
| nds32_loop_begin (int mode) |
| { |
| /* Insert loop region relocation here. */ |
| expressionS exp; |
| |
| exp.X_op = O_symbol; |
| exp.X_add_symbol = abs_section_sym; |
| if (mode == 1) |
| { |
| exp.X_add_number = R_NDS32_RELAX_REGION_INNERMOST_LOOP_FLAG; |
| fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0, |
| BFD_RELOC_NDS32_RELAX_REGION_BEGIN); |
| } |
| else |
| { |
| exp.X_add_number = R_NDS32_RELAX_REGION_INNERMOST_LOOP_FLAG; |
| fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0, |
| BFD_RELOC_NDS32_RELAX_REGION_END); |
| } |
| } |
| |
| struct nds32_relocs_group |
| { |
| struct nds32_relocs_pattern *pattern; |
| struct nds32_relocs_group *next; |
| }; |
| |
| static struct nds32_relocs_group *nds32_relax_hint_current = NULL; |
| /* Used to reorder the id for ".relax_hint id". */ |
| static int relax_hint_bias = 0; |
| /* Record current relax hint id. */ |
| static int relax_hint_id_current = -1; |
| int reset_bias = 0; |
| /* If ".relax_hint begin" is triggered? */ |
| int relax_hint_begin = 0; |
| |
| /* Record the reordered relax hint id. */ |
| |
| struct relax_hint_id |
| { |
| int old_id; |
| int new_id; |
| struct relax_hint_id *next; |
| }; |
| |
| /* FIXME: Need to find somewhere to free the list. */ |
| struct relax_hint_id *record_id_head = NULL; |
| |
| /* Is the buffer large enough? */ |
| #define MAX_BUFFER 12 |
| |
| static char *nds_itoa (int n); |
| |
| static char * |
| nds_itoa (int n) |
| { |
| char *buf = xmalloc (MAX_BUFFER * sizeof (char)); |
| snprintf (buf, MAX_BUFFER, "%d", n); |
| return buf; |
| } |
| |
| /* Insert a relax hint. */ |
| |
| static void |
| nds32_relax_hint (int mode ATTRIBUTE_UNUSED) |
| { |
| char *name = NULL; |
| char saved_char; |
| struct nds32_relocs_pattern *relocs = NULL; |
| struct nds32_relocs_group *group, *new; |
| struct relax_hint_id *record_id; |
| |
| name = input_line_pointer; |
| while (*input_line_pointer && !is_whitespace (*input_line_pointer)) |
| input_line_pointer++; |
| saved_char = *input_line_pointer; |
| *input_line_pointer = 0; |
| name = strdup (name); |
| |
| if (name && strcmp (name, "begin") == 0) |
| { |
| if (relax_hint_id_current == -1) |
| reset_bias = 1; |
| relax_hint_bias++; |
| relax_hint_id_current++; |
| relax_hint_begin = 1; |
| } |
| |
| /* Original case ".relax_hint id". It's id may need to be reordered. */ |
| if (!relax_hint_begin) |
| { |
| int tmp = strtol (name, NULL, 10); |
| record_id = record_id_head; |
| while (record_id) |
| { |
| if (record_id->old_id == tmp) |
| { |
| name = nds_itoa (record_id->new_id); |
| goto reordered_id; |
| } |
| record_id = record_id->next; |
| } |
| if (reset_bias) |
| { |
| relax_hint_bias = relax_hint_id_current - atoi (name) + 1; |
| reset_bias = 0; |
| } |
| relax_hint_id_current = tmp + relax_hint_bias; |
| |
| /* Insert the element to the head of the link list. */ |
| struct relax_hint_id *tmp_id = malloc (sizeof (struct relax_hint_id)); |
| tmp_id->old_id = tmp; |
| tmp_id->new_id = relax_hint_id_current; |
| tmp_id->next = record_id_head; |
| record_id_head = tmp_id; |
| } |
| |
| if (name && strcmp (name, "end") == 0) |
| relax_hint_begin = 0; |
| name = nds_itoa (relax_hint_id_current); |
| |
| reordered_id: |
| |
| /* Find relax hint entry for next instruction, and all member will be |
| initialized at that time. */ |
| relocs = str_hash_find (nds32_hint_hash, name); |
| if (relocs == NULL) |
| { |
| relocs = notes_calloc (1, sizeof (*relocs)); |
| str_hash_insert (nds32_hint_hash, name, relocs, 0); |
| } |
| else |
| { |
| while (relocs->next) |
| relocs = relocs->next; |
| relocs->next = notes_calloc (1, sizeof (*relocs)); |
| relocs = relocs->next; |
| } |
| |
| *input_line_pointer = saved_char; |
| ignore_rest_of_line (); |
| |
| /* Get the final one of relax hint series. */ |
| |
| /* It has to build this list because there are maybe more than one |
| instructions relative to the same instruction. It to connect to |
| next instruction after md_assemble. */ |
| new = XNEW (struct nds32_relocs_group); |
| memset (new, 0, sizeof (struct nds32_relocs_group)); |
| new->pattern = relocs; |
| new->next = NULL; |
| group = nds32_relax_hint_current; |
| if (!group) |
| nds32_relax_hint_current = new; |
| else |
| { |
| while (group->next != NULL) |
| group = group->next; |
| group->next = new; |
| } |
| relaxing = true; |
| } |
| |
| /* Decide the size of vector entries, only accepts 4 or 16 now. */ |
| |
| static void |
| nds32_vec_size (int ignore ATTRIBUTE_UNUSED) |
| { |
| expressionS exp; |
| |
| expression (&exp); |
| |
| if (exp.X_op == O_constant) |
| { |
| if (exp.X_add_number == 4 || exp.X_add_number == 16) |
| { |
| if (vec_size == 0) |
| vec_size = exp.X_add_number; |
| else if (vec_size != exp.X_add_number) |
| as_warn (_("Different arguments of .vec_size are found, " |
| "previous %d, current %d"), |
| (int) vec_size, (int) exp.X_add_number); |
| } |
| else |
| as_warn (_("Argument of .vec_size is expected 4 or 16, actual: %d."), |
| (int) exp.X_add_number); |
| } |
| else |
| as_warn (_("Argument of .vec_size is not a constant.")); |
| } |
| |
| /* The behavior of ".flag" directive varies depending on the target. |
| In nds32 target, we use it to recognize whether this assembly content is |
| generated by compiler. Other features can also be added in this function |
| in the future. */ |
| |
| static void |
| nds32_flag (int ignore ATTRIBUTE_UNUSED) |
| { |
| char *name; |
| char saved_char; |
| int i; |
| const char *possible_flags[] = { "verbatim" }; |
| |
| /* Skip whitespaces. */ |
| name = input_line_pointer; |
| while (*input_line_pointer && !is_whitespace (*input_line_pointer)) |
| input_line_pointer++; |
| saved_char = *input_line_pointer; |
| *input_line_pointer = 0; |
| |
| for (i = 0; i < (int) ARRAY_SIZE (possible_flags); i++) |
| { |
| if (strcmp (name, possible_flags[i]) == 0) |
| { |
| switch (i) |
| { |
| case 0: |
| /* flag: verbatim */ |
| verbatim = 1; |
| break; |
| default: |
| break; |
| } |
| /* Already found the flag, no need to continue next loop. */ |
| break; |
| } |
| } |
| |
| *input_line_pointer = saved_char; |
| ignore_rest_of_line (); |
| } |
| |
| static void |
| ict_model (int ignore ATTRIBUTE_UNUSED) |
| { |
| char *name; |
| char saved_char; |
| int i; |
| const char *possible_flags[] = { "small", "large" }; |
| |
| /* Skip whitespaces. */ |
| name = input_line_pointer; |
| while (*input_line_pointer && !is_whitespace (*input_line_pointer)) |
| input_line_pointer++; |
| saved_char = *input_line_pointer; |
| *input_line_pointer = 0; |
| |
| for (i = 0; i < (int) ARRAY_SIZE (possible_flags); i++) |
| { |
| if (strcmp (name, possible_flags[i]) == 0) |
| { |
| switch (i) |
| { |
| case 0: |
| /* flag: verbatim */ |
| ict_flag = ICT_SMALL; |
| break; |
| case 1: |
| ict_flag = ICT_LARGE; |
| break; |
| default: |
| break; |
| } |
| /* Already found the flag, no need to continue next loop. */ |
| break; |
| } |
| } |
| |
| *input_line_pointer = saved_char; |
| ignore_rest_of_line (); |
| } |
| |
| static void |
| nds32_n12hc (int ignore ATTRIBUTE_UNUSED) |
| { |
| /* N1213HC core is used. */ |
| } |
| |
| |
| /* The target specific pseudo-ops which we support. */ |
| const pseudo_typeS md_pseudo_table[] = |
| { |
| /* Forced alignment if declared these ways. */ |
| {"ascii", stringer, 8 + 0}, |
| {"asciz", stringer, 8 + 1}, |
| {"double", nds32_aligned_float_cons, 'd'}, |
| {"dword", nds32_aligned_cons, 3}, |
| {"float", nds32_aligned_float_cons, 'f'}, |
| {"half", nds32_aligned_cons, 1}, |
| {"hword", nds32_aligned_cons, 1}, |
| {"int", nds32_aligned_cons, 2}, |
| {"long", nds32_aligned_cons, 2}, |
| {"octa", nds32_aligned_cons, 4}, |
| {"quad", nds32_aligned_cons, 3}, |
| {"qword", nds32_aligned_cons, 4}, |
| {"short", nds32_aligned_cons, 1}, |
| {"byte", nds32_aligned_cons, 0}, |
| {"single", nds32_aligned_float_cons, 'f'}, |
| {"string", stringer, 8 + 1}, |
| {"word", nds32_aligned_cons, 2}, |
| |
| {"little", set_endian_little, 1}, |
| {"big", set_endian_little, 0}, |
| {"16bit_on", trigger_16bit, 1}, |
| {"16bit_off", trigger_16bit, 0}, |
| {"restore_16bit", restore_16bit, 0}, |
| {"off_16bit", off_16bit, 0}, |
| |
| {"sdata_d", nds32_seg, SDATA_D_SECTION}, |
| {"sdata_w", nds32_seg, SDATA_W_SECTION}, |
| {"sdata_h", nds32_seg, SDATA_H_SECTION}, |
| {"sdata_b", nds32_seg, SDATA_B_SECTION}, |
| {"sdata_f", nds32_seg, SDATA_F_SECTION}, |
| |
| {"sbss_d", nds32_seg, SBSS_D_SECTION}, |
| {"sbss_w", nds32_seg, SBSS_W_SECTION}, |
| {"sbss_h", nds32_seg, SBSS_H_SECTION}, |
| {"sbss_b", nds32_seg, SBSS_B_SECTION}, |
| {"sbss_f", nds32_seg, SBSS_F_SECTION}, |
| |
| {"pic", nds32_enable_pic, 0}, |
| {"n12_hc", nds32_n12hc, 0}, |
| {"abi_1", nds32_set_abi, E_NDS_ABI_V1}, |
| {"abi_2", nds32_set_abi, E_NDS_ABI_AABI}, |
| /* Obsolete. */ |
| {"abi_2fp", nds32_set_abi, E_NDS_ABI_V2FP}, |
| {"abi_2fp_plus", nds32_set_abi, E_NDS_ABI_V2FP_PLUS}, |
| {"relax", nds32_relax_relocs, 1}, |
| {"no_relax", nds32_relax_relocs, 0}, |
| {"hint_func_args", nds32_set_hint_func_args, 0}, /* Abandon?? */ |
| {"omit_fp_begin", nds32_omit_fp_begin, 1}, |
| {"omit_fp_end", nds32_omit_fp_begin, 0}, |
| {"vec_size", nds32_vec_size, 0}, |
| {"flag", nds32_flag, 0}, |
| {"innermost_loop_begin", nds32_loop_begin, 1}, |
| {"innermost_loop_end", nds32_loop_begin, 0}, |
| {"relax_hint", nds32_relax_hint, 0}, |
| {"ict_model", ict_model, 0}, |
| {NULL, NULL, 0} |
| }; |
| |
| void |
| nds32_pre_do_align (int n, char *fill, int len, int max) |
| { |
| /* Only make a frag if we HAVE to... */ |
| fragS *fragP; |
| if (n != 0 && !need_pass_2) |
| { |
| if (fill == NULL) |
| { |
| if (subseg_text_p (now_seg)) |
| { |
| dwarf2_emit_insn (0); |
| fragP = frag_now; |
| add_mapping_symbol_for_align (n, frag_now_fix (), 0); |
| frag_align_code (n, max); |
| |
| /* Tag this alignment when there is a label before it. */ |
| if (label_exist) |
| { |
| fragP->tc_frag_data.flag = NDS32_FRAG_LABEL; |
| label_exist = 0; |
| } |
| } |
| else |
| frag_align (n, 0, max); |
| } |
| else if (len <= 1) |
| frag_align (n, *fill, max); |
| else |
| frag_align_pattern (n, fill, len, max); |
| } |
| } |
| |
| void |
| nds32_do_align (int n) |
| { |
| /* Optimize for space and label exists. */ |
| expressionS exp; |
| |
| /* FIXME:I think this will break debug info sections and except_table. */ |
| if (!enable_relax_relocs || !subseg_text_p (now_seg)) |
| return; |
| |
| /* Create and attach a BFD_RELOC_NDS32_LABEL fixup |
| the size of instruction may not be correct because |
| it could be relaxable. */ |
| exp.X_op = O_symbol; |
| exp.X_add_symbol = section_symbol (now_seg); |
| exp.X_add_number = n; |
| fix_new_exp (frag_now, |
| frag_now_fix (), 0, &exp, 0, BFD_RELOC_NDS32_LABEL); |
| } |
| |
| /* Supported Andes machines. */ |
| struct nds32_machs |
| { |
| enum bfd_architecture bfd_mach; |
| int mach_flags; |
| }; |
| |
| /* This is the callback for nds32-asm.c to parse operands. */ |
| |
| int |
| nds32_asm_parse_operand (struct nds32_asm_desc *pdesc ATTRIBUTE_UNUSED, |
| struct nds32_asm_insn *pinsn, |
| char **pstr, int64_t *value) |
| { |
| char *hold; |
| expressionS *pexp = pinsn->info; |
| |
| hold = input_line_pointer; |
| input_line_pointer = *pstr; |
| expression (pexp); |
| resolve_register (pexp); |
| *pstr = input_line_pointer; |
| input_line_pointer = hold; |
| |
| switch (pexp->X_op) |
| { |
| case O_symbol: |
| *value = 0; |
| return NASM_R_SYMBOL; |
| case O_constant: |
| *value = pexp->X_add_number; |
| return NASM_R_CONST; |
| case O_illegal: |
| case O_absent: |
| case O_register: |
| default: |
| return NASM_R_ILLEGAL; |
| } |
| } |
| |
| /* GAS will call this function at the start of the assembly, after the command |
| line arguments have been parsed and all the machine independent |
| initializations have been completed. */ |
| |
| void |
| md_begin (void) |
| { |
| const struct nds32_keyword *k; |
| relax_info_t *relax_info; |
| int flags = 0; |
| |
| bfd_set_arch_mach (stdoutput, TARGET_ARCH, nds32_baseline); |
| |
| nds32_init_nds32_pseudo_opcodes (); |
| asm_desc.parse_operand = nds32_asm_parse_operand; |
| if (nds32_gpr16) |
| flags |= NASM_OPEN_REDUCED_REG; |
| nds32_asm_init (&asm_desc, flags); |
| |
| /* Initial general purpose registers hash table. */ |
| nds32_gprs_hash = str_htab_create (); |
| for (k = nds32_keyword_gpr; k->name; k++) |
| str_hash_insert (nds32_gprs_hash, k->name, k, 0); |
| |
| /* Initial branch hash table. */ |
| nds32_relax_info_hash = str_htab_create (); |
| for (relax_info = relax_table; relax_info->opcode; relax_info++) |
| str_hash_insert (nds32_relax_info_hash, relax_info->opcode, relax_info, 0); |
| |
| /* Initial relax hint hash table. */ |
| nds32_hint_hash = str_htab_create (); |
| enable_16bit = nds32_16bit_ext; |
| } |
| |
| /* HANDLE_ALIGN in write.c. */ |
| |
| void |
| nds32_handle_align (fragS *fragp) |
| { |
| static const unsigned char nop16[] = { 0x92, 0x00 }; |
| static const unsigned char nop32[] = { 0x40, 0x00, 0x00, 0x09 }; |
| int bytes; |
| char *p; |
| |
| if (fragp->fr_type != rs_align_code) |
| return; |
| |
| bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix; |
| p = fragp->fr_literal + fragp->fr_fix; |
| |
| if (bytes & 1) |
| { |
| *p++ = 0; |
| bytes--; |
| } |
| |
| if (bytes & 2) |
| { |
| expressionS exp_t; |
| exp_t.X_op = O_symbol; |
| exp_t.X_add_symbol = abs_section_sym; |
| exp_t.X_add_number = R_NDS32_INSN16_CONVERT_FLAG; |
| fix_new_exp (fragp, fragp->fr_fix, 2, &exp_t, 0, |
| BFD_RELOC_NDS32_INSN16); |
| memcpy (p, nop16, 2); |
| p += 2; |
| bytes -= 2; |
| } |
| |
| while (bytes >= 4) |
| { |
| memcpy (p, nop32, 4); |
| p += 4; |
| bytes -= 4; |
| } |
| |
| bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix; |
| fragp->fr_fix += bytes; |
| } |
| |
| /* md_flush_pending_output */ |
| |
| void |
| nds32_flush_pending_output (void) |
| { |
| nds32_last_label = NULL; |
| } |
| |
| void |
| nds32_frob_label (symbolS *label) |
| { |
| dwarf2_emit_label (label); |
| } |
| |
| /* TC_START_LABEL */ |
| |
| int |
| nds32_start_label (int asmdone ATTRIBUTE_UNUSED, int secdone ATTRIBUTE_UNUSED) |
| { |
| if (optimize && subseg_text_p (now_seg)) |
| label_exist = 1; |
| return 1; |
| } |
| |
| /* TARGET_FORMAT */ |
| |
| const char * |
| nds32_target_format (void) |
| { |
| #ifdef TE_LINUX |
| if (target_big_endian) |
| return "elf32-nds32be-linux"; |
| else |
| return "elf32-nds32le-linux"; |
| #else |
| if (target_big_endian) |
| return "elf32-nds32be"; |
| else |
| return "elf32-nds32le"; |
| #endif |
| } |
| |
| static enum nds32_br_range |
| get_range_type (const struct nds32_field *field) |
| { |
| gas_assert (field != NULL); |
| |
| if (field->bitpos != 0) |
| return BR_RANGE_U4G; |
| |
| if (field->bitsize == 24 && field->shift == 1) |
| return BR_RANGE_S16M; |
| else if (field->bitsize == 16 && field->shift == 1) |
| return BR_RANGE_S64K; |
| else if (field->bitsize == 14 && field->shift == 1) |
| return BR_RANGE_S16K; |
| else if (field->bitsize == 8 && field->shift == 1) |
| return BR_RANGE_S256; |
| else |
| return BR_RANGE_U4G; |
| } |
| |
| /* Save pseudo instruction relocation list. */ |
| |
| static struct nds32_relocs_pattern* |
| nds32_elf_save_pseudo_pattern (fixS* fixP, struct nds32_asm_insn *insn, |
| char *out, symbolS *sym, |
| struct nds32_relocs_pattern *reloc_ptr, |
| fragS *fragP) |
| { |
| struct nds32_opcode *opcode = insn->opcode; |
| if (!reloc_ptr) |
| reloc_ptr = XNEW (struct nds32_relocs_pattern); |
| reloc_ptr->seg = now_seg; |
| reloc_ptr->sym = sym; |
| reloc_ptr->frag = fragP; |
| reloc_ptr->frchain = frchain_now; |
| reloc_ptr->fixP = fixP; |
| reloc_ptr->opcode = opcode; |
| reloc_ptr->where = out; |
| reloc_ptr->insn = insn->insn; |
| reloc_ptr->next = NULL; |
| return reloc_ptr; |
| } |
| |
| /* Check X_md to transform relocation. */ |
| |
| static fixS* |
| nds32_elf_record_fixup_exp (fragS *fragP, const char *str, |
| const struct nds32_field *fld, |
| expressionS *pexp, char* out, |
| struct nds32_asm_insn *insn) |
| { |
| int reloc = -1; |
| expressionS exp; |
| fixS *fixP = NULL; |
| |
| /* Handle instruction relocation. */ |
| if (fld && fld->bitpos == 0 && (insn->attr & NASM_ATTR_HI20)) |
| { |
| /* Relocation for hi20 modifier. */ |
| switch (pexp->X_md) |
| { |
| case BFD_RELOC_NDS32_GOTOFF: /* @GOTOFF */ |
| reloc = BFD_RELOC_NDS32_GOTOFF_HI20; |
| break; |
| case BFD_RELOC_NDS32_GOT20: /* @GOT */ |
| reloc = BFD_RELOC_NDS32_GOT_HI20; |
| break; |
| case BFD_RELOC_NDS32_25_PLTREL: /* @PLT */ |
| if (!nds32_pic) |
| as_bad (_("Invalid PIC expression.")); |
| else |
| reloc = BFD_RELOC_NDS32_PLT_GOTREL_HI20; |
| break; |
| case BFD_RELOC_NDS32_GOTPC20: /* _GLOBAL_OFFSET_TABLE_ */ |
| reloc = BFD_RELOC_NDS32_GOTPC_HI20; |
| break; |
| case BFD_RELOC_NDS32_TPOFF: /* @TPOFF */ |
| reloc = BFD_RELOC_NDS32_TLS_LE_HI20; |
| break; |
| case BFD_RELOC_NDS32_GOTTPOFF: /* @GOTTPOFF */ |
| reloc = nds32_pic ? BFD_RELOC_NDS32_TLS_IEGP_HI20 : BFD_RELOC_NDS32_TLS_IE_HI20; |
| break; |
| case BFD_RELOC_NDS32_TLS_DESC: /* @TLSDESC */ |
| reloc = BFD_RELOC_NDS32_TLS_DESC_HI20; |
| break; |
| default: /* No suffix */ |
| if (nds32_pic) |
| /* When the file is pic, the address must be offset to gp. |
| It may define another relocation or use GOTOFF. */ |
| reloc = BFD_RELOC_NDS32_PLT_GOTREL_HI20; |
| else |
| reloc = BFD_RELOC_NDS32_HI20; |
| break; |
| } |
| fixP = fix_new_exp (fragP, out - fragP->fr_literal, insn->opcode->isize, |
| insn->info, 0 /* pcrel */, reloc); |
| } |
| else if (fld && fld->bitpos == 0 && (insn->attr & NASM_ATTR_LO12)) |
| { |
| /* Relocation for lo12 modifier. */ |
| if (fld->bitsize == 15 && fld->shift == 0) |
| { |
| /* [ls]bi || ori */ |
| switch (pexp->X_md) |
| { |
| case BFD_RELOC_NDS32_GOTOFF: /* @GOTOFF */ |
| reloc = BFD_RELOC_NDS32_GOTOFF_LO12; |
| break; |
| case BFD_RELOC_NDS32_GOT20: /* @GOT */ |
| reloc = BFD_RELOC_NDS32_GOT_LO12; |
| break; |
| case BFD_RELOC_NDS32_25_PLTREL: /* @PLT */ |
| if (!nds32_pic) |
| as_bad (_("Invalid PIC expression.")); |
| else |
| reloc = BFD_RELOC_NDS32_PLT_GOTREL_LO12; |
| break; |
| case BFD_RELOC_NDS32_GOTPC20: /* _GLOBAL_OFFSET_TABLE_ */ |
| reloc = BFD_RELOC_NDS32_GOTPC_LO12; |
| break; |
| case BFD_RELOC_NDS32_TPOFF: /* @TPOFF */ |
| reloc = BFD_RELOC_NDS32_TLS_LE_LO12; |
| break; |
| case BFD_RELOC_NDS32_GOTTPOFF: /* @GOTTPOFF */ |
| reloc = nds32_pic ? BFD_RELOC_NDS32_TLS_IEGP_LO12 : BFD_RELOC_NDS32_TLS_IE_LO12; |
| break; |
| case BFD_RELOC_NDS32_TLS_DESC: /* @TLSDESC */ |
| reloc = BFD_RELOC_NDS32_TLS_DESC_LO12; |
| break; |
| default: /* No suffix */ |
| if (nds32_pic) |
| /* When the file is pic, the address must be offset to gp. |
| It may define another relocation or use GOTOFF. */ |
| reloc = BFD_RELOC_NDS32_PLT_GOTREL_LO12; |
| else |
| reloc = BFD_RELOC_NDS32_LO12S0; |
| break; |
| } |
| } |
| else if (fld->bitsize == 15 && fld->shift == 1) |
| reloc = BFD_RELOC_NDS32_LO12S1; /* [ls]hi */ |
| else if (fld->bitsize == 15 && fld->shift == 2) |
| { |
| /* [ls]wi */ |
| switch (pexp->X_md) |
| { |
| case BFD_RELOC_NDS32_GOTTPOFF: /* @GOTTPOFF */ |
| reloc = nds32_pic ? BFD_RELOC_NDS32_TLS_IEGP_LO12S2 : BFD_RELOC_NDS32_TLS_IE_LO12S2; |
| break; |
| default: /* No suffix */ |
| reloc = BFD_RELOC_NDS32_LO12S2; |
| break; |
| } |
| } |
| else if (fld->bitsize == 15 && fld->shift == 3) |
| reloc = BFD_RELOC_NDS32_LO12S3; /* [ls]di */ |
| else if (fld->bitsize == 12 && fld->shift == 2) |
| reloc = BFD_RELOC_NDS32_LO12S2_SP; /* f[ls][sd]i */ |
| |
| fixP = fix_new_exp (fragP, out - fragP->fr_literal, insn->opcode->isize, |
| insn->info, 0 /* pcrel */, reloc); |
| } |
| else if (fld && fld->bitpos == 0 && insn->opcode->isize == 4 |
| && (insn->attr & NASM_ATTR_PCREL)) |
| { |
| /* Relocation for 32-bit branch instructions. */ |
| if (fld->bitsize == 24 && fld->shift == 1) |
| reloc = BFD_RELOC_NDS32_25_PCREL; |
| else if (fld->bitsize == 16 && fld->shift == 1) |
| reloc = BFD_RELOC_NDS32_17_PCREL; |
| else if (fld->bitsize == 14 && fld->shift == 1) |
| reloc = BFD_RELOC_NDS32_15_PCREL; |
| else if (fld->bitsize == 8 && fld->shift == 1) |
| reloc = BFD_RELOC_NDS32_WORD_9_PCREL; |
| else |
| abort (); |
| |
| fixP = fix_new_exp (fragP, out - fragP->fr_literal, insn->opcode->isize, |
| insn->info, 1 /* pcrel */, reloc); |
| } |
| else if (fld && fld->bitpos == 0 && insn->opcode->isize == 4 |
| && (insn->attr & NASM_ATTR_GPREL)) |
| { |
| /* Relocation for 32-bit gp-relative instructions. */ |
| if (fld->bitsize == 19 && fld->shift == 0) |
| reloc = BFD_RELOC_NDS32_SDA19S0; |
| else if (fld->bitsize == 18 && fld->shift == 1) |
| reloc = BFD_RELOC_NDS32_SDA18S1; |
| else if (fld->bitsize == 17 && fld->shift == 2) |
| reloc = BFD_RELOC_NDS32_SDA17S2; |
| else |
| abort (); |
| |
| fixP = fix_new_exp (fragP, out - fragP->fr_literal, insn->opcode->isize, |
| insn->info, 0 /* pcrel */, reloc); |
| /* Insert INSN16 for converting fp_as_gp. */ |
| exp.X_op = O_symbol; |
| exp.X_add_symbol = abs_section_sym; |
| exp.X_add_number = 0; |
| if (in_omit_fp && reloc == BFD_RELOC_NDS32_SDA17S2) |
| fix_new_exp (fragP, out - fragP->fr_literal, |
| insn->opcode->isize, &exp, 0 /* pcrel */, |
| BFD_RELOC_NDS32_INSN16); |
| } |
| else if (fld && fld->bitpos == 0 && insn->opcode->isize == 2 |
| && (insn->attr & NASM_ATTR_PCREL)) |
| { |
| /* Relocation for 16-bit branch instructions. */ |
| if (fld->bitsize == 8 && fld->shift == 1) |
| reloc = BFD_RELOC_NDS32_9_PCREL; |
| else |
| abort (); |
| |
| fixP = fix_new_exp (fragP, out - fragP->fr_literal, insn->opcode->isize, |
| insn->info, 1 /* pcrel */, reloc); |
| } |
| else if (fld) |
| as_bad (_("Don't know how to handle this field. %s"), str); |
| |
| return fixP; |
| } |
| |
| /* Build instruction pattern to relax. There are two type group pattern |
| including pseudo instruction and relax hint. */ |
| |
| static void |
| nds32_elf_build_relax_relation (fixS *fixP, expressionS *pexp, char* out, |
| struct nds32_asm_insn *insn, fragS *fragP, |
| const struct nds32_field *fld, |
| bool pseudo_hint) |
| { |
| struct nds32_relocs_pattern *reloc_ptr; |
| struct nds32_relocs_group *group; |
| symbolS *sym = NULL; |
| |
| /* The expression may be used uninitialized. */ |
| if (fld) |
| sym = pexp->X_add_symbol; |
| |
| if (pseudo_hint) |
| { |
| /* We cannot know how many instructions will be expanded for |
| the pseudo instruction here. The first expanded instruction fills |
| the memory created by relax_hint. The follower will created and link |
| here. */ |
| group = nds32_relax_hint_current; |
| while (group) |
| { |
| if (group->pattern->opcode == NULL) |
| nds32_elf_save_pseudo_pattern (fixP, insn, out, sym, |
| group->pattern, fragP); |
| else |
| { |
| group->pattern->next = |
| nds32_elf_save_pseudo_pattern (fixP, insn, out, sym, |
| NULL, fragP); |
| group->pattern = group->pattern->next; |
| } |
| group = group->next; |
| } |
| } |
| else if (pseudo_opcode) |
| { |
| /* Save instruction relation for pseudo instruction expanding pattern. */ |
| reloc_ptr = nds32_elf_save_pseudo_pattern (fixP, insn, out, sym, |
| NULL, fragP); |
| if (!relocs_list) |
| relocs_list = reloc_ptr; |
| else |
| { |
| struct nds32_relocs_pattern *temp = relocs_list; |
| while (temp->next) |
| temp = temp->next; |
| temp->next = reloc_ptr; |
| } |
| } |
| else if (nds32_relax_hint_current) |
| { |
| /* Save instruction relation by relax hint. */ |
| group = nds32_relax_hint_current; |
| while (group) |
| { |
| nds32_elf_save_pseudo_pattern (fixP, insn, out, sym, |
| group->pattern, fragP); |
| group = group->next; |
| free (nds32_relax_hint_current); |
| nds32_relax_hint_current = group; |
| } |
| } |
| |
| /* Set relaxing false only for relax_hint trigger it. */ |
| if (!pseudo_opcode) |
| relaxing = false; |
| } |
| |
| #define N32_MEM_EXT(insn) ((N32_OP6_MEM << 25) | insn) |
| |
| /* Relax pattern for link time relaxation. */ |
| /* Relaxation types only! relocation types are not necessary. */ |
| /* Refer to nds32_elf_record_fixup_exp (). */ |
| |
| static struct nds32_relax_hint_table relax_ls_table[] = |
| { |
| { |
| /* LA and Floating LSI. */ |
| .main_type = NDS32_RELAX_HINT_LA_FLSI, |
| .relax_code_size = 12, |
| .relax_code_seq = |
| { |
| OP6 (SETHI), |
| OP6 (ORI), |
| OP6 (LBI), |
| }, |
| .relax_fixup = |
| { |
| {0, 4, NDS32_HINT | NDS32_ADDEND, BFD_RELOC_NDS32_LOADSTORE}, |
| {4, 4, NDS32_HINT | NDS32_PTR, BFD_RELOC_NDS32_PTR}, |
| {4, 4, NDS32_HINT | NDS32_INSN16, BFD_RELOC_NDS32_INSN16}, |
| {8, 4, NDS32_HINT | NDS32_SYM, BFD_RELOC_NDS32_LSI}, |
| {8, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {8, 4, NDS32_HINT | NDS32_INSN16, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| } |
| }, |
| { |
| /* Load Address / Load-Store (LALS). */ |
| .main_type = NDS32_RELAX_HINT_LALS, |
| .relax_code_size = 12, |
| .relax_code_seq = |
| { |
| OP6 (SETHI), |
| OP6 (ORI), |
| OP6 (LBI), |
| }, |
| .relax_fixup = |
| { |
| {0, 4, NDS32_HINT | NDS32_ADDEND, BFD_RELOC_NDS32_LOADSTORE}, |
| {4, 4, NDS32_HINT | NDS32_INSN16, BFD_RELOC_NDS32_INSN16}, |
| {8, 4, NDS32_HINT | NDS32_INSN16, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| } |
| }, |
| { |
| /* B(AL) symbol@PLT */ |
| .main_type = NDS32_RELAX_HINT_LA_PLT, |
| .relax_code_size = 16, |
| .relax_code_seq = |
| { |
| OP6 (SETHI), |
| OP6 (ORI), |
| OP6 (ALU1), |
| OP6 (JREG), |
| }, |
| .relax_fixup = |
| { |
| {0, 4, NDS32_HINT | NDS32_ADDEND, BFD_RELOC_NDS32_LOADSTORE}, |
| {4, 4, NDS32_HINT | NDS32_PTR, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_HINT | NDS32_PTR, BFD_RELOC_NDS32_PTR}, |
| {12, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_PLT_GOT_SUFF}, |
| {12, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_HINT | NDS32_INSN16, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| } |
| }, |
| { |
| /* LA (@GOT). */ |
| .main_type = NDS32_RELAX_HINT_LA_GOT, |
| .relax_code_size = 12, |
| .relax_code_seq = |
| { |
| OP6 (SETHI), |
| OP6 (ORI), |
| OP6 (MEM), |
| }, |
| .relax_fixup = |
| { |
| {0, 4, NDS32_HINT | NDS32_ADDEND, BFD_RELOC_NDS32_LOADSTORE}, |
| {4, 4, NDS32_HINT | NDS32_PTR, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {8, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_GOT_SUFF}, |
| {0, 0, 0, 0} |
| } |
| }, |
| { |
| /* LA (@GOTOFF). */ |
| .main_type = NDS32_RELAX_HINT_LA_GOTOFF, |
| .relax_code_size = 16, |
| .relax_code_seq = |
| { |
| OP6 (SETHI), |
| OP6 (ORI), |
| OP6 (ALU1), |
| OP6 (MEM), |
| }, |
| .relax_fixup = |
| { |
| {0, 4, NDS32_HINT | NDS32_ADDEND, BFD_RELOC_NDS32_LOADSTORE}, |
| {4, 4, NDS32_HINT | NDS32_PTR, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {8, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_GOTOFF_SUFF}, |
| {12, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_GOTOFF_SUFF}, |
| {0, 0, 0, 0} |
| } |
| }, |
| { |
| /* TLS LE LS|LA */ |
| .main_type = NDS32_RELAX_HINT_TLS_LE_LS, |
| .relax_code_size = 16, |
| .relax_code_seq = |
| { |
| OP6(SETHI), |
| OP6(ORI), |
| OP6(MEM), |
| OP6(ALU1), |
| }, |
| .relax_fixup = |
| { |
| {0, 4, NDS32_HINT | NDS32_ADDEND, BFD_RELOC_NDS32_LOADSTORE}, |
| {4, 4, NDS32_HINT | NDS32_PTR_MULTIPLE, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {8, 4, NDS32_HINT | NDS32_SYM, BFD_RELOC_NDS32_TLS_LE_LS}, |
| {12, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {12, 4, NDS32_HINT | NDS32_SYM, BFD_RELOC_NDS32_TLS_LE_ADD}, |
| {0, 0, 0, 0} |
| } |
| }, |
| { |
| /* TLS IE LA */ |
| .main_type = NDS32_RELAX_HINT_TLS_IE_LA, |
| .relax_code_size = 8, |
| .relax_code_seq = |
| { |
| OP6(SETHI), |
| OP6(LBI), |
| }, |
| .relax_fixup = |
| { |
| {0, 4, NDS32_HINT | NDS32_ADDEND, BFD_RELOC_NDS32_LOADSTORE}, |
| {4, 4, NDS32_HINT | NDS32_INSN16, BFD_RELOC_NDS32_INSN16}, |
| {0, 0, 0, 0} |
| } |
| }, |
| { |
| /* TLS IEGP LA */ |
| .main_type = NDS32_RELAX_HINT_TLS_IEGP_LA, |
| .relax_code_size = 12, |
| .relax_code_seq = |
| { |
| OP6 (SETHI), |
| OP6 (ORI), |
| OP6 (MEM), |
| }, |
| .relax_fixup = |
| { |
| {0, 4, NDS32_HINT | NDS32_ADDEND, BFD_RELOC_NDS32_LOADSTORE}, |
| {4, 4, NDS32_HINT | NDS32_PTR_PATTERN, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {8, 4, NDS32_HINT | NDS32_SYM, BFD_RELOC_NDS32_TLS_IEGP_LW}, |
| {0, 0, 0, 0} |
| } |
| }, |
| { |
| /* TLS DESC LS: */ |
| .main_type = NDS32_RELAX_HINT_TLS_DESC_LS, |
| .relax_code_size = 24, |
| .relax_code_seq = |
| { |
| OP6 (SETHI), |
| OP6 (ORI), |
| OP6 (ALU1), |
| OP6 (LBI), /* load argument */ |
| OP6 (JREG), |
| OP6 (MEM), /* load/store variable or load argument */ |
| }, |
| .relax_fixup = |
| { |
| {0, 4, NDS32_HINT | NDS32_ADDEND, BFD_RELOC_NDS32_LOADSTORE}, |
| {4, 4, NDS32_HINT | NDS32_PTR_PATTERN, BFD_RELOC_NDS32_PTR}, |
| {8, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_PTR_RESOLVED}, |
| {8, 4, NDS32_HINT | NDS32_SYM, BFD_RELOC_NDS32_TLS_DESC_ADD}, |
| {12, 4, NDS32_HINT | NDS32_SYM, BFD_RELOC_NDS32_TLS_DESC_FUNC}, |
| {16, 4, NDS32_HINT | NDS32_SYM, BFD_RELOC_NDS32_TLS_DESC_CALL}, |
| {20, 4, NDS32_HINT | NDS32_SYM_DESC_MEM, BFD_RELOC_NDS32_TLS_DESC_MEM}, |
| {0, 0, 0, 0} |
| } |
| }, |
| { |
| .main_type = 0, |
| .relax_code_seq = {0}, |
| .relax_fixup = {{0, 0 , 0, 0}} |
| } |
| }; |
| |
| /* Since sethi loadstore relocation has to using next instruction to determine |
| elimination itself or not, we have to return the next instruction range. */ |
| |
| static int |
| nds32_elf_sethi_range (struct nds32_relocs_pattern *pattern) |
| { |
| int range = 0; |
| while (pattern) |
| { |
| switch (pattern->opcode->value) |
| { |
| case INSN_LBI: |
| case INSN_SBI: |
| case INSN_LBSI: |
| case N32_MEM_EXT (N32_MEM_LB): |
| case N32_MEM_EXT (N32_MEM_LBS): |
| case N32_MEM_EXT (N32_MEM_SB): |
| range = NDS32_LOADSTORE_BYTE; |
| break; |
| case INSN_LHI: |
| case INSN_SHI: |
| case INSN_LHSI: |
| case N32_MEM_EXT (N32_MEM_LH): |
| case N32_MEM_EXT (N32_MEM_LHS): |
| case N32_MEM_EXT (N32_MEM_SH): |
| range = NDS32_LOADSTORE_HALF; |
| break; |
| case INSN_LWI: |
| case INSN_SWI: |
| case N32_MEM_EXT (N32_MEM_LW): |
| case N32_MEM_EXT (N32_MEM_SW): |
| range = NDS32_LOADSTORE_WORD; |
| break; |
| case INSN_FLSI: |
| case INSN_FSSI: |
| range = NDS32_LOADSTORE_FLOAT_S; |
| break; |
| case INSN_FLDI: |
| case INSN_FSDI: |
| range = NDS32_LOADSTORE_FLOAT_D; |
| break; |
| case INSN_ORI: |
| range = NDS32_LOADSTORE_IMM; |
| break; |
| default: |
| range = NDS32_LOADSTORE_NONE; |
| break; |
| } |
| if (range != NDS32_LOADSTORE_NONE) |
| break; |
| pattern = pattern->next; |
| } |
| return range; |
| } |
| |
| /* The args means: instruction size, the 1st instruction is converted to 16 or |
| not, optimize option, 16 bit instruction is enable. */ |
| |
| #define SET_ADDEND(size, convertible, optimize, insn16_on) \ |
| (((size) & 0xff) | ((convertible) ? 1u << 31 : 0) \ |
| | ((optimize) ? 1 << 30 : 0) | (insn16_on ? 1 << 29 : 0)) |
| #define MAC_COMBO (E_NDS32_HAS_FPU_MAC_INST|E_NDS32_HAS_MAC_DX_INST) |
| |
| static void |
| nds32_set_elf_flags_by_insn (struct nds32_asm_insn * insn) |
| { |
| static int skip_flags = NASM_ATTR_FPU_FMA |
| | NASM_ATTR_BRANCH | NASM_ATTR_SATURATION_EXT |
| | NASM_ATTR_GPREL | NASM_ATTR_DXREG |
| | NASM_ATTR_ISA_V1 | NASM_ATTR_ISA_V2 |
| | NASM_ATTR_ISA_V3 | NASM_ATTR_ISA_V3M |
| | NASM_ATTR_PCREL; |
| |
| int new_flags = insn->opcode->attr & ~skip_flags; |
| while (new_flags) |
| { |
| int next = 1 << (ffs (new_flags) - 1); |
| new_flags &= ~next; |
| switch (next) |
| { |
| case NASM_ATTR_PERF_EXT: |
| { |
| if (nds32_perf_ext) |
| { |
| nds32_elf_flags |= E_NDS32_HAS_EXT_INST; |
| skip_flags |= NASM_ATTR_PERF_EXT; |
| } |
| else |
| as_bad (_("instruction %s requires enabling performance " |
| "extension"), insn->opcode->opcode); |
| } |
| break; |
| case NASM_ATTR_PERF2_EXT: |
| { |
| if (nds32_perf_ext2) |
| { |
| nds32_elf_flags |= E_NDS32_HAS_EXT2_INST; |
| skip_flags |= NASM_ATTR_PERF2_EXT; |
| } |
| else |
| as_bad (_("instruction %s requires enabling performance " |
| "extension II"), insn->opcode->opcode); |
| } |
| break; |
| case NASM_ATTR_AUDIO_ISAEXT: |
| { |
| if (nds32_audio_ext) |
| { |
| nds32_elf_flags |= E_NDS32_HAS_AUDIO_INST; |
| skip_flags |= NASM_ATTR_AUDIO_ISAEXT; |
| } |
| else |
| as_bad (_("instruction %s requires enabling AUDIO extension"), |
| insn->opcode->opcode); |
| } |
| break; |
| case NASM_ATTR_STR_EXT: |
| { |
| if (nds32_string_ext) |
| { |
| nds32_elf_flags |= E_NDS32_HAS_STRING_INST; |
| skip_flags |= NASM_ATTR_STR_EXT; |
| } |
| else |
| as_bad (_("instruction %s requires enabling STRING extension"), |
| insn->opcode->opcode); |
| } |
| break; |
| case NASM_ATTR_DIV: |
| { |
| if (insn->opcode->attr & NASM_ATTR_DXREG) |
| { |
| if (nds32_div && nds32_dx_regs) |
| { |
| nds32_elf_flags |= E_NDS32_HAS_DIV_DX_INST; |
| skip_flags |= NASM_ATTR_DIV; |
| } |
| else |
| as_bad (_("instruction %s requires enabling DIV & DX_REGS " |
| "extension"), insn->opcode->opcode); |
| } |
| } |
| break; |
| case NASM_ATTR_FPU: |
| { |
| if (nds32_fpu_sp_ext || nds32_fpu_dp_ext) |
| { |
| if (!(nds32_elf_flags |
| & (E_NDS32_HAS_FPU_INST | E_NDS32_HAS_FPU_DP_INST))) |
| nds32_fpu_com = 1; |
| skip_flags |= NASM_ATTR_FPU; |
| } |
| else |
| as_bad (_("instruction %s requires enabling FPU extension"), |
| insn->opcode->opcode); |
| } |
| break; |
| case NASM_ATTR_FPU_SP_EXT: |
| { |
| if (nds32_fpu_sp_ext) |
| { |
| nds32_elf_flags |= E_NDS32_HAS_FPU_INST; |
| skip_flags |= NASM_ATTR_FPU_SP_EXT; |
| } |
| else |
| as_bad (_("instruction %s requires enabling FPU_SP extension"), |
| insn->opcode->opcode); |
| } |
| break; |
| case NASM_ATTR_FPU_DP_EXT: |
| { |
| if (nds32_fpu_dp_ext) |
| { |
| nds32_elf_flags |= E_NDS32_HAS_FPU_DP_INST; |
| skip_flags |= NASM_ATTR_FPU_DP_EXT; |
| } |
| else |
| as_bad (_("instruction %s requires enabling FPU_DP extension"), |
| insn->opcode->opcode); |
| } |
| break; |
| case NASM_ATTR_MAC: |
| { |
| if (insn->opcode->attr & NASM_ATTR_FPU_SP_EXT) |
| { |
| if (nds32_fpu_sp_ext && nds32_mac) |
| nds32_elf_flags |= E_NDS32_HAS_FPU_MAC_INST; |
| else |
| as_bad (_("instruction %s requires enabling FPU_MAC " |
| "extension"), insn->opcode->opcode); |
| } |
| else if (insn->opcode->attr & NASM_ATTR_FPU_DP_EXT) |
| { |
| if (nds32_fpu_dp_ext && nds32_mac) |
| nds32_elf_flags |= E_NDS32_HAS_FPU_MAC_INST; |
| else |
| as_bad (_("instruction %s requires enabling FPU_MAC " |
| "extension"), insn->opcode->opcode); |
| } |
| else if (insn->opcode->attr & NASM_ATTR_DXREG) |
| { |
| if (nds32_dx_regs && nds32_mac) |
| nds32_elf_flags |= E_NDS32_HAS_MAC_DX_INST; |
| else |
| as_bad (_("instruction %s requires enabling DX_REGS " |
| "extension"), insn->opcode->opcode); |
| } |
| |
| if (MAC_COMBO == (MAC_COMBO & nds32_elf_flags)) |
| skip_flags |= NASM_ATTR_MAC; |
| } |
| break; |
| case NASM_ATTR_DSP_ISAEXT: |
| { |
| if (nds32_dsp_ext) |
| { |
| nds32_elf_flags |= E_NDS32_HAS_DSP_INST; |
| skip_flags |= NASM_ATTR_DSP_ISAEXT; |
| } |
| else |
| as_bad (_("instruction %s requires enabling dsp extension"), |
| insn->opcode->opcode); |
| } |
| break; |
| case NASM_ATTR_ZOL: |
| { |
| if (nds32_zol_ext) |
| { |
| nds32_elf_flags |= E_NDS32_HAS_ZOL; |
| skip_flags |= NASM_ATTR_ZOL; |
| } |
| else |
| as_bad (_("instruction %s requires enabling zol extension"), |
| insn->opcode->opcode); |
| } |
| break; |
| default: |
| as_bad (_("internal error: unknown instruction attribute: 0x%08x"), |
| next); |
| } |
| } |
| } |
| |
| /* Flag for analysis relaxation type. */ |
| |
| enum nds32_insn_type |
| { |
| N32_RELAX_SETHI = 1, |
| N32_RELAX_BR = (1 << 1), |
| N32_RELAX_LSI = (1 << 2), |
| N32_RELAX_JUMP = (1 << 3), |
| N32_RELAX_CALL = (1 << 4), |
| N32_RELAX_ORI = (1 << 5), |
| N32_RELAX_MEM = (1 << 6), |
| N32_RELAX_MOVI = (1 << 7), |
| N32_RELAX_ALU1 = (1 << 8), |
| N32_RELAX_16BIT = (1 << 9), |
| }; |
| |
| struct nds32_hint_map |
| { |
| /* the preamble relocation */ |
| bfd_reloc_code_real_type hi_type; |
| /* mnemonic */ |
| const char *opc; |
| /* relax pattern ID */ |
| enum nds32_relax_hint_type hint_type; |
| /* range */ |
| enum nds32_br_range range; |
| /* pattern character flags */ |
| enum nds32_insn_type insn_list; |
| /* optional pattern character flags */ |
| enum nds32_insn_type option_list; |
| }; |
| |
| /* Table to match instructions with hint and relax pattern. */ |
| |
| static struct nds32_hint_map hint_map [] = |
| { |
| { |
| /* LONGCALL4. */ |
| BFD_RELOC_NDS32_HI20, |
| "jal", |
| NDS32_RELAX_HINT_NONE, |
| BR_RANGE_U4G, |
| N32_RELAX_SETHI | N32_RELAX_ORI | N32_RELAX_CALL, |
| 0, |
| }, |
| { |
| /* LONGCALL5. */ |
| _dummy_first_bfd_reloc_code_real, |
| "bgezal", |
| NDS32_RELAX_HINT_NONE, |
| BR_RANGE_S16M, |
| N32_RELAX_BR | N32_RELAX_CALL, |
| 0, |
| }, |
| { |
| /* LONGCALL6. */ |
| BFD_RELOC_NDS32_HI20, |
| "bgezal", |
| NDS32_RELAX_HINT_NONE, |
| BR_RANGE_U4G, |
| N32_RELAX_BR | N32_RELAX_SETHI | N32_RELAX_ORI | N32_RELAX_CALL, |
| 0, |
| }, |
| { |
| /* LONGJUMP4. */ |
| BFD_RELOC_NDS32_HI20, |
| "j", |
| NDS32_RELAX_HINT_NONE, |
| BR_RANGE_U4G, |
| N32_RELAX_SETHI | N32_RELAX_ORI | N32_RELAX_JUMP, |
| 0, |
| }, |
| { |
| /* LONGJUMP5. */ |
| /* There is two kinds of variation of LONGJUMP5. One of them |
| generate EMPTY relocation for converted INSN16 if needed. |
| But we don't distinguish them here. */ |
| _dummy_first_bfd_reloc_code_real, |
| "beq", |
| NDS32_RELAX_HINT_NONE, |
| BR_RANGE_S16M, |
| N32_RELAX_BR | N32_RELAX_JUMP, |
| 0, |
| }, |
| { |
| /* LONGJUMP6. */ |
| BFD_RELOC_NDS32_HI20, |
| "beq", |
| NDS32_RELAX_HINT_NONE, |
| BR_RANGE_U4G, |
| N32_RELAX_SETHI | N32_RELAX_ORI | N32_RELAX_BR | N32_RELAX_JUMP, |
| 0, |
| }, |
| { |
| /* LONGJUMP7. */ |
| _dummy_first_bfd_reloc_code_real, |
| "beqc", |
| NDS32_RELAX_HINT_NONE, |
| BR_RANGE_S16K, |
| N32_RELAX_MOVI | N32_RELAX_BR, |
| 0, |
| }, |
| { |
| /* LONGCALL (BAL|JR|LA symbol@PLT). */ |
| BFD_RELOC_NDS32_PLT_GOTREL_HI20, |
| NULL, |
| NDS32_RELAX_HINT_LA_PLT, |
| BR_RANGE_U4G, |
| N32_RELAX_SETHI | N32_RELAX_ORI, |
| N32_RELAX_ALU1 | N32_RELAX_CALL | N32_RELAX_JUMP, |
| }, |
| /* relative issue: #12566 */ |
| { |
| /* LA and Floating LSI. */ |
| BFD_RELOC_NDS32_HI20, |
| NULL, |
| NDS32_RELAX_HINT_LA_FLSI, |
| BR_RANGE_U4G, |
| N32_RELAX_SETHI | N32_RELAX_ORI | N32_RELAX_LSI, |
| 0, |
| }, |
| /* relative issue: #11685 #11602 */ |
| { |
| /* load address / load-store (LALS). */ |
| BFD_RELOC_NDS32_HI20, |
| NULL, |
| NDS32_RELAX_HINT_LALS, |
| BR_RANGE_U4G, |
| N32_RELAX_SETHI, |
| N32_RELAX_ORI | N32_RELAX_LSI, |
| }, |
| { |
| /* setup $GP (_GLOBAL_OFFSET_TABLE_) */ |
| BFD_RELOC_NDS32_GOTPC_HI20, |
| NULL, |
| NDS32_RELAX_HINT_LALS, |
| BR_RANGE_U4G, |
| N32_RELAX_SETHI | N32_RELAX_ORI, |
| 0, |
| }, |
| { |
| /* GOT LA/LS (symbol@GOT) */ |
| BFD_RELOC_NDS32_GOT_HI20, |
| NULL, |
| NDS32_RELAX_HINT_LA_GOT, |
| BR_RANGE_U4G, |
| N32_RELAX_SETHI | N32_RELAX_ORI, |
| N32_RELAX_MEM, |
| }, |
| { |
| /* GOTOFF LA/LS (symbol@GOTOFF) */ |
| BFD_RELOC_NDS32_GOTOFF_HI20, |
| NULL, |
| NDS32_RELAX_HINT_LA_GOTOFF, |
| BR_RANGE_U4G, |
| N32_RELAX_SETHI | N32_RELAX_ORI, |
| N32_RELAX_ALU1 | N32_RELAX_MEM, /* | N32_RELAX_LSI, */ |
| }, |
| { |
| /* TLS LE LA|LS (@TPOFF) */ |
| BFD_RELOC_NDS32_TLS_LE_HI20, |
| NULL, |
| NDS32_RELAX_HINT_TLS_LE_LS, |
| BR_RANGE_U4G, |
| N32_RELAX_SETHI | N32_RELAX_ORI, |
| N32_RELAX_ALU1 | N32_RELAX_MEM, |
| }, |
| { |
| /* TLS IE LA */ |
| BFD_RELOC_NDS32_TLS_IE_HI20, |
| NULL, |
| NDS32_RELAX_HINT_TLS_IE_LA, |
| BR_RANGE_U4G, |
| N32_RELAX_SETHI | N32_RELAX_LSI, |
| 0, |
| }, |
| { |
| /* TLS IE LS */ |
| BFD_RELOC_NDS32_TLS_IE_HI20, |
| NULL, |
| NDS32_RELAX_HINT_TLS_IE_LS, |
| BR_RANGE_U4G, |
| N32_RELAX_SETHI | N32_RELAX_LSI | N32_RELAX_MEM, |
| 0, |
| }, |
| { |
| /* TLS IEGP LA */ |
| BFD_RELOC_NDS32_TLS_IEGP_HI20, |
| NULL, |
| NDS32_RELAX_HINT_TLS_IEGP_LA, |
| BR_RANGE_U4G, |
| N32_RELAX_SETHI | N32_RELAX_ORI | N32_RELAX_MEM, |
| 0, |
| }, |
| { |
| /* TLS DESC LS */ |
| BFD_RELOC_NDS32_TLS_DESC_HI20, |
| NULL, |
| NDS32_RELAX_HINT_TLS_DESC_LS, |
| BR_RANGE_U4G, |
| N32_RELAX_SETHI | N32_RELAX_ORI | N32_RELAX_ALU1 | N32_RELAX_CALL, |
| N32_RELAX_LSI | N32_RELAX_MEM, |
| }, |
| /* last one */ |
| {0, NULL, 0, 0 ,0, 0} |
| }; |
| |
| /* Find the relaxation pattern according to instructions. */ |
| |
| static bool |
| nds32_find_reloc_table (struct nds32_relocs_pattern *relocs_pattern, |
| struct nds32_relax_hint_table *hint_info) |
| { |
| unsigned int opcode, seq_size; |
| enum nds32_br_range range; |
| struct nds32_relocs_pattern *pattern, *hi_pattern = NULL; |
| const char *opc = NULL; |
| relax_info_t *relax_info = NULL; |
| nds32_relax_fixup_info_t *fixup_info, *hint_fixup; |
| enum nds32_relax_hint_type hint_type = NDS32_RELAX_HINT_NONE; |
| struct nds32_relax_hint_table *table_ptr; |
| uint32_t *code_seq, *hint_code; |
| enum nds32_insn_type relax_type = 0; |
| struct nds32_hint_map *map_ptr = hint_map; |
| unsigned int i; |
| const char *check_insn[] = |
| { "bnes38", "beqs38", "bnez38", "bnezs8", "beqz38", "beqzs8" }; |
| |
| /* TODO: PLT GOT. */ |
| /* Traverse all pattern instruction and set flag. */ |
| pattern = relocs_pattern; |
| while (pattern) |
| { |
| if (pattern->opcode->isize == 4) |
| { |
| /* 4 byte instruction. */ |
| opcode = N32_OP6 (pattern->opcode->value); |
| switch (opcode) |
| { |
| case N32_OP6_SETHI: |
| hi_pattern = pattern; |
| relax_type |= N32_RELAX_SETHI; |
| break; |
| case N32_OP6_MEM: |
| relax_type |= N32_RELAX_MEM; |
| break; |
| case N32_OP6_ALU1: |
| relax_type |= N32_RELAX_ALU1; |
| break; |
| case N32_OP6_ORI: |
| relax_type |= N32_RELAX_ORI; |
| break; |
| case N32_OP6_BR1: |
| case N32_OP6_BR2: |
| case N32_OP6_BR3: |
| relax_type |= N32_RELAX_BR; |
| break; |
| case N32_OP6_MOVI: |
| relax_type |= N32_RELAX_MOVI; |
| break; |
| case N32_OP6_LBI: |
| case N32_OP6_SBI: |
| case N32_OP6_LBSI: |
| case N32_OP6_LHI: |
| case N32_OP6_SHI: |
| case N32_OP6_LHSI: |
| case N32_OP6_LWI: |
| case N32_OP6_SWI: |
| case N32_OP6_LWC: |
| case N32_OP6_SWC: |
| case N32_OP6_LDC: |
| case N32_OP6_SDC: |
| relax_type |= N32_RELAX_LSI; |
| break; |
| case N32_OP6_JREG: |
| if (__GF (pattern->opcode->value, 0, 1) == 1) |
| relax_type |= N32_RELAX_CALL; |
| else |
| relax_type |= N32_RELAX_JUMP; |
| break; |
| case N32_OP6_JI: |
| if (__GF (pattern->opcode->value, 24, 1) == 1) |
| relax_type |= N32_RELAX_CALL; |
| else |
| relax_type |= N32_RELAX_JUMP; |
| break; |
| default: |
| as_warn (_("relax hint unrecognized instruction: line %d."), |
| pattern->frag->fr_line); |
| return false; |
| } |
| } |
| else |
| { |
| /* 2 byte instruction. Compare by opcode name because |
| the opcode of 2byte instruction is not regular. */ |
| int is_matched = 0; |
| for (i = 0; i < ARRAY_SIZE (check_insn); i++) |
| { |
| if (strcmp (pattern->opcode->opcode, check_insn[i]) == 0) |
| { |
| relax_type |= N32_RELAX_BR; |
| is_matched += 1; |
| break; |
| } |
| } |
| if (!is_matched) |
| relax_type |= N32_RELAX_16BIT; |
| } |
| pattern = pattern->next; |
| } |
| |
| /* Analysis instruction flag to choose relaxation table. */ |
| while (map_ptr->insn_list != 0) |
| { |
| struct nds32_hint_map *hint = map_ptr++; |
| enum nds32_insn_type must = hint->insn_list; |
| enum nds32_insn_type optional = hint->option_list; |
| enum nds32_insn_type extra; |
| |
| if (must != (must & relax_type)) |
| continue; |
| |
| extra = relax_type ^ must; |
| if (extra != (extra & optional)) |
| continue; |
| |
| if (!hi_pattern |
| || (hi_pattern->fixP |
| && hi_pattern->fixP->fx_r_type == hint->hi_type)) |
| { |
| opc = hint->opc; |
| hint_type = hint->hint_type; |
| range = hint->range; |
| map_ptr = hint; |
| break; |
| } |
| } |
| |
| if (map_ptr->insn_list == 0) |
| { |
| if (!nds32_pic) |
| as_warn (_("Can not find match relax hint. Line: %d"), |
| relocs_pattern->frag->fr_line); |
| return false; |
| } |
| |
| /* Get the match table. */ |
| if (opc) |
| { |
| /* Branch relax pattern. */ |
| relax_info = str_hash_find (nds32_relax_info_hash, opc); |
| if (!relax_info) |
| return false; |
| fixup_info = relax_info->relax_fixup[range]; |
| code_seq = relax_info->relax_code_seq[range]; |
| seq_size = relax_info->relax_code_size[range]; |
| } |
| else if (hint_type) |
| { |
| /* Load-store relax pattern. */ |
| table_ptr = relax_ls_table; |
| while (table_ptr->main_type != 0) |
| { |
| if (table_ptr->main_type == hint_type) |
| { |
| fixup_info = table_ptr->relax_fixup; |
| code_seq = table_ptr->relax_code_seq; |
| seq_size = table_ptr->relax_code_size; |
| break; |
| } |
| table_ptr++; |
| } |
| if (table_ptr->main_type == 0) |
| return false; |
| } |
| else |
| return false; |
| |
| hint_fixup = hint_info->relax_fixup; |
| hint_code = hint_info->relax_code_seq; |
| hint_info->relax_code_size = seq_size; |
| |
| while (fixup_info->size != 0) |
| { |
| if (fixup_info->ramp & NDS32_HINT) |
| { |
| memcpy (hint_fixup, fixup_info, sizeof (nds32_relax_fixup_info_t)); |
| hint_fixup++; |
| } |
| fixup_info++; |
| } |
| /* Clear final relocation. */ |
| memset (hint_fixup, 0, sizeof (nds32_relax_fixup_info_t)); |
| /* Copy code sequence. */ |
| memcpy (hint_code, code_seq, seq_size); |
| return true; |
| } |
| |
| /* Because there are a lot of variant of load-store, check |
| all these type here. */ |
| |
| #define CLEAN_REG(insn) ((insn) & 0xfe0003ff) |
| #define GET_OPCODE(insn) ((insn) & 0xfe000000) |
| |
| static bool |
| nds32_match_hint_insn (struct nds32_opcode *opcode, uint32_t seq) |
| { |
| const char *check_insn[] = |
| { "bnes38", "beqs38", "bnez38", "bnezs8", "beqz38", "beqzs8", "jral5" }; |
| uint32_t insn = opcode->value; |
| unsigned int i; |
| |
| insn = CLEAN_REG (opcode->value); |
| if (insn == seq) |
| return true; |
| |
| switch (seq) |
| { |
| case OP6 (LBI): |
| /* In relocation_table, it regards instruction LBI as representation |
| of all the NDS32_RELAX_HINT_LS pattern. */ |
| if (insn == OP6 (LBI) || insn == OP6 (SBI) || insn == OP6 (LBSI) |
| || insn == OP6 (LHI) || insn == OP6 (SHI) || insn == OP6 (LHSI) |
| || insn == OP6 (LWI) || insn == OP6 (SWI) |
| || insn == OP6 (LWC) || insn == OP6 (SWC) |
| || insn == OP6 (LDC) || insn == OP6 (SDC)) |
| return true; |
| break; |
| case OP6 (BR2): |
| /* This is for LONGCALL5 and LONGCALL6. */ |
| if (insn == OP6 (BR2)) |
| return true; |
| break; |
| case OP6 (BR1): |
| /* This is for LONGJUMP5 and LONGJUMP6. */ |
| if (opcode->isize == 4 |
| && (insn == OP6 (BR1) || insn == OP6 (BR2) || insn == OP6 (BR3))) |
| return true; |
| else if (opcode->isize == 2) |
| { |
| for (i = 0; i < ARRAY_SIZE (check_insn); i++) |
| if (strcmp (opcode->opcode, check_insn[i]) == 0) |
| return true; |
| } |
| break; |
| case OP6 (MOVI): |
| /* This is for LONGJUMP7. */ |
| if (opcode->isize == 2 && strcmp (opcode->opcode, "movi55") == 0) |
| return true; |
| break; |
| case OP6 (MEM): |
| if (OP6 (MEM) == GET_OPCODE (insn)) |
| return true; |
| break; |
| case OP6 (JREG): |
| /* bit 24: N32_JI_JAL */ /* feed me! */ |
| if ((insn & ~(N32_BIT (24))) == JREG (JRAL)) |
| return true; |
| break; |
| default: |
| if (opcode->isize == 2) |
| { |
| for (i = 0; i < ARRAY_SIZE (check_insn); i++) |
| if (strcmp (opcode->opcode, check_insn[i]) == 0) |
| return true; |
| |
| if ((strcmp (opcode->opcode, "add5.pc") == 0) || |
| (strcmp (opcode->opcode, "add45") == 0)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* Append relax relocation for link time relaxing. */ |
| |
| static void |
| nds32_elf_append_relax_relocs (const char *key, const void *value) |
| { |
| struct nds32_relocs_pattern *relocs_pattern = |
| (struct nds32_relocs_pattern *) value; |
| struct nds32_relocs_pattern *pattern_temp, *pattern_now; |
| symbolS *sym, *hi_sym = NULL; |
| expressionS exp; |
| fragS *fragP; |
| segT seg_bak = now_seg; |
| frchainS *frchain_bak = frchain_now; |
| struct nds32_relax_hint_table hint_info; |
| nds32_relax_fixup_info_t *hint_fixup, *fixup_now; |
| size_t fixup_size; |
| offsetT branch_offset, hi_branch_offset = 0; |
| fixS *fixP; |
| int range, offset; |
| unsigned int ptr_offset, hint_count, relax_code_size, count = 0; |
| uint32_t *code_seq, code_insn; |
| char *where; |
| int pcrel; |
| |
| if (!relocs_pattern) |
| return; |
| |
| if (!nds32_find_reloc_table (relocs_pattern, &hint_info)) |
| return; |
| |
| /* Save symbol for some EMPTY relocation using. */ |
| pattern_now = relocs_pattern; |
| while (pattern_now) |
| { |
| if (pattern_now->opcode->value == OP6 (SETHI)) |
| { |
| hi_sym = pattern_now->sym; |
| hi_branch_offset = pattern_now->fixP->fx_offset; |
| break; |
| } |
| pattern_now = pattern_now->next; |
| } |
| |
| /* Inserting fix up must specify now_seg or frchain_now. */ |
| now_seg = relocs_pattern->seg; |
| frchain_now = relocs_pattern->frchain; |
| fragP = relocs_pattern->frag; |
| branch_offset = fragP->fr_offset; |
| |
| hint_fixup = hint_info.relax_fixup; |
| code_seq = hint_info.relax_code_seq; |
| relax_code_size = hint_info.relax_code_size; |
| pattern_now = relocs_pattern; |
| |
| #ifdef NDS32_LINUX_TOOLCHAIN |
| /* prepare group relocation ID (number). */ |
| long group_id = 0; |
| if (key) |
| { |
| /* convert .relax_hint key to number */ |
| errno = 0; |
| group_id = strtol (key, NULL, 10); |
| if ((errno == ERANGE && (group_id == LONG_MAX || group_id == LONG_MIN)) |
| || (errno != 0 && group_id == 0)) |
| { |
| as_bad (_("Internal error: .relax_hint KEY is not a number!")); |
| goto restore; |
| } |
| } |
| #endif |
| |
| /* Insert relaxation. */ |
| exp.X_op = O_symbol; |
| |
| /* For each instruction in the hint group. */ |
| while (pattern_now) |
| { |
| if (count >= relax_code_size / 4) |
| count = 0; |
| |
| /* Choose the match fixup by instruction. */ |
| code_insn = CLEAN_REG (*(code_seq + count)); |
| if (!nds32_match_hint_insn (pattern_now->opcode, code_insn)) |
| { |
| /* Try search from head again */ |
| count = 0; |
| code_insn = CLEAN_REG (*(code_seq + count)); |
| |
| while (!nds32_match_hint_insn (pattern_now->opcode, code_insn)) |
| { |
| count++; |
| if (count >= relax_code_size / 4) |
| { |
| as_bad (_("Internal error: Relax hint (%s) error. %s: %s (%x)"), |
| key, |
| now_seg->name, |
| pattern_now->opcode->opcode, |
| pattern_now->opcode->value); |
| goto restore; |
| } |
| code_insn = CLEAN_REG (*(code_seq + count)); |
| } |
| } |
| fragP = pattern_now->frag; |
| sym = pattern_now->sym; |
| branch_offset = fragP->fr_offset; |
| offset = count * 4; |
| where = pattern_now->where; |
| /* Find the instruction map fix. */ |
| fixup_now = hint_fixup; |
| while (fixup_now->offset != offset) |
| { |
| fixup_now++; |
| if (fixup_now->size == 0) |
| break; |
| } |
| /* This element is without relaxation relocation. */ |
| if (fixup_now->size == 0) |
| { |
| pattern_now = pattern_now->next; |
| continue; |
| } |
| fixup_size = fixup_now->size; |
| |
| /* Insert all fixup. */ |
| pcrel = 0; |
| while (fixup_size != 0 && fixup_now->offset == offset) |
| { |
| /* Set the real instruction size in element. */ |
| fixup_size = pattern_now->opcode->isize; |
| pcrel = ((fixup_now->ramp & NDS32_PCREL) != 0) ? 1 : 0; |
| if (fixup_now->ramp & NDS32_FIX) |
| { |
| /* Convert original relocation. */ |
| pattern_now->fixP->fx_r_type = fixup_now->r_type ; |
| fixup_size = 0; |
| } |
| else if ((fixup_now->ramp & NDS32_PTR) != 0) |
| { |
| /* This relocation has to point to another instruction. Make |
| sure each resolved relocation has to be pointed. */ |
| pattern_temp = relocs_pattern; |
| /* All instruction in relax_table should be 32-bit. */ |
| hint_count = hint_info.relax_code_size / 4; |
| code_insn = CLEAN_REG (*(code_seq + hint_count - 1)); |
| while (pattern_temp) |
| { |
| /* Point to every resolved relocation. */ |
| if (nds32_match_hint_insn (pattern_temp->opcode, code_insn)) |
| { |
| ptr_offset = |
| pattern_temp->where - pattern_temp->frag->fr_literal; |
| exp.X_add_symbol = symbol_temp_new (now_seg, |
| pattern_temp->frag, |
| ptr_offset); |
| exp.X_add_number = 0; |
| fixP = |
| fix_new_exp (fragP, where - fragP->fr_literal, |
| fixup_size, &exp, 0, fixup_now->r_type); |
| fixP->fx_addnumber = fixP->fx_offset; |
| } |
| pattern_temp = pattern_temp->next; |
| } |
| fixup_size = 0; |
| } |
| else if (fixup_now->ramp & NDS32_ADDEND) |
| { |
| range = nds32_elf_sethi_range (relocs_pattern); |
| if (range == NDS32_LOADSTORE_NONE) |
| { |
| as_bad (_("Internal error: Range error. %s"), now_seg->name); |
| return; |
| } |
| exp.X_add_symbol = abs_section_sym; |
| exp.X_add_number = SET_ADDEND (4, 0, optimize, enable_16bit); |
| exp.X_add_number |= ((range & 0x3f) << 8); |
| } |
| else if ((fixup_now->ramp & NDS32_ABS) != 0) |
| { |
| /* This is a tag relocation. */ |
| exp.X_add_symbol = abs_section_sym; |
| exp.X_add_number = 0; |
| } |
| else if ((fixup_now->ramp & NDS32_INSN16) != 0) |
| { |
| if (!enable_16bit) |
| fixup_size = 0; |
| /* This is a tag relocation. */ |
| exp.X_add_symbol = abs_section_sym; |
| exp.X_add_number = 0; |
| } |
| else if ((fixup_now->ramp & NDS32_SYM) != 0) |
| { |
| /* For EMPTY relocation save the true symbol. */ |
| exp.X_add_symbol = hi_sym; |
| exp.X_add_number = hi_branch_offset; |
| } |
| else if (NDS32_SYM_DESC_MEM & fixup_now->ramp) |
| { |
| /* Do the same as NDS32_SYM. */ |
| exp.X_add_symbol = hi_sym; |
| exp.X_add_number = hi_branch_offset; |
| |
| /* Extra to NDS32_SYM. */ |
| /* Detect if DESC_FUNC relax type do apply. */ |
| if ((REG_GP == N32_RA5 (pattern_now->insn)) |
| || (REG_GP == N32_RB5 (pattern_now->insn))) |
| { |
| fixP = fix_new_exp (fragP, where - fragP->fr_literal, |
| fixup_size, &exp, pcrel, |
| BFD_RELOC_NDS32_TLS_DESC_FUNC); |
| fixP->fx_addnumber = fixP->fx_offset; |
| |
| fixup_size = 0; |
| } |
| /* Else do as usual. */ |
| } |
| else if (fixup_now->ramp & NDS32_PTR_PATTERN) |
| { |
| /* Find out PTR_RESOLVED code pattern. */ |
| nds32_relax_fixup_info_t *next_fixup = fixup_now + 1; |
| uint32_t resolved_pattern = 0; |
| while (next_fixup->offset) |
| { |
| if (next_fixup->r_type == BFD_RELOC_NDS32_PTR_RESOLVED) |
| { |
| uint32_t new_pattern = code_seq[next_fixup->offset >> 2]; |
| if (!resolved_pattern) |
| resolved_pattern = new_pattern; |
| else if (new_pattern != resolved_pattern) |
| { |
| as_warn (_("Multiple BFD_RELOC_NDS32_PTR_RESOLVED " |
| "patterns are not supported yet!")); |
| break; |
| } |
| } |
| ++next_fixup; |
| } |
| |
| /* Find matched code and insert fix-ups. */ |
| struct nds32_relocs_pattern *next_pattern = pattern_now->next; |
| /* This relocation has to point to another instruction. |
| Make sure each resolved relocation has to be pointed. */ |
| /* All instruction in relax_table should be 32-bit. */ |
| while (next_pattern) |
| { |
| uint32_t cur_pattern = GET_OPCODE (next_pattern->opcode->value); |
| if (cur_pattern == resolved_pattern) |
| { |
| ptr_offset = next_pattern->where |
| - next_pattern->frag->fr_literal; |
| exp.X_add_symbol = symbol_temp_new (now_seg, |
| next_pattern->frag, |
| ptr_offset); |
| exp.X_add_number = 0; |
| fixP = fix_new_exp (fragP, where - fragP->fr_literal, |
| fixup_size, &exp, 0, |
| fixup_now->r_type); |
| fixP->fx_addnumber = fixP->fx_offset; |
| } |
| next_pattern = next_pattern->next; |
| } |
| |
| fixup_size = 0; |
| } |
| else if (fixup_now->ramp & NDS32_PTR_MULTIPLE) |
| { |
| /* Find each PTR_RESOLVED pattern after PTR. */ |
| nds32_relax_fixup_info_t *next_fixup = fixup_now + 1; |
| while (next_fixup->offset) |
| { |
| if (next_fixup->r_type == BFD_RELOC_NDS32_PTR_RESOLVED) |
| { |
| uint32_t pattern = code_seq[next_fixup->offset >> 2]; |
| /* Find matched code to insert fix-ups. */ |
| struct nds32_relocs_pattern *next_insn = pattern_now->next; |
| while (next_insn) |
| { |
| uint32_t insn_pattern = GET_OPCODE (next_insn->opcode->value); |
| if (insn_pattern == pattern) |
| { |
| ptr_offset = next_insn->where |
| - next_insn->frag->fr_literal; |
| exp.X_add_symbol = symbol_temp_new (now_seg, |
| next_insn->frag, |
| ptr_offset); |
| exp.X_add_number = 0; |
| fixP = fix_new_exp (fragP, |
| where - fragP->fr_literal, |
| fixup_size, &exp, 0, |
| fixup_now->r_type); |
| fixP->fx_addnumber = fixP->fx_offset; |
| } |
| next_insn = next_insn->next; |
| } |
| } |
| ++next_fixup; |
| } |
| fixup_size = 0; |
| } |
| else |
| { |
| exp.X_add_symbol = sym; |
| exp.X_add_number = branch_offset; |
| } |
| |
| if (fixup_size != 0) |
| { |
| fixP = fix_new_exp (fragP, where - fragP->fr_literal, fixup_size, |
| &exp, pcrel, fixup_now->r_type); |
| fixP->fx_addnumber = fixP->fx_offset; |
| } |
| fixup_now++; |
| fixup_size = fixup_now->size; |
| } |
| |
| #ifdef NDS32_LINUX_TOOLCHAIN |
| /* Insert group relocation for each relax hint. */ |
| if (key) |
| { |
| exp.X_add_symbol = hi_sym; /* for eyes only */ |
| exp.X_add_number = group_id; |
| fixP = fix_new_exp (fragP, where - fragP->fr_literal, fixup_size, |
| &exp, pcrel, BFD_RELOC_NDS32_GROUP); |
| fixP->fx_addnumber = fixP->fx_offset; |
| } |
| #endif |
| |
| if (count < relax_code_size / 4) |
| count++; |
| pattern_now = pattern_now->next; |
| } |
| |
| restore: |
| now_seg = seg_bak; |
| frchain_now = frchain_bak; |
| } |
| |
| static int |
| nds32_elf_append_relax_relocs_traverse (void **slot, void *arg ATTRIBUTE_UNUSED) |
| { |
| string_tuple_t *tuple = *((string_tuple_t **) slot); |
| nds32_elf_append_relax_relocs (tuple->key, tuple->value); |
| return 1; |
| } |
| |
| |
| static void |
| nds32_str_tolower (const char *src, char *dest) |
| { |
| unsigned int i, len; |
| |
| len = strlen (src); |
| |
| for (i = 0; i < len; i++) |
| *(dest + i) = TOLOWER (*(src + i)); |
| |
| *(dest + i) = '\0'; |
| } |
| |
| /* Check instruction if it can be used for the baseline. */ |
| |
| static bool |
| nds32_check_insn_available (struct nds32_asm_insn insn, const char *str) |
| { |
| int attr = insn.attr & ATTR_ALL; |
| static int baseline_isa = 0; |
| char *s; |
| |
| s = xmalloc (strlen (str) + 1); |
| nds32_str_tolower (str, s); |
| if (verbatim |
| && (((insn.opcode->value == ALU2 (MTUSR) |
| || insn.opcode->value == ALU2 (MFUSR)) |
| && (strstr (s, "lc") |
| || strstr (s, "le") |
| || strstr (s, "lb"))) |
| || (insn.attr & NASM_ATTR_ZOL))) |
| { |
| as_bad (_("Not support instruction %s in verbatim."), str); |
| return false; |
| } |
| free (s); |
| |
| if (!enable_16bit && insn.opcode->isize == 2) |
| { |
| as_bad (_("16-bit instruction is disabled: %s."), str); |
| return false; |
| } |
| |
| /* No isa setting or all isa can use. */ |
| if (attr == 0 || attr == ATTR_ALL) |
| return true; |
| |
| if (baseline_isa == 0) |
| { |
| /* Map option baseline and instruction attribute. */ |
| switch (nds32_baseline) |
| { |
| case ISA_V2: |
| baseline_isa = ATTR (ISA_V2); |
| break; |
| case ISA_V3: |
| baseline_isa = ATTR (ISA_V3); |
| break; |
| case ISA_V3M: |
| baseline_isa = ATTR (ISA_V3M); |
| break; |
| } |
| } |
| |
| if ((baseline_isa & attr) == 0) |
| { |
| as_bad (_("Instruction %s not supported in the baseline."), str); |
| return false; |
| } |
| return true; |
| } |
| |
| /* Stub of machine dependent. */ |
| |
| void |
| md_assemble (char *str) |
| { |
| struct nds32_asm_insn insn; |
| char *out; |
| struct nds32_pseudo_opcode *popcode; |
| const struct nds32_field *fld = NULL; |
| fixS *fixP; |
| uint16_t insn_16; |
| struct nds32_relocs_pattern *relocs_temp; |
| struct nds32_relocs_group *group_temp; |
| fragS *fragP; |
| int label = label_exist; |
| static bool pseudo_hint = false; |
| |
| popcode = nds32_lookup_pseudo_opcode (str); |
| /* Note that we need to check 'verbatim' and |
| 'opcode->physical_op'. If the assembly content is generated by |
| compiler and this opcode is a physical instruction, there is no |
| need to perform pseudo instruction expansion/transformation. */ |
| if (popcode && !(verbatim && popcode->physical_op)) |
| { |
| /* Pseudo instruction is with relax_hint. */ |
| if (relaxing) |
| pseudo_hint = true; |
| pseudo_opcode = true; |
| nds32_pseudo_opcode_wrapper (str, popcode); |
| pseudo_opcode = false; |
| pseudo_hint = false; |
| nds32_elf_append_relax_relocs (NULL, relocs_list); |
| |
| /* Free relax_hint group list. */ |
| while (nds32_relax_hint_current) |
| { |
| group_temp = nds32_relax_hint_current->next; |
| free (nds32_relax_hint_current); |
| nds32_relax_hint_current = group_temp; |
| } |
| |
| /* Free pseudo list. */ |
| relocs_temp = relocs_list; |
| while (relocs_temp) |
| { |
| relocs_list = relocs_list->next; |
| free (relocs_temp); |
| relocs_temp = relocs_list; |
| } |
| |
| return; |
| } |
| |
| label_exist = 0; |
| insn.info = XNEW (expressionS); |
| asm_desc.result = NASM_OK; |
| nds32_assemble (&asm_desc, &insn, str); |
| |
| switch (asm_desc.result) |
| { |
| case NASM_ERR_UNKNOWN_OP: |
| as_bad (_("Unrecognized opcode, %s."), str); |
| return; |
| case NASM_ERR_SYNTAX: |
| as_bad (_("Incorrect syntax, %s."), str); |
| return; |
| case NASM_ERR_OPERAND: |
| as_bad (_("Unrecognized operand/register, %s."), str); |
| return; |
| case NASM_ERR_OUT_OF_RANGE: |
| as_bad (_("Operand out of range, %s."), str); |
| return; |
| case NASM_ERR_REG_REDUCED: |
| as_bad (_("Prohibited register used for reduced-register, %s."), str); |
| return; |
| case NASM_ERR_JUNK_EOL: |
| as_bad (_("Junk at end of line, %s."), str); |
| return; |
| } |
| |
| gas_assert (insn.opcode); |
| |
| nds32_set_elf_flags_by_insn (&insn); |
| |
| gas_assert (insn.opcode->isize == 4 || insn.opcode->isize == 2); |
| |
| if (!nds32_check_insn_available (insn, str)) |
| return; |
| |
| /* Make sure the beginning of text being 2-byte align. */ |
| nds32_adjust_label (1); |
| add_mapping_symbol (MAP_CODE, 0, 0); |
| fld = insn.field; |
| /* Try to allocate the max size to guarantee relaxable same branch |
| instructions in the same fragment. */ |
| frag_grow (NDS32_MAXCHAR); |
| fragP = frag_now; |
| |
| if (fld && (insn.attr & NASM_ATTR_BRANCH) |
| && (pseudo_opcode || (insn.opcode->value != INSN_JAL |
| && insn.opcode->value != INSN_J)) |
| && (!verbatim || pseudo_opcode)) |
| { |
| /* User assembly code branch relax for it. */ |
| /* If fld is not NULL, it is a symbol. */ |
| /* Branch must relax to proper pattern in user assembly code exclude |
| J and JAL. Keep these two in original type for users which wants |
| to keep their size be fixed. In general, assembler does not convert |
| instruction generated by compiler. But jump instruction may be |
| truncated in text virtual model. For workaround, compiler generate |
| pseudo jump to fix this issue currently. */ |
| |
| /* Get branch range type. */ |
| dwarf2_emit_insn (0); |
| enum nds32_br_range range_type; |
| expressionS *pexp = insn.info; |
| |
| range_type = get_range_type (fld); |
| |
| out = frag_var (rs_machine_dependent, NDS32_MAXCHAR, |
| 0, /* VAR is un-used. */ |
| range_type, /* SUBTYPE is used as range type. */ |
| pexp->X_add_symbol, pexp->X_add_number, 0); |
| |
| fragP->fr_fix += insn.opcode->isize; |
| fragP->tc_frag_data.opcode = insn.opcode; |
| fragP->tc_frag_data.insn = insn.insn; |
| if (insn.opcode->isize == 4) |
| bfd_putb32 (insn.insn, out); |
| else if (insn.opcode->isize == 2) |
| bfd_putb16 (insn.insn, out); |
| fragP->tc_frag_data.flag |= NDS32_FRAG_BRANCH; |
| |
| free (insn.info); |
| return; |
| /* md_convert_frag will insert relocations. */ |
| } |
| else if (!relaxing && enable_16bit && (optimize || optimize_for_space) |
| && ((!fld && !verbatim && insn.opcode->isize == 4 |
| && nds32_convert_32_to_16 (stdoutput, insn.insn, &insn_16, NULL)) |
| || (insn.opcode->isize == 2 |
| && nds32_convert_16_to_32 (stdoutput, insn.insn, NULL)))) |
| { |
| /* Record this one is relaxable. */ |
| expressionS *pexp = insn.info; |
| dwarf2_emit_insn (0); |
| if (fld) |
| { |
| out = frag_var (rs_machine_dependent, |
| 4, /* Max size is 32-bit instruction. */ |
| 0, /* VAR is un-used. */ |
| 0, pexp->X_add_symbol, pexp->X_add_number, 0); |
| fragP->tc_frag_data.flag |= NDS32_FRAG_RELAXABLE_BRANCH; |
| } |
| else |
| out = frag_var (rs_machine_dependent, |
| 4, /* Max size is 32-bit instruction. */ |
| 0, /* VAR is un-used. */ |
| 0, NULL, 0, NULL); |
| fragP->tc_frag_data.flag |= NDS32_FRAG_RELAXABLE; |
| fragP->tc_frag_data.opcode = insn.opcode; |
| fragP->tc_frag_data.insn = insn.insn; |
| fragP->fr_fix += 2; |
| |
| /* In original, we don't relax the instruction with label on it, |
| but this may cause some redundant nop16. Therefore, tag this |
| relaxable instruction and relax it carefully. */ |
| if (label) |
| fragP->tc_frag_data.flag |= NDS32_FRAG_LABEL; |
| |
| if (insn.opcode->isize == 4) |
| bfd_putb16 (insn_16, out); |
| else if (insn.opcode->isize == 2) |
| bfd_putb16 (insn.insn, out); |
| |
| free (insn.info); |
| return; |
| } |
| else if ((verbatim || !relaxing) && optimize && label) |
| { |
| /* This instruction is with label. */ |
| expressionS exp; |
| out = frag_var (rs_machine_dependent, insn.opcode->isize, |
| 0, 0, NULL, 0, NULL); |
| /* If this instruction is branch target, it is not relaxable. */ |
| fragP->tc_frag_data.flag = NDS32_FRAG_LABEL; |
| fragP->tc_frag_data.opcode = insn.opcode; |
| fragP->tc_frag_data.insn = insn.insn; |
| fragP->fr_fix += insn.opcode->isize; |
| if (insn.opcode->isize == 4) |
| { |
| exp.X_op = O_symbol; |
| exp.X_add_symbol = abs_section_sym; |
| exp.X_add_number = 0; |
| fixP = fix_new_exp (fragP, fragP->fr_fix - 4, 0, &exp, |
| 0, BFD_RELOC_NDS32_LABEL); |
| if (!verbatim) |
| fragP->tc_frag_data.flag = NDS32_FRAG_ALIGN; |
| } |
| } |
| else |
| out = frag_more (insn.opcode->isize); |
| |
| if (insn.opcode->isize == 4) |
| bfd_putb32 (insn.insn, out); |
| else if (insn.opcode->isize == 2) |
| bfd_putb16 (insn.insn, out); |
| |
| dwarf2_emit_insn (insn.opcode->isize); |
| |
| /* Compiler generating code and user assembly pseudo load-store, insert |
| fixup here. */ |
| expressionS *pexp = insn.info; |
| fixP = nds32_elf_record_fixup_exp (fragP, str, fld, pexp, out, &insn); |
| /* Build relaxation pattern when relaxing is enable. */ |
| if (relaxing) |
| nds32_elf_build_relax_relation (fixP, pexp, out, &insn, fragP, fld, |
| pseudo_hint); |
| |
| free (insn.info); |
| } |
| |
| /* md_macro_start */ |
| |
| void |
| nds32_macro_start (void) |
| { |
| } |
| |
| /* md_macro_info */ |
| |
| void |
| nds32_macro_info (void *info ATTRIBUTE_UNUSED) |
| { |
| } |
| |
| /* md_macro_end */ |
| |
| void |
| nds32_macro_end (void) |
| { |
| } |
| |
| /* GAS will call this function with one argument, an expressionS pointer, for |
| any expression that can not be recognized. When the function is called, |
| input_line_pointer will point to the start of the expression. */ |
| |
| void |
| md_operand (expressionS *expressionP) |
| { |
| if (*input_line_pointer == '#') |
| { |
| input_line_pointer++; |
| expression (expressionP); |
| } |
| } |
| |
| /* GAS will call this function for each section at the end of the assembly, to |
| permit the CPU back end to adjust the alignment of a section. The function |
| must take two arguments, a segT for the section and a valueT for the size of |
| the section, and return a valueT for the rounded size. */ |
| |
| valueT |
| md_section_align (segT segment, valueT size) |
| { |
| int align = bfd_section_alignment (segment); |
| |
| return ((size + (1 << align) - 1) & ((valueT) -1 << align)); |
| } |
| |
| /* GAS will call this function when a symbol table lookup fails, before it |
| creates a new symbol. Typically this would be used to supply symbols whose |
| name or value changes dynamically, possibly in a context sensitive way. |
| Predefined symbols with fixed values, such as register names or condition |
| codes, are typically entered directly into the symbol table when md_begin |
| is called. One argument is passed, a char * for the symbol. */ |
| |
| symbolS * |
| md_undefined_symbol (char *name ATTRIBUTE_UNUSED) |
| { |
| return NULL; |
| } |
| |
| static long |
| nds32_calc_branch_offset (segT segment, fragS *fragP, |
| long stretch ATTRIBUTE_UNUSED, |
| relax_info_t *relax_info, |
| enum nds32_br_range branch_range_type) |
| { |
| struct nds32_opcode *opcode = fragP->tc_frag_data.opcode; |
| symbolS *branch_symbol = fragP->fr_symbol; |
| offsetT branch_offset = fragP->fr_offset; |
| offsetT branch_target_address; |
| offsetT branch_insn_address; |
| long offset = 0; |
| |
| if ((S_GET_SEGMENT (branch_symbol) != segment) |
| || S_IS_WEAK (branch_symbol)) |
| { |
| /* The symbol is not in the SEGMENT. It could be far far away. */ |
| offset = 0x80000000; |
| } |
| else |
| { |
| /* Calculate symbol-to-instruction offset. */ |
| branch_target_address = S_GET_VALUE (branch_symbol) + branch_offset; |
| /* If the destination symbol is beyond current frag address, |
| STRETCH will take effect to symbol's position. */ |
| if (S_GET_VALUE (branch_symbol) > fragP->fr_address) |
| branch_target_address += stretch; |
| |
| branch_insn_address = fragP->fr_address + fragP->fr_fix; |
| branch_insn_address -= opcode->isize; |
| |
| /* Update BRANCH_INSN_ADDRESS to relaxed position. */ |
| branch_insn_address += (relax_info->relax_code_size[branch_range_type] |
| - relax_info->relax_branch_isize[branch_range_type]); |
| |
| offset = branch_target_address - branch_insn_address; |
| } |
| |
| return offset; |
| } |
| |
| static enum nds32_br_range |
| nds32_convert_to_range_type (long offset) |
| { |
| enum nds32_br_range range_type; |
| |
| if (-(0x100) <= offset && offset < 0x100) /* 256 bytes */ |
| range_type = BR_RANGE_S256; |
| else if (-(0x4000) <= offset && offset < 0x4000) /* 16K bytes */ |
| range_type = BR_RANGE_S16K; |
| else if (-(0x10000) <= offset && offset < 0x10000) /* 64K bytes */ |
| range_type = BR_RANGE_S64K; |
| else if (-(0x1000000) <= offset && offset < 0x1000000) /* 16M bytes */ |
| range_type = BR_RANGE_S16M; |
| else /* 4G bytes */ |
| range_type = BR_RANGE_U4G; |
| |
| return range_type; |
| } |
| |
| /* Set instruction register mask. */ |
| |
| static void |
| nds32_elf_get_set_cond (relax_info_t *relax_info, int offset, uint32_t *insn, |
| uint32_t ori_insn, int range) |
| { |
| nds32_cond_field_t *cond_fields = relax_info->cond_field; |
| nds32_cond_field_t *code_seq_cond = relax_info->relax_code_condition[range]; |
| uint32_t mask; |
| int i = 0; |
| |
| /* The instruction has conditions. Collect condition values. */ |
| while (code_seq_cond[i].bitmask != 0) |
| { |
| if (offset == code_seq_cond[i].offset) |
| { |
| mask = (ori_insn >> cond_fields[i].bitpos) & cond_fields[i].bitmask; |
| /* Sign extend. */ |
| if (cond_fields[i].signed_extend) |
| mask = (mask ^ ((cond_fields[i].bitmask + 1) >> 1)) - |
| ((cond_fields[i].bitmask + 1) >> 1); |
| *insn |= (mask & code_seq_cond[i].bitmask) << code_seq_cond[i].bitpos; |
| } |
| i++; |
| } |
| } |
| |
| static int |
| nds32_relax_branch_instructions (segT segment, fragS *fragP, |
| long stretch ATTRIBUTE_UNUSED, |
| int init) |
| { |
| enum nds32_br_range branch_range_type; |
| struct nds32_opcode *opcode = fragP->tc_frag_data.opcode; |
| long offset = 0; |
| enum nds32_br_range real_range_type; |
| int adjust = 0; |
| relax_info_t *relax_info; |
| int diff = 0; |
| int i, j, k; |
| int code_seq_size; |
| uint32_t *code_seq; |
| uint32_t insn; |
| int insn_size; |
| int code_seq_offset; |
| |
| /* Replace with gas_assert (fragP->fr_symbol != NULL); */ |
| if (fragP->fr_symbol == NULL) |
| return adjust; |
| |
| /* If frag_var is not enough room, the previous frag is fr_full and with |
| opcode. The new one is rs_dependent but without opcode. */ |
| if (opcode == NULL) |
| return adjust; |
| |
| /* Use U4G mode for b and bal in verbatim mode because lto may combine |
| functions into a file. And order the file in the last when linking. |
| Once there is multiple definition, the same function will be kicked. |
| This may cause relocation truncated error. */ |
| if (verbatim && !nds32_pic |
| && (strcmp (opcode->opcode, "j") == 0 |
| || strcmp (opcode->opcode, "jal") == 0)) |
| { |
| fragP->fr_subtype = BR_RANGE_U4G; |
| if (init) |
| return 8; |
| else |
| return 0; |
| } |
| |
| relax_info = str_hash_find (nds32_relax_info_hash, opcode->opcode); |
| |
| if (relax_info == NULL) |
| return adjust; |
| |
| if (init) |
| { |
| branch_range_type = relax_info->br_range; |
| i = BR_RANGE_S256; |
| } |
| else |
| { |
| branch_range_type = fragP->fr_subtype; |
| i = branch_range_type; |
| } |
| |
| offset = nds32_calc_branch_offset (segment, fragP, stretch, |
| relax_info, branch_range_type); |
| |
| real_range_type = nds32_convert_to_range_type (offset); |
| |
| /* If actual range is equal to instruction jump range, do nothing. */ |
| if (real_range_type == branch_range_type) |
| { |
| fragP->fr_subtype = real_range_type; |
| return adjust; |
| } |
| |
| /* Find out proper relaxation code sequence. */ |
| for (; i < BR_RANGE_NUM; i++) |
| { |
| if (real_range_type <= (unsigned int) i) |
| { |
| if (init) |
| diff = relax_info->relax_code_size[i] - opcode->isize; |
| else if (real_range_type < (unsigned int) i) |
| diff = relax_info->relax_code_size[real_range_type] |
| - relax_info->relax_code_size[branch_range_type]; |
| else |
| diff = relax_info->relax_code_size[i] |
| - relax_info->relax_code_size[branch_range_type]; |
| |
| /* If the instruction could be converted to 16-bits, |
| minus the difference. */ |
| code_seq_offset = 0; |
| j = 0; |
| k = 0; |
| code_seq_size = relax_info->relax_code_size[i]; |
| code_seq = relax_info->relax_code_seq[i]; |
| while (code_seq_offset < code_seq_size) |
| { |
| insn = code_seq[j]; |
| if (insn & 0x80000000) /* 16-bits instruction. */ |
| { |
| insn_size = 2; |
| } |
| else /* 32-bits instruction. */ |
| { |
| insn_size = 4; |
| |
| while (relax_info->relax_fixup[i][k].size !=0 |
| && relax_info->relax_fixup[i][k].offset < code_seq_offset) |
| k++; |
| } |
| |
| code_seq_offset += insn_size; |
| j++; |
| } |
| |
| /* Update fr_subtype to new NDS32_BR_RANGE. */ |
| fragP->fr_subtype = real_range_type; |
| break; |
| } |
| } |
| |
| return diff + adjust; |
| } |
| |
| /* Adjust relaxable frag till current frag. */ |
| |
| static int |
| nds32_adjust_relaxable_frag (fragS *startP, fragS *fragP) |
| { |
| int adj; |
| if (startP->tc_frag_data.flag & NDS32_FRAG_RELAXED) |
| adj = -2; |
| else |
| adj = 2; |
| |
| startP->tc_frag_data.flag ^= NDS32_FRAG_RELAXED; |
| |
| while (startP) |
| { |
| startP = startP->fr_next; |
| if (startP) |
| { |
| startP->fr_address += adj; |
| if (startP == fragP) |
| break; |
| } |
| } |
| return adj; |
| } |
| |
| static addressT |
| nds32_get_align (addressT address, int align) |
| { |
| addressT mask, new_address; |
| |
| mask = ~((addressT) (~0) << align); |
| new_address = (address + mask) & (~mask); |
| return (new_address - address); |
| } |
| |
| /* Check the prev_frag is legal. */ |
| static void |
| invalid_prev_frag (fragS * fragP, fragS **prev_frag, bool relax) |
| { |
| addressT address; |
| fragS *frag_start = *prev_frag; |
| |
| if (!frag_start || !relax) |
| return; |
| |
| if (frag_start->last_fr_address >= fragP->last_fr_address) |
| { |
| *prev_frag = NULL; |
| return; |
| } |
| |
| fragS *frag_t = *prev_frag; |
| while (frag_t != fragP) |
| { |
| if (frag_t->fr_type == rs_align |
| || frag_t->fr_type == rs_align_code |
| || frag_t->fr_type == rs_align_test) |
| { |
| /* Relax instruction can not walk across label. */ |
| if (frag_t->tc_frag_data.flag & NDS32_FRAG_LABEL) |
| { |
| prev_frag = NULL; |
| return; |
| } |
| /* Relax previous relaxable to align rs_align frag. */ |
| address = frag_t->fr_address + frag_t->fr_fix; |
| addressT offset = nds32_get_align (address, (int) frag_t->fr_offset); |
| if (offset & 0x2) |
| { |
| /* If there is label on the prev_frag, check if it is aligned. */ |
| if (!((*prev_frag)->tc_frag_data.flag & NDS32_FRAG_LABEL) |
| || (((*prev_frag)->fr_address + (*prev_frag)->fr_fix - 2 ) |
| & 0x2) == 0) |
| nds32_adjust_relaxable_frag (*prev_frag, frag_t); |
| } |
| *prev_frag = NULL; |
| return; |
| } |
| frag_t = frag_t->fr_next; |
| } |
| |
| if (fragP->tc_frag_data.flag & NDS32_FRAG_ALIGN) |
| { |
| address = fragP->fr_address; |
| addressT offset = nds32_get_align (address, 2); |
| if (offset & 0x2) |
| { |
| /* If there is label on the prev_frag, check if it is aligned. */ |
| if (!((*prev_frag)->tc_frag_data.flag & NDS32_FRAG_LABEL) |
| || (((*prev_frag)->fr_address + (*prev_frag)->fr_fix - 2 ) |
| & 0x2) == 0) |
| nds32_adjust_relaxable_frag (*prev_frag, fragP); |
| } |
| *prev_frag = NULL; |
| return; |
| } |
| } |
| |
| /* md_relax_frag */ |
| |
| int |
| nds32_relax_frag (segT segment, fragS *fragP, long stretch ATTRIBUTE_UNUSED) |
| { |
| /* Currently, there are two kinds of relaxation in nds32 assembler. |
| 1. relax for branch |
| 2. relax for 32-bits to 16-bits */ |
| |
| static fragS *prev_frag = NULL; |
| int adjust = 0; |
| |
| invalid_prev_frag (fragP, &prev_frag, true); |
| |
| if (fragP->tc_frag_data.flag & NDS32_FRAG_BRANCH) |
| adjust = nds32_relax_branch_instructions (segment, fragP, stretch, 0); |
| if (fragP->tc_frag_data.flag & NDS32_FRAG_LABEL) |
| prev_frag = NULL; |
| if (fragP->tc_frag_data.flag & NDS32_FRAG_RELAXABLE |
| && (fragP->tc_frag_data.flag & NDS32_FRAG_RELAXED) == 0) |
| /* Here is considered relaxed case originally. But it may cause |
| an endless loop when relaxing. Once the instruction is relaxed, |
| it can not be undone. */ |
| prev_frag = fragP; |
| |
| return adjust; |
| } |
| |
| /* This function returns an initial guess of the length by which a fragment |
| must grow to hold a branch to reach its destination. Also updates |
| fr_type/fr_subtype as necessary. |
| |
| It is called just before doing relaxation. Any symbol that is now undefined |
| will not become defined. The guess for fr_var is ACTUALLY the growth beyond |
| fr_fix. Whatever we do to grow fr_fix or fr_var contributes to our returned |
| value. Although it may not be explicit in the frag, pretend fr_var starts |
| with a 0 value. */ |
| |
| int |
| md_estimate_size_before_relax (fragS *fragP, segT segment) |
| { |
| /* Currently, there are two kinds of relaxation in nds32 assembler. |
| 1. relax for branch |
| 2. relax for 32-bits to 16-bits */ |
| |
| /* Save previous relaxable frag. */ |
| static fragS *prev_frag = NULL; |
| int adjust = 0; |
| |
| invalid_prev_frag (fragP, &prev_frag, false); |
| |
| if (fragP->tc_frag_data.flag & NDS32_FRAG_BRANCH) |
| adjust = nds32_relax_branch_instructions (segment, fragP, 0, 1); |
| if (fragP->tc_frag_data.flag & NDS32_FRAG_LABEL) |
| prev_frag = NULL; |
| if (fragP->tc_frag_data.flag & NDS32_FRAG_RELAXED) |
| adjust = 2; |
| else if (fragP->tc_frag_data.flag & NDS32_FRAG_RELAXABLE) |
| prev_frag = fragP; |
| |
| return adjust; |
| } |
| |
| /* GAS will call this for each rs_machine_dependent fragment. The instruction |
| is completed using the data from the relaxation pass. It may also create any |
| necessary relocations. |
| |
| *FRAGP has been relaxed to its final size, and now needs to have the bytes |
| inside it modified to conform to the new size. It is called after relaxation |
| is finished. |
| |
| fragP->fr_type == rs_machine_dependent. |
| fragP->fr_subtype is the subtype of what the address relaxed to. */ |
| |
| void |
| md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec, fragS *fragP) |
| { |
| /* Convert branch relaxation instructions. */ |
| symbolS *branch_symbol = fragP->fr_symbol; |
| offsetT branch_offset = fragP->fr_offset; |
| enum nds32_br_range branch_range_type = fragP->fr_subtype; |
| struct nds32_opcode *opcode = fragP->tc_frag_data.opcode; |
| uint32_t origin_insn = fragP->tc_frag_data.insn; |
| relax_info_t *relax_info; |
| char *fr_buffer; |
| int fr_where; |
| int addend ATTRIBUTE_UNUSED; |
| offsetT branch_target_address, branch_insn_address; |
| expressionS exp; |
| fixS *fixP; |
| uint32_t *code_seq; |
| uint32_t insn; |
| int code_size, insn_size, offset, fixup_size; |
| int buf_offset, pcrel; |
| int i, k; |
| uint16_t insn_16; |
| nds32_relax_fixup_info_t fixup_info[MAX_RELAX_FIX]; |
| /* Save the 1st instruction is converted to 16 bit or not. */ |
| unsigned int branch_size; |
| enum bfd_reloc_code_real final_r_type; |
| |
| /* Replace with gas_assert (branch_symbol != NULL); */ |
| if (branch_symbol == NULL && !(fragP->tc_frag_data.flag & NDS32_FRAG_RELAXED)) |
| return; |
| |
| /* If frag_var is not enough room, the previous frag is fr_full and with |
| opcode. The new one is rs_dependent but without opcode. */ |
| if (opcode == NULL) |
| return; |
| |
| if (fragP->tc_frag_data.flag & NDS32_FRAG_RELAXABLE_BRANCH) |
| { |
| relax_info = str_hash_find (nds32_relax_info_hash, opcode->opcode); |
| |
| if (relax_info == NULL) |
| return; |
| |
| i = BR_RANGE_S256; |
| while (i < BR_RANGE_NUM |
| && relax_info->relax_code_size[i] |
| != (fragP->tc_frag_data.flag & NDS32_FRAG_RELAXED ? 4 : 2)) |
| i++; |
| |
| if (i >= BR_RANGE_NUM) |
| as_bad ("Internal error: Cannot find relocation of" |
| "relaxable branch."); |
| |
| exp.X_op = O_symbol; |
| exp.X_add_symbol = branch_symbol; |
| exp.X_add_number = branch_offset; |
| pcrel = ((relax_info->relax_fixup[i][0].ramp & NDS32_PCREL) != 0) ? 1 : 0; |
| fr_where = fragP->fr_fix - 2; |
| fixP = fix_new_exp (fragP, fr_where, relax_info->relax_fixup[i][0].size, |
| &exp, pcrel, relax_info->relax_fixup[i][0].r_type); |
| fixP->fx_addnumber = fixP->fx_offset; |
| |
| if (fragP->tc_frag_data.flag & NDS32_FRAG_RELAXED) |
| { |
| insn_16 = fragP->tc_frag_data.insn; |
| nds32_convert_16_to_32 (stdoutput, insn_16, &insn); |
| fr_buffer = fragP->fr_literal + fr_where; |
| fragP->fr_fix += 2; |
| exp.X_op = O_symbol; |
| exp.X_add_symbol = abs_section_sym; |
| exp.X_add_number = 0; |
| fix_new_exp (fragP, fr_where, 4, |
| &exp, 0, BFD_RELOC_NDS32_INSN16); |
| number_to_chars_bigendian (fr_buffer, insn, 4); |
| } |
| } |
| else if (fragP->tc_frag_data.flag & NDS32_FRAG_RELAXED) |
| { |
| if (fragP->tc_frag_data.opcode->isize == 2) |
| { |
| insn_16 = fragP->tc_frag_data.insn; |
| nds32_convert_16_to_32 (stdoutput, insn_16, &insn); |
| } |
| else |
| insn = fragP->tc_frag_data.insn; |
| fragP->fr_fix += 2; |
| fr_where = fragP->fr_fix - 4; |
| fr_buffer = fragP->fr_literal + fr_where; |
| exp.X_op = O_symbol; |
| exp.X_add_symbol = abs_section_sym; |
| exp.X_add_number = 0; |
| fix_new_exp (fragP, fr_where, 4, &exp, 0, |
| BFD_RELOC_NDS32_INSN16); |
| number_to_chars_bigendian (fr_buffer, insn, 4); |
| } |
| else if (fragP->tc_frag_data.flag & NDS32_FRAG_BRANCH) |
| { |
| /* Branch instruction adjust and append relocations. */ |
| relax_info = str_hash_find (nds32_relax_info_hash, opcode->opcode); |
| |
| if (relax_info == NULL) |
| return; |
| |
| fr_where = fragP->fr_fix - opcode->isize; |
| fr_buffer = fragP->fr_literal + fr_where; |
| |
| if ((S_GET_SEGMENT (branch_symbol) != sec) |
| || S_IS_WEAK (branch_symbol)) |
| { |
| if (fragP->fr_offset & 3) |
| as_warn (_("Addend to unresolved symbol is not on word boundary.")); |
| addend = 0; |
| } |
| else |
| { |
| /* Calculate symbol-to-instruction offset. */ |
| branch_target_address = S_GET_VALUE (branch_symbol) + branch_offset; |
| branch_insn_address = fragP->fr_address + fr_where; |
| addend = (branch_target_address - branch_insn_address) >> 1; |
| } |
| |
| code_size = relax_info->relax_code_size[branch_range_type]; |
| code_seq = relax_info->relax_code_seq[branch_range_type]; |
| |
| memcpy (fixup_info, relax_info->relax_fixup[branch_range_type], |
| sizeof (fixup_info)); |
| |
| /* Fill in frag. */ |
| i = 0; |
| k = 0; |
| offset = 0; /* code_seq offset */ |
| buf_offset = 0; /* fr_buffer offset */ |
| while (offset < code_size) |
| { |
| insn = code_seq[i]; |
| if (insn & 0x80000000) /* 16-bits instruction. */ |
| { |
| insn = (insn >> 16) & 0xFFFF; |
| insn_size = 2; |
| } |
| else /* 32-bits instruction. */ |
| { |
| insn_size = 4; |
| } |
| |
| nds32_elf_get_set_cond (relax_info, offset, &insn, |
| origin_insn, branch_range_type); |
| |
| /* Try to convert to 16-bits instruction. Currently, only the first |
| instruction in pattern can be converted. EX: bnez sethi ori jr, |
| only bnez can be converted to 16 bit and ori can't. */ |
| |
| while (fixup_info[k].size != 0 |
| && relax_info->relax_fixup[branch_range_type][k].offset < offset) |
| k++; |
| |
| number_to_chars_bigendian (fr_buffer + buf_offset, insn, insn_size); |
| buf_offset += insn_size; |
| |
| offset += insn_size; |
| i++; |
| } |
| |
| /* Set up fixup. */ |
| exp.X_op = O_symbol; |
| |
| for (i = 0; fixup_info[i].size != 0; i++) |
| { |
| fixup_size = fixup_info[i].size; |
| pcrel = ((fixup_info[i].ramp & NDS32_PCREL) != 0) ? 1 : 0; |
| |
| if ((fixup_info[i].ramp & NDS32_CREATE_LABEL) != 0) |
| { |
| /* This is a reverse branch. */ |
| exp.X_add_symbol = symbol_temp_new (sec, fragP->fr_next, 0); |
| exp.X_add_number = 0; |
| } |
| else if ((fixup_info[i].ramp & NDS32_PTR) != 0) |
| { |
| /* This relocation has to point to another instruction. */ |
| branch_size = fr_where + code_size - 4; |
| exp.X_add_symbol = symbol_temp_new (sec, fragP, branch_size); |
| exp.X_add_number = 0; |
| } |
| else if ((fixup_info[i].ramp & NDS32_ABS) != 0) |
| { |
| /* This is a tag relocation. */ |
| exp.X_add_symbol = abs_section_sym; |
| exp.X_add_number = 0; |
| } |
| else if ((fixup_info[i].ramp & NDS32_INSN16) != 0) |
| { |
| if (!enable_16bit) |
| continue; |
| /* This is a tag relocation. */ |
| exp.X_add_symbol = abs_section_sym; |
| exp.X_add_number = 0; |
| } |
| else |
| { |
| exp.X_add_symbol = branch_symbol; |
| exp.X_add_number = branch_offset; |
| } |
| |
| if (fixup_info[i].r_type != 0) |
| { |
| final_r_type = fixup_info[i].r_type; |
| fixP = fix_new_exp (fragP, fr_where + fixup_info[i].offset, |
| fixup_size, &exp, pcrel, |
| final_r_type); |
| fixP->fx_addnumber = fixP->fx_offset; |
| } |
| } |
| |
| fragP->fr_fix = fr_where + buf_offset; |
| } |
| } |
| |
| /* tc_frob_file_before_fix */ |
| |
| void |
| nds32_frob_file_before_fix (void) |
| { |
| } |
| |
| static bool |
| nds32_relaxable_section (asection *sec) |
| { |
| return ((sec->flags & SEC_DEBUGGING) == 0 |
| && strcmp (sec->name, ".eh_frame") != 0); |
| } |
| |
| /* TC_FORCE_RELOCATION */ |
| int |
| nds32_force_relocation (fixS * fix) |
| { |
| switch (fix->fx_r_type) |
| { |
| case BFD_RELOC_NDS32_INSN16: |
| case BFD_RELOC_NDS32_LABEL: |
| case BFD_RELOC_NDS32_LONGCALL1: |
| case BFD_RELOC_NDS32_LONGCALL2: |
| case BFD_RELOC_NDS32_LONGCALL3: |
| case BFD_RELOC_NDS32_LONGJUMP1: |
| case BFD_RELOC_NDS32_LONGJUMP2: |
| case BFD_RELOC_NDS32_LONGJUMP3: |
| case BFD_RELOC_NDS32_LOADSTORE: |
| case BFD_RELOC_NDS32_9_FIXED: |
| case BFD_RELOC_NDS32_15_FIXED: |
| case BFD_RELOC_NDS32_17_FIXED: |
| case BFD_RELOC_NDS32_25_FIXED: |
| case BFD_RELOC_NDS32_9_PCREL: |
| case BFD_RELOC_NDS32_15_PCREL: |
| case BFD_RELOC_NDS32_17_PCREL: |
| case BFD_RELOC_NDS32_WORD_9_PCREL: |
| case BFD_RELOC_NDS32_10_UPCREL: |
| case BFD_RELOC_NDS32_25_PCREL: |
| case BFD_RELOC_NDS32_MINUEND: |
| case BFD_RELOC_NDS32_SUBTRAHEND: |
| return 1; |
| |
| case BFD_RELOC_8: |
| case BFD_RELOC_16: |
| case BFD_RELOC_32: |
| case BFD_RELOC_NDS32_DIFF_ULEB128: |
| /* Linker should handle difference between two symbol. */ |
| return fix->fx_subsy != NULL |
| && nds32_relaxable_section (S_GET_SEGMENT (fix->fx_addsy)); |
| case BFD_RELOC_64: |
| if (fix->fx_subsy) |
| as_bad ("Double word for difference between two symbols " |
| "is not supported across relaxation."); |
| default: |
| ; |
| } |
| |
| if (generic_force_reloc (fix)) |
| return 1; |
| |
| return fix->fx_pcrel; |
| } |
| |
| /* TC_VALIDATE_FIX_SUB */ |
| |
| int |
| nds32_validate_fix_sub (fixS *fix, segT add_symbol_segment) |
| { |
| segT sub_symbol_segment; |
| |
| /* This code is referred from Xtensa. Check their implementation for |
| details. */ |
| |
| /* Make sure both symbols are in the same segment, and that segment is |
| "normal" and relaxable. */ |
| sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy); |
| return (sub_symbol_segment == add_symbol_segment |
| && add_symbol_segment != undefined_section); |
| } |
| |
| 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); |
| } |
| |
| /* This function is called to convert an ASCII string into a floating point |
| value in format used by the CPU. */ |
| |
| const char * |
| md_atof (int type, char *litP, int *sizeP) |
| { |
| int i; |
| int prec; |
| LITTLENUM_TYPE words[MAX_LITTLENUMS]; |
| char *t; |
| |
| switch (type) |
| { |
| case 'f': |
| case 'F': |
| case 's': |
| case 'S': |
| prec = 2; |
| break; |
| case 'd': |
| case 'D': |
| case 'r': |
| case 'R': |
| 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 * sizeof (LITTLENUM_TYPE); |
| |
| if (target_big_endian) |
| { |
| for (i = 0; i < prec; i++) |
| { |
| md_number_to_chars (litP, (valueT) words[i], |
| sizeof (LITTLENUM_TYPE)); |
| litP += sizeof (LITTLENUM_TYPE); |
| } |
| } |
| else |
| { |
| for (i = prec - 1; i >= 0; i--) |
| { |
| md_number_to_chars (litP, (valueT) words[i], |
| sizeof (LITTLENUM_TYPE)); |
| litP += sizeof (LITTLENUM_TYPE); |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* md_elf_section_change_hook */ |
| |
| void |
| nds32_elf_section_change_hook (void) |
| { |
| } |
| |
| /* md_cleanup */ |
| |
| void |
| nds32_cleanup (void) |
| { |
| } |
| |
| /* This function is used to scan leb128 subtraction expressions, |
| and insert fixups for them. |
| |
| e.g., .leb128 .L1 - .L0 |
| |
| These expressions are heavily used in debug information or |
| exception tables. Because relaxation will change code size, |
| we must resolve them in link time. */ |
| |
| static void |
| nds32_insert_leb128_fixes (bfd *abfd ATTRIBUTE_UNUSED, |
| asection *sec, void *xxx ATTRIBUTE_UNUSED) |
| { |
| segment_info_type *seginfo = seg_info (sec); |
| struct frag *fragP; |
| |
| subseg_set (sec, 0); |
| |
| for (fragP = seginfo->frchainP->frch_root; |
| fragP; fragP = fragP->fr_next) |
| { |
| expressionS *exp; |
| |
| /* Only unsigned leb128 can be handle. */ |
| if (fragP->fr_type != rs_leb128 || fragP->fr_subtype != 0 |
| || fragP->fr_symbol == NULL) |
| continue; |
| |
| exp = symbol_get_value_expression (fragP->fr_symbol); |
| |
| if (exp->X_op != O_subtract) |
| continue; |
| |
| fix_new_exp (fragP, fragP->fr_fix, 0, |
| exp, 0, BFD_RELOC_NDS32_DIFF_ULEB128); |
| } |
| } |
| |
| static void |
| nds32_insert_relax_entry (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, |
| void *xxx ATTRIBUTE_UNUSED) |
| { |
| segment_info_type *seginfo; |
| fragS *fragP; |
| fixS *fixP; |
| expressionS exp; |
| fixS *fixp; |
| |
| seginfo = seg_info (sec); |
| if (!seginfo || !symbol_rootP || !subseg_text_p (sec) || sec->size == 0) |
| return; |
| |
| for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next) |
| if (!fixp->fx_done) |
| break; |
| |
| if (!fixp && !verbatim && ict_flag == ICT_NONE) |
| return; |
| |
| subseg_change (sec, 0); |
| |
| /* Set RELAX_ENTRY flags for linker. */ |
| fragP = seginfo->frchainP->frch_root; |
| exp.X_op = O_symbol; |
| exp.X_add_symbol = abs_section_sym; |
| exp.X_add_number = 0; |
| if (!enable_relax_relocs) |
| exp.X_add_number |= R_NDS32_RELAX_ENTRY_DISABLE_RELAX_FLAG; |
| else |
| { |
| /* These flags are only enabled when global relax is enabled. |
| Maybe we can check DISABLE_RELAX_FLAG at link-time, |
| so we set them anyway. */ |
| if (verbatim) |
| exp.X_add_number |= R_NDS32_RELAX_ENTRY_VERBATIM_FLAG; |
| if (ict_flag == ICT_SMALL) |
| exp.X_add_number |= R_NDS32_RELAX_ENTRY_ICT_SMALL; |
| else if (ict_flag == ICT_LARGE) |
| exp.X_add_number |= R_NDS32_RELAX_ENTRY_ICT_LARGE; |
| } |
| if (optimize) |
| exp.X_add_number |= R_NDS32_RELAX_ENTRY_OPTIMIZE_FLAG; |
| if (optimize_for_space) |
| exp.X_add_number |= R_NDS32_RELAX_ENTRY_OPTIMIZE_FOR_SPACE_FLAG; |
| |
| fixP = fix_new_exp (fragP, 0, 0, &exp, 0, BFD_RELOC_NDS32_RELAX_ENTRY); |
| fixP->fx_no_overflow = 1; |
| } |
| |
| /* Analysis relax hint and insert suitable relocation pattern. */ |
| |
| static void |
| nds32_elf_analysis_relax_hint (void) |
| { |
| htab_traverse_noresize (nds32_hint_hash, |
| nds32_elf_append_relax_relocs_traverse, NULL); |
| } |
| |
| static void |
| nds32_elf_insert_final_frag (void) |
| { |
| struct frchain *frchainP; |
| asection *s; |
| fragS *fragP; |
| |
| if (!optimize) |
| return; |
| |
| for (s = stdoutput->sections; s; s = s->next) |
| { |
| segment_info_type *seginfo = seg_info (s); |
| if (!seginfo) |
| continue; |
| |
| for (frchainP = seginfo->frchainP; frchainP != NULL; |
| frchainP = frchainP->frch_next) |
| { |
| subseg_set (s, frchainP->frch_subseg); |
| |
| if (subseg_text_p (now_seg)) |
| { |
| fragP = frag_now; |
| frag_var (rs_machine_dependent, 2, /* Max size. */ |
| 0, /* VAR is un-used. */ 0, NULL, 0, NULL); |
| fragP->tc_frag_data.flag |= NDS32_FRAG_FINAL; |
| } |
| } |
| } |
| } |
| |
| void |
| md_finish (void) |
| { |
| nds32_elf_insert_final_frag (); |
| nds32_elf_analysis_relax_hint (); |
| bfd_map_over_sections (stdoutput, nds32_insert_leb128_fixes, NULL); |
| } |
| |
| /* Implement md_allow_local_subtract. */ |
| |
| bool |
| nds32_allow_local_subtract (expressionS *expr_l ATTRIBUTE_UNUSED, |
| expressionS *expr_r ATTRIBUTE_UNUSED, |
| segT sec ATTRIBUTE_UNUSED) |
| { |
| /* Don't allow any subtraction, because relax may change the code. */ |
| return false; |
| } |
| |
| long |
| nds32_pcrel_from_section (fixS *fixP, segT sec ATTRIBUTE_UNUSED) |
| { |
| if (fixP->fx_addsy == NULL || !S_IS_DEFINED (fixP->fx_addsy) |
| || S_IS_EXTERNAL (fixP->fx_addsy) || S_IS_WEAK (fixP->fx_addsy)) |
| { |
| /* Let linker resolve undefined symbols. */ |
| return 0; |
| } |
| |
| return fixP->fx_frag->fr_address + fixP->fx_where; |
| } |
| |
| /* md_post_relax_hook () |
| Insert relax entry relocation into sections. */ |
| |
| void |
| nds32_post_relax_hook (void) |
| { |
| bfd_map_over_sections (stdoutput, nds32_insert_relax_entry, NULL); |
| } |
| |
| /* tc_fix_adjustable () |
| |
| Return whether this symbol (fixup) can be replaced with |
| section symbols. */ |
| |
| bool |
| nds32_fix_adjustable (fixS *fixP) |
| { |
| switch (fixP->fx_r_type) |
| { |
| case BFD_RELOC_NDS32_WORD_9_PCREL: |
| case BFD_RELOC_NDS32_9_PCREL: |
| case BFD_RELOC_NDS32_15_PCREL: |
| case BFD_RELOC_NDS32_17_PCREL: |
| case BFD_RELOC_NDS32_25_PCREL: |
| case BFD_RELOC_NDS32_HI20: |
| case BFD_RELOC_NDS32_LO12S0: |
| case BFD_RELOC_8: |
| case BFD_RELOC_16: |
| case BFD_RELOC_32: |
| case BFD_RELOC_NDS32_PTR: |
| case BFD_RELOC_NDS32_LONGCALL4: |
| case BFD_RELOC_NDS32_LONGCALL5: |
| case BFD_RELOC_NDS32_LONGCALL6: |
| case BFD_RELOC_NDS32_LONGJUMP4: |
| case BFD_RELOC_NDS32_LONGJUMP5: |
| case BFD_RELOC_NDS32_LONGJUMP6: |
| case BFD_RELOC_NDS32_LONGJUMP7: |
| return 1; |
| default: |
| return 0; |
| } |
| } |
| |
| /* elf_tc_final_processing */ |
| |
| void |
| elf_nds32_final_processing (void) |
| { |
| /* An FPU_COM instruction is found without previous non-FPU_COM |
| instruction. */ |
| if (nds32_fpu_com |
| && !(nds32_elf_flags & (E_NDS32_HAS_FPU_INST | E_NDS32_HAS_FPU_DP_INST))) |
| { |
| /* Since only FPU_COM instructions are used and no other FPU instructions |
| are used. The nds32_elf_flags will be decided by the enabled options |
| by command line or default configuration. */ |
| if (nds32_fpu_dp_ext || nds32_fpu_sp_ext) |
| { |
| nds32_elf_flags |= nds32_fpu_dp_ext ? E_NDS32_HAS_FPU_DP_INST : 0; |
| nds32_elf_flags |= nds32_fpu_sp_ext ? E_NDS32_HAS_FPU_INST : 0; |
| } |
| else |
| { |
| /* Should never here. */ |
| as_bad (_("Used FPU instructions requires enabling FPU extension")); |
| } |
| } |
| |
| if (nds32_elf_flags & (E_NDS32_HAS_FPU_INST | E_NDS32_HAS_FPU_DP_INST)) |
| { |
| /* Single/double FPU has been used, set FPU register config. */ |
| /* We did not check the actual number of register used. We may |
| want to do it while assemble. */ |
| nds32_elf_flags &= ~E_NDS32_FPU_REG_CONF; |
| nds32_elf_flags |= (nds32_freg << E_NDS32_FPU_REG_CONF_SHIFT); |
| } |
| |
| if (nds32_pic) |
| nds32_elf_flags |= E_NDS32_HAS_PIC; |
| |
| if (nds32_gpr16) |
| nds32_elf_flags |= E_NDS32_HAS_REDUCED_REGS; |
| |
| nds32_elf_flags |= (E_NDS32_ELF_VER_1_4 | nds32_abi); |
| elf_elfheader (stdoutput)->e_flags |= nds32_elf_flags; |
| } |
| |
| /* Implement md_apply_fix. Apply the fix-up or transform the fix-up for |
| later relocation generation. */ |
| |
| void |
| nds32_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) |
| { |
| char *where = fixP->fx_frag->fr_literal + fixP->fx_where; |
| bfd_vma value = *valP; |
| |
| if (fixP->fx_r_type < BFD_RELOC_UNUSED |
| && fixP->fx_r_type > BFD_RELOC_NONE |
| && fixP->fx_r_type != BFD_RELOC_NDS32_DIFF_ULEB128) |
| { |
| /* In our old nds32 binutils, it must convert relocations which is |
| generated by CGEN. However, it does not have to consider this anymore. |
| In current, it only deal with data relocations which enum |
| is smaller than BFD_RELOC_NONE and BFD_RELOC_NDS32_DIFF_ULEB128. |
| It is believed that we can construct a better mechanism to |
| deal with the whole relocation issue in nds32 target |
| without using CGEN. */ |
| fixP->fx_addnumber = value; |
| fixP->tc_fix_data = NULL; |
| |
| /* Transform specific relocations here for later relocation generation. |
| Tag tls data for linker. */ |
| switch (fixP->fx_r_type) |
| { |
| case BFD_RELOC_NDS32_DATA: |
| /* This reloc is obselete, we do not need it so far. */ |
| fixP->fx_done = 1; |
| break; |
| case BFD_RELOC_NDS32_TPOFF: |
| case BFD_RELOC_NDS32_TLS_LE_HI20: |
| case BFD_RELOC_NDS32_TLS_LE_LO12: |
| case BFD_RELOC_NDS32_TLS_LE_ADD: |
| case BFD_RELOC_NDS32_TLS_LE_LS: |
| case BFD_RELOC_NDS32_GOTTPOFF: |
| case BFD_RELOC_NDS32_TLS_IE_HI20: |
| case BFD_RELOC_NDS32_TLS_IE_LO12S2: |
| case BFD_RELOC_NDS32_TLS_DESC_HI20: |
| case BFD_RELOC_NDS32_TLS_DESC_LO12: |
| case BFD_RELOC_NDS32_TLS_IE_LO12: |
| case BFD_RELOC_NDS32_TLS_IEGP_HI20: |
| case BFD_RELOC_NDS32_TLS_IEGP_LO12: |
| case BFD_RELOC_NDS32_TLS_IEGP_LO12S2: |
| S_SET_THREAD_LOCAL (fixP->fx_addsy); |
| break; |
| default: |
| break; |
| } |
| return; |
| } |
| |
| if (fixP->fx_addsy == (symbolS *) NULL) |
| fixP->fx_done = 1; |
| |
| if (fixP->fx_subsy != (symbolS *) NULL) |
| { |
| /* HOW DIFF RELOCATION WORKS. |
| |
| First of all, this relocation is used to calculate the distance |
| between two symbols in the SAME section. It is used for jump- |
| table, debug information, exception table, et al. Therefore, |
| it is a unsigned positive value. It is NOT used for general- |
| purpose arithmetic. |
| |
| Consider this example, the distance between .LEND and .LBEGIN |
| is stored at the address of foo. |
| |
| ---- >8 ---- >8 ---- >8 ---- >8 ---- |
| .data |
| foo: |
| .word .LBEGIN - .LEND |
| |
| .text |
| [before] |
| .LBEGIN |
| \ |
| [between] distance |
| / |
| .LEND |
| [after] |
| ---- 8< ---- 8< ---- 8< ---- 8< ---- |
| |
| We use a single relocation entry for this expression. |
| * The initial distance value is stored directly in that location |
| specified by r_offset (i.e., foo in this example.) |
| * The begin of the region, i.e., .LBEGIN, is specified by |
| r_info/R_SYM and r_addend, e.g., .text + 0x32. |
| * The end of region, i.e., .LEND, is represented by |
| .LBEGIN + distance instead of .LEND, so we only need |
| a single relocation entry instead of two. |
| |
| When an instruction is relaxed, we adjust the relocation entry |
| depending on where the instruction locates. There are three |
| cases, before, after and between the region. |
| * between: Distance value is read from r_offset, adjusted and |
| written back into r_offset. |
| * before: Only r_addend is adjust. |
| * after: We don't care about it. |
| |
| Hereby, there are some limitation. |
| |
| `(.LEND - 1) - .LBEGIN' and `(.LEND - .LBEGIN) - 1' |
| are semantically different, and we cannot handle latter case |
| when relaxation. |
| |
| The latter expression means subtracting 1 from the distance |
| between .LEND and .LBEGIN. And the former expression means |
| the distance between (.LEND - 1) and .LBEGIN. |
| |
| The nuance affects whether to adjust distance value when relax |
| an instruction. In another words, whether the instruction |
| locates in the region. Because we use a single relocation entry, |
| there is no field left for .LEND and the subtrahend. |
| |
| Since GCC-4.5, GCC may produce debug information in such expression |
| .long .L1-1-.L0 |
| in order to describe register clobbering during an function-call. |
| .L0: |
| call foo |
| .L1: |
| |
| Check http://gcc.gnu.org/ml/gcc-patches/2009-06/msg01317.html |
| for details. */ |
| |
| value -= S_GET_VALUE (fixP->fx_subsy); |
| *valP = value; |
| fixP->fx_subsy = NULL; |
| fixP->fx_offset -= value; |
| |
| switch (fixP->fx_r_type) |
| { |
| case BFD_RELOC_8: |
| fixP->fx_r_type = BFD_RELOC_NDS32_DIFF8; |
| md_number_to_chars (where, value, 1); |
| break; |
| case BFD_RELOC_16: |
| fixP->fx_r_type = BFD_RELOC_NDS32_DIFF16; |
| md_number_to_chars (where, value, 2); |
| break; |
| case BFD_RELOC_32: |
| fixP->fx_r_type = BFD_RELOC_NDS32_DIFF32; |
| md_number_to_chars (where, value, 4); |
| break; |
| case BFD_RELOC_NDS32_DIFF_ULEB128: |
| /* cvt_frag_to_fill () has called output_leb128 () for us. */ |
| break; |
| default: |
| as_bad_subtract (fixP); |
| return; |
| } |
| } |
| else if (fixP->fx_done) |
| { |
| /* We're finished with this fixup. Install it because |
| bfd_install_relocation won't be called to do it. */ |
| switch (fixP->fx_r_type) |
| { |
| case BFD_RELOC_8: |
| md_number_to_chars (where, value, 1); |
| break; |
| case BFD_RELOC_16: |
| md_number_to_chars (where, value, 2); |
| break; |
| case BFD_RELOC_32: |
| md_number_to_chars (where, value, 4); |
| break; |
| case BFD_RELOC_64: |
| md_number_to_chars (where, value, 8); |
| break; |
| default: |
| as_bad_where (fixP->fx_file, fixP->fx_line, |
| _("Internal error: Unknown fixup type %d (`%s')"), |
| fixP->fx_r_type, |
| bfd_get_reloc_code_name (fixP->fx_r_type)); |
| break; |
| } |
| } |
| } |
| |
| /* Implement tc_gen_reloc. Generate ELF relocation for a fix-up. */ |
| |
| arelent * |
| tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP) |
| { |
| arelent *reloc; |
| bfd_reloc_code_real_type code; |
| |
| reloc = notes_alloc (sizeof (arelent)); |
| reloc->sym_ptr_ptr = notes_alloc (sizeof (asymbol *)); |
| *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy); |
| reloc->address = fixP->fx_frag->fr_address + fixP->fx_where; |
| |
| code = fixP->fx_r_type; |
| |
| reloc->howto = bfd_reloc_type_lookup (stdoutput, code); |
| if (reloc->howto == (reloc_howto_type *) NULL) |
| { |
| as_bad_where (fixP->fx_file, fixP->fx_line, |
| _("internal error: can't export reloc type %d (`%s')"), |
| fixP->fx_r_type, bfd_get_reloc_code_name (code)); |
| return NULL; |
| } |
| |
| /* Add relocation handling here. */ |
| |
| switch (fixP->fx_r_type) |
| { |
| default: |
| /* In general, addend of a relocation is the offset to the |
| associated symbol. */ |
| reloc->addend = fixP->fx_offset; |
| break; |
| |
| case BFD_RELOC_NDS32_DATA: |
| /* Prevent linker from optimizing data in text sections. |
| For example, jump table. */ |
| reloc->addend = fixP->fx_size; |
| break; |
| } |
| |
| return reloc; |
| } |
| |
| static struct suffix_name suffix_table[] = |
| { |
| {"GOTOFF", BFD_RELOC_NDS32_GOTOFF}, |
| {"GOT", BFD_RELOC_NDS32_GOT20}, |
| {"TPOFF", BFD_RELOC_NDS32_TPOFF}, |
| {"PLT", BFD_RELOC_NDS32_25_PLTREL}, |
| {"GOTTPOFF", BFD_RELOC_NDS32_GOTTPOFF}, |
| {"TLSDESC", BFD_RELOC_NDS32_TLS_DESC}, |
| }; |
| |
| /* Implement md_parse_name. */ |
| |
| int |
| nds32_parse_name (char const *name, expressionS *exprP, |
| enum expr_mode mode ATTRIBUTE_UNUSED, |
| char *nextcharP ATTRIBUTE_UNUSED) |
| { |
| segT segment; |
| |
| exprP->X_op_symbol = NULL; |
| exprP->X_md = BFD_RELOC_UNUSED; |
| |
| exprP->X_add_symbol = symbol_find_or_make (name); |
| exprP->X_op = O_symbol; |
| exprP->X_add_number = 0; |
| |
| /* Check the special name if a symbol. */ |
| segment = S_GET_SEGMENT (exprP->X_add_symbol); |
| if ((segment != undefined_section) && (*nextcharP != '@')) |
| return 0; |
| |
| if (strcmp (name, GOT_NAME) == 0 && *nextcharP != '@') |
| { |
| /* Set for _GOT_OFFSET_TABLE_. */ |
| exprP->X_md = BFD_RELOC_NDS32_GOTPC20; |
| } |
| else if (*nextcharP == '@') |
| { |
| size_t i; |
| char *next; |
| for (i = 0; i < ARRAY_SIZE (suffix_table); i++) |
| { |
| next = input_line_pointer + 1 + strlen (suffix_table[i].suffix); |
| if (strncasecmp (input_line_pointer + 1, suffix_table[i].suffix, |
| strlen (suffix_table[i].suffix)) == 0 |
| && !is_part_of_name (*next)) |
| { |
| exprP->X_md = suffix_table[i].reloc; |
| *input_line_pointer = *nextcharP; |
| input_line_pointer = next; |
| *nextcharP = *input_line_pointer; |
| *input_line_pointer = '\0'; |
| break; |
| } |
| } |
| } |
| |
| return 1; |
| } |
| |
| /* Implement tc_regname_to_dw2regnum. */ |
| |
| int |
| tc_nds32_regname_to_dw2regnum (char *regname) |
| { |
| struct nds32_keyword *sym = str_hash_find (nds32_gprs_hash, regname); |
| |
| if (!sym) |
| return -1; |
| |
| return sym->value; |
| } |
| |
| void |
| tc_nds32_frame_initial_instructions (void) |
| { |
| /* CIE */ |
| /* Default cfa is register-31/sp. */ |
| cfi_add_CFA_def_cfa (31, 0); |
| } |