This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH 3/5] New target port: Andes 'nds32'. (gas)
- From: Kuan-Lin Chen <kuanlinchentw at gmail dot com>
- To: binutils at sourceware dot org
- Date: Mon, 8 Jul 2013 16:45:46 +0800
- Subject: [PATCH 3/5] New target port: Andes 'nds32'. (gas)
This patch has the gas directory changes for the nds32 port.
gas/
* Makefile.am (TARGET_CPU_CFILES, TARGET_CPU_HFILES): Add nds32 target.
* config/tc-nds32.c: New file for nds32 support.
* config/tc-nds32.h: New file for nds32 support.
* configure.in (nds32): Add nds32 target.
* configure.tgt (nds32be, nds32le, nds32-*-elf*,
nds32-*-linux*): Add nds32 target.
diff --git a/gas/Makefile.am b/gas/Makefile.am
index b85755d..aec1631 100644
--- a/gas/Makefile.am
+++ b/gas/Makefile.am
@@ -164,6 +164,7 @@ TARGET_CPU_CFILES = \
config/tc-moxie.c \
config/tc-msp430.c \
config/tc-mt.c \
+ config/tc-nds32.c \
config/tc-nios2.c \
config/tc-ns32k.c \
config/tc-openrisc.c \
@@ -235,6 +236,7 @@ TARGET_CPU_HFILES = \
config/tc-mn10300.h \
config/tc-msp430.h \
config/tc-mt.h \
+ config/tc-nds32.h \
config/tc-nios2.h \
config/tc-ns32k.h \
config/tc-openrisc.h \
diff --git a/gas/config/tc-nds32.c b/gas/config/tc-nds32.c
new file mode 100644
index 0000000..e7628ff
--- /dev/null
+++ b/gas/config/tc-nds32.c
@@ -0,0 +1,13026 @@
+/* tc-nds32.c -- Assemble for the NDS32 ISA
+
+ Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
+ Free Software Foundation, Inc.
+ Contributed by Andes Technology Corporation.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the license, 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 this program; see the file COPYING3. If not,
+ see <http://www.gnu.org/licenses/>. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+#include "opcodes/nds32-desc.h"
+#include "opcodes/nds32-opc.h"
+#include "cgen.h"
+#include "elf/nds32.h"
+#include "opcode/nds32.h"
+#include "bfd/elf32-nds32.h"
+#include "hash.h"
+#include "sb.h"
+#include "macro.h"
+#include "struc-symbol.h"
+
+#include <stdio.h>
+
+#ifdef CPU_CONFIG
+#include <CPU_CONFIG>
+#else
+#define NDS32_ISA_DIV
+#define NDS32_ISA_MAC_EXT
+#define NDS32_ISA_16B_EXT
+#define NDS32_ISA_PERF_EXT
+#define NDS32_ISA_PERF2_EXT
+#define NDS32_ISA_STRING_EXT
+#define NDS32_ISA_BASELINE_V3
+#define NDS32_ISA_DX_REGS
+#define NDS32_ISA_AUDIO_EXT
+#define NDS32_ISA_AUDIO_CONFIG 32
+#define NDS32_ISA_COP_EXT
+#define NDS32_ISA_IFC_EXT
+#define NDS32_ISA_EX9_EXT
+#endif
+
+extern int nds32_cgen_get_int_operand (CGEN_CPU_DESC, int,
+ const CGEN_FIELDS *);
+static int adj_label_pair (fragS * fragP);
+
+/* 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";
+
+
+/* Relocations against symbols are done in two parts, with a 20-bit HI
+ relocation and a 12-bit LO relocation. This means that in order for the
+ linker to handle carries correctly, it must be able to locate both the HI
+ and the LO relocation. This means that the relocations must appear in order
+ in the relocation table. */
+
+/* In order to implement this, we keep track of each unmatched HI relocation.
+ We then sort them so that they immediately precede the corresponding LO
+ relocation. */
+
+struct nds32_hi_fixup
+{
+ struct nds32_hi_fixup *next; /* Next HI fixup. */
+ fixS *fixp; /* This fixup. */
+ segT seg; /* The section this fixup is in. */
+};
+
+/* The list of unmatched HI relocs. */
+static struct nds32_hi_fixup *nds32_hi_fixup_list;
+
+
+/* Alignment support. */
+
+/* Save previous relaxed frag to align next label. */
+static fragS *prev_insn16 = NULL;
+/* Save previous frag to align next label. */
+static fragS *prev_frag = NULL;
+/* Save previous LABEL reloc for chaining to frag. */
+static fixS *prev_fixP = NULL;
+/* Remember there is a label existing,
+ and a reloc should be create in the following process. */
+static int label_exist = 0;
+/* Remember the alignment of the pseudo label brought by .align. */
+static int label_align = 0;
+static fixS *get_prev_fix (void);
+static void set_prev_fix (fixS *);
+static void nds32_insert_align_reloc (int);
+
+static int relax_jal_bound = 3;
+static int omit_frame_pointer = 1;
+static int is_conservative_ifc = 0;
+
+#define OPT_EXIT_FAILURE 3
+
+/* The bit assignment is for different machines using different
+ extensions. For new machines, maybe simply or together the
+ bits. For example, someday in future, machine N3H is assigned
+ to 0x10070002 means has all extensions listed below. */
+#define MACHINE_IS_N12 0x00000001
+#define MACHINE_IS_N12H 0x00000002
+
+/* Flags. */
+
+/* Non-zero if we are generating PIC code. */
+static int pic_code = 0;
+
+/* Check configuration setting conflict. */
+
+static int elf_ver = E_NDS32_ELF_VER_1_4;
+
+#if defined NDS32_ABI_2
+static int abi_ver = E_NDS_ABI_AABI;
+#elif defined NDS32_ABI_2FP
+static int abi_ver = E_NDS_ABI_V2FP;
+#else
+static int abi_ver = E_NDS_ABI_V1;
+#endif
+
+/* Turn on the NO_MAC flag as default if not V2/V3/V3M arch. */
+#if !defined NDS32_ISA_BASELINE_V1
+static int nds32_flags = 0;
+#else
+static int nds32_flags = E_NDS32_HAS_NO_MAC_INST;
+#endif
+
+
+/* Non-zero if warn when a high/shigh reloc has no matching low reloc. Each
+ high/shigh reloc must be paired with it's low cousin in order to properly
+ calculate the addend in a relocatable link (since there is a potential
+ carry from the low to the high/shigh). This option is off by default
+ though for user-written assembler code it might make sense to make the
+ default be on (i.e. have gcc pass a flag to turn it off). This warning
+ must not be on for GCC created code as optimization may delete the low but
+ not the high/shigh (at least we shouldn't assume or require it to). */
+static int warn_unmatched_high = 0;
+
+static int enable_relax_relocs = 1;
+static int enable_relax_ex9 = 0;
+static int enable_relax_ifc = 0;
+static int enable_relax_warning = 0;
+static int multi_call_relax = 1;
+static int pltgot_call_relax = 1;
+static int hint_func_args = 0; /* bit == 0: used; bit == 1: ignored */
+static int use_hint_func_args = 1;
+static int vec_size = 0;
+/* If the assembly code is generated by compiler,
+ it is supposed to have ".flag compiler_generated_asm" at beginning
of the content.
+ We have 'nds32_flag' to parse it and set this field to be nozero. */
+static int compiler_generated_asm = 0;
+
+/* From now on, the default values for all extensions is based on target
+ configuration - eht 08/07/07. If any extension is turned on at target
+ configuration time, the default value for this extension flag is 1.
+ Customers may turn this flag off at compile/assemble time. If any extension
+ is turned off at target configuration time, the default value for this
+ extension flag is 0. Customers can turn this flag on at compile/assemble
+ time, this can cause invalid instruction exception at runtime. */
+
+/* 1 if any extension option has been specified, in which case support for the
+ extended NDS32 instruction set should be enabled.
+ Default to all extensions turn on by target configuration. */
+
+/* 1 if -mno-16bit has been specified, in which case support for the base
+ 16-bit instruction set should be disabled. */
+#if defined NDS32_ISA_16B_EXT
+int disable_16bit = 0;
+static int saved_disable_16bit = 0;
+#else
+int disable_16bit = 1;
+static int saved_disable_16bit = 1;
+#endif
+
+/* Non-zero if -mno-ext-perf is not specified, in which case support for
+ extended NDS32 C/C++ performance instruction set should be enabled. */
+#if defined NDS32_ISA_PERF_EXT
+static int enable_c_extension = 1;
+#else
+static int enable_c_extension = 0;
+#endif
+
+/* Non-zero if -mno-ext-perf is not specified, in which case support for
+ extended NDS32 C/C++ performance instruction set should be enabled. */
+#if defined NDS32_ISA_PERF2_EXT
+static int enable_c_extension2 = 1;
+#else
+static int enable_c_extension2 = 0;
+#endif
+
+#define ARCH_V1 bfd_mach_n1h
+#define ARCH_V2 bfd_mach_n1h_v2
+#define ARCH_V3 bfd_mach_n1h_v3
+#define ARCH_V3M bfd_mach_n1h_v3m
+
+#if defined(NDS32_ISA_BASELINE_V1)
+static int march_cpu_opt = ARCH_V1;
+#elif defined(NDS32_ISA_BASELINE_V2)
+static int march_cpu_opt = ARCH_V2;
+#elif defined(NDS32_ISA_BASELINE_V3)
+static int march_cpu_opt = ARCH_V3;
+#elif defined(NDS32_ISA_BASELINE_V3M)
+static int march_cpu_opt = ARCH_V3M;
+#else
+static int march_cpu_opt = ARCH_V3;
+#endif
+
+#if defined NDS32_ISA_DX_REGS && ! defined NDS32_ISA_BASELINE_V1
+static int enable_dx_regs_extension = 1;
+#else
+static int enable_dx_regs_extension = 0;
+#endif
+
+/* Non-zero if -mext-dsp has been specified, in which case support for
+ extended NDS32 DSP instruction set should be enabled. */
+#ifdef NDS32_ISA_DSP_EXT
+static int enable_dsp_extension = 1;
+#else
+static int enable_dsp_extension = 0;
+#endif
+
+/* Non-zero if -mext-div is specified, in which case support for
+ extended NDS32 DIV instruction set should be enabled. */
+#if defined NDS32_ISA_DIV || !defined NDS32_ISA_BASELINE_V1
+static int enable_div_extension = 1;
+#else
+static int enable_div_extension = 0;
+#endif
+
+/* Non-zero if -mext-audio is specified, in which case support for
+ extended NDS32 AUDIO instruction set should be enabled. */
+#if defined NDS32_ISA_AUDIO_EXT
+# if defined NDS32_ISA_AUDIO_CONFIG
+# if (NDS32_ISA_AUDIO_CONFIG == 24) || (NDS32_ISA_AUDIO_CONFIG == 32) \
+ || (NDS32_ISA_AUDIO_CONFIG == BOTH)
+static int enable_audio_extension = 1;
+# else
+# error "Invalid NDS32_ISA_AUDIO_CONFIG value"
+# endif
+# else
+static int enable_audio_extension = 1;
+# endif
+#else /* No audio extension support. */
+static int enable_audio_extension = 0;
+#endif
+
+/* Non-zero if -mext-mac is specified, in which case support for
+ extended NDS32 MAC/MUL instruction set should be enabled. */
+#if !defined NDS32_ISA_BASELINE_V1 \
+ || defined NDS32_ISA_MAC_EXT || defined NDS32_ISA_AUDIO_EXT
+static int enable_mac_extension = 1;
+#else
+static int enable_mac_extension = 0;
+#endif
+
+/* Non-zero if -mext-string is specified, in which case support for
+ extended NDS32 STRING instruction set should be enabled. */
+#ifdef NDS32_ISA_STRING_EXT
+static int enable_string_extension = 1;
+#else
+static int enable_string_extension = 0;
+#endif
+
+/* Non-zero if -mext-audio is specified, in which case support for
+ extended NDS32 AUDIO instruction set should be enabled. */
+#ifdef NDS32_ISA_REDUCE_REGS
+static int enable_reduce_regs = 1;
+#else
+static int enable_reduce_regs = 0;
+#endif
+
+/* Non-zero if -mfpu has been specified, in which case support for
+ floating point processor instruction set should be enabled. */
+#if defined NDS32_ISA_FPU_DP_EXT
+static int enable_fpu_dp_extension = 1;
+static int enable_fpu_sp_extension = 1;
+#else
+# if defined NDS32_ISA_FPU_SP_EXT
+static int enable_fpu_sp_extension = 1;
+# else
+static int enable_fpu_sp_extension = 0;
+# endif
+static int enable_fpu_dp_extension = 0;
+#endif
+
+/* Indicate if an FPU_COM instruction is found
+ without previous non-FPU_COM instruction. */
+static int nds32_fpu_com = 0;
+
+#if defined NDS32_ISA_FPU_MAC_EXT \
+ && (defined NDS32_ISA_FPU_DP_EXT || defined NDS32_ISA_FPU_SP_EXT)
+static int enable_fpu_mac_extension = 1;
+#else
+static int enable_fpu_mac_extension = 0;
+#endif
+
+#if defined NDS32_ISA_FPU_CONFIG && NDS32_ISA_FPU_CONFIG >= 0 \
+ && NDS32_ISA_FPU_CONFIG < 4
+static int fs_reg_num = ((1 << (NDS32_ISA_FPU_CONFIG + 3)) > 32)
+ ? 32 : (1 << (NDS32_ISA_FPU_CONFIG + 3));
+static int fd_reg_num = ((1 << (NDS32_ISA_FPU_CONFIG + 2)) > 32)
+ ? 32 : (1 << (NDS32_ISA_FPU_CONFIG + 2));
+static int fpu_config = NDS32_ISA_FPU_CONFIG << E_NDS32_FPU_REG_CONF_SHIFT;
+#else
+static int fs_reg_num = 0;
+static int fd_reg_num = 0;
+static int fpu_config = 0;
+#endif
+
+/* Enable all extensions. */
+static int enable_all_ext = 0;
+
+/* Non-zero if optimizations should be performed. */
+static int optimize = 0;
+static int optimize_for_space = 0;
+static int optimize_for_space_no_align = 0;
+
+/* Back-to-back branch optimization. */
+static int b2bb_prev = 0;
+static int opt_b2bb = 0;
+
+typedef enum
+{
+ ALARM_BLOCK_NONE = 0,
+ ALARM_BLOCK_BAD,
+ ALARM_BLOCK_FATAL
+} ALARM_BLOCK;
+static ALARM_BLOCK alarm_block = ALARM_BLOCK_BAD;
+
+static struct hash_control *insn_mnem_hash = NULL;
+static struct hash_control *nds32_cgen_nomap_hash = NULL;
+
+
+enum options
+{
+ OPTION_BIG = OPTION_MD_BASE,
+ OPTION_LITTLE,
+ OPTION_16BIT,
+ OPTION_NO_16BIT,
+ OPTION_EXT_ALL,
+ OPTION_EXT_PERF,
+ OPTION_NO_EXT_PERF,
+ OPTION_EXT_DSP,
+ OPTION_NO_EXT_DSP,
+ OPTION_FPU_SP,
+ OPTION_FPU_DP,
+ OPTION_FPU_FMA,
+ OPTION_NO_FPU_SP,
+ OPTION_NO_FPU_DP,
+ OPTION_NO_FPU_FMA,
+ OPTION_FPU_FREG,
+ OPTION_REDUCED_REGS,
+ OPTION_NO_REDUCED_REGS,
+ OPTION_EXT_AUDIO,
+ OPTION_NO_EXT_AUDIO,
+ OPTION_EXT_MAC,
+ OPTION_NO_EXT_MAC,
+ OPTION_DIV,
+ OPTION_NO_DIV,
+ OPTION_PIC_CODE,
+ OPTION_NO_PIC_CODE,
+ OPTION_OPTIMIZE,
+ OPTION_OPTIMIZE_SPACE,
+ OPTION_WARN_UNMATCHED,
+ OPTION_NO_WARN_UNMATCHED,
+ OPTION_CPU,
+ OPTION_EXT_PERF2,
+ OPTION_NO_EXT_PERF2,
+ OPTION_EXT_STRING,
+ OPTION_NO_EXT_STRING,
+ OPTION_MARCH,
+ OPTION_DX_REGS,
+ OPTION_NO_DX_REGS,
+ OPTION_NO_RELAX,
+ OPTION_ABI,
+ OPTION_RELAX_WARN,
+ OPTION_NO_MULCALL_RELAX,
+ OPTION_NO_PLTGOT_CALL_RELAX,
+ OPTION_RELAX_JAL,
+ OPTION_NO_HINT_FUNC_ARGS,
+ OPTION_NO_OMIT_FP,
+ OPTION_NO_CONSERVATIVE_IFC,
+ OPTION_CONSERVATIVE_IFC,
+ OPTION_ALARM_BLOCK,
+ OPTION_B2BB,
+ OPTION_NO_B2BB
+};
+
+#define NDS32_SHORTOPTS "G:O"
+const char *md_shortopts = NDS32_SHORTOPTS;
+struct option md_longopts[] = {
+ {"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},
+ {"mcpu", required_argument, NULL, OPTION_CPU},
+ {"cpu", required_argument, NULL, OPTION_CPU},
+ {"mabi", required_argument, NULL, OPTION_ABI},
+ {"mpic", no_argument, NULL, OPTION_PIC_CODE},
+ {"mno-pic", no_argument, NULL, OPTION_NO_PIC_CODE},
+ {"O0", no_argument, NULL, OPTION_OPTIMIZE},
+ {"O1", no_argument, NULL, OPTION_OPTIMIZE},
+ {"O2", no_argument, NULL, OPTION_OPTIMIZE},
+ {"O3", no_argument, NULL, OPTION_OPTIMIZE},
+ {"Os", no_argument, NULL, OPTION_OPTIMIZE_SPACE},
+ {"march", required_argument, NULL, OPTION_MARCH},
+ {"misa", required_argument, NULL, OPTION_MARCH},
+
+ {"mdx-regs", no_argument, NULL, OPTION_DX_REGS},
+ {"mno-dx-regs", no_argument, NULL, OPTION_NO_DX_REGS},
+ {"mno-16-bit", no_argument, NULL, OPTION_NO_16BIT},
+ {"m16-bit", no_argument, NULL, OPTION_16BIT},
+ /* These are options for extensions. */
+ {"mall-ext", no_argument, NULL, OPTION_EXT_ALL},
+ {"mperf-ext", no_argument, NULL, OPTION_EXT_PERF},
+ {"mno-perf-ext", no_argument, NULL, OPTION_NO_EXT_PERF},
+ {"mperf2-ext", no_argument, NULL, OPTION_EXT_PERF2},
+ {"mno-perf2-ext", no_argument, NULL, OPTION_NO_EXT_PERF2},
+ {"mdsp-ext", no_argument, NULL, OPTION_EXT_DSP},
+ {"mno-dsp-ext", no_argument, NULL, OPTION_NO_EXT_DSP},
+ {"maudio-isa-ext", no_argument, NULL, OPTION_EXT_AUDIO},
+ {"mno-audio-isa-ext", no_argument, NULL, OPTION_NO_EXT_AUDIO},
+ {"mmac", no_argument, NULL, OPTION_EXT_MAC},
+ {"mno-mac", no_argument, NULL, OPTION_NO_EXT_MAC},
+ {"mstring-ext", no_argument, NULL, OPTION_EXT_STRING},
+ {"mno-string-ext", no_argument, NULL, OPTION_NO_EXT_STRING},
+ {"mdiv", no_argument, NULL, OPTION_DIV},
+ {"mno-div", no_argument, NULL, OPTION_NO_DIV},
+ {"mfpu-sp-ext", no_argument, NULL, OPTION_FPU_SP},
+ {"mno-fpu-sp-ext", no_argument, NULL, OPTION_NO_FPU_SP},
+ {"mfpu-dp-ext", no_argument, NULL, OPTION_FPU_DP},
+ {"mno-fpu-dp-ext", no_argument, NULL, OPTION_NO_FPU_DP},
+ {"mfpu-fma", no_argument, NULL, OPTION_FPU_FMA},
+ {"mno-fpu-fma", no_argument, NULL, OPTION_NO_FPU_FMA},
+ {"mfpu-freg", required_argument, NULL, OPTION_FPU_FREG},
+ {"mreduced-regs", no_argument, NULL, OPTION_REDUCED_REGS},
+ {"mfull-regs", no_argument, NULL, OPTION_NO_REDUCED_REGS},
+ /* These are relaxation options. */
+ {"mno-relax", no_argument, NULL, OPTION_NO_RELAX},
+ {"mb2bb", no_argument, NULL, OPTION_B2BB},
+ {"mno-b2bb", no_argument, NULL, OPTION_NO_B2BB},
+
+ /* These are internal options. */
+ {"mno-conservative-ifc", no_argument, NULL, OPTION_NO_CONSERVATIVE_IFC},
+ {"mconservative-ifc", no_argument, NULL, OPTION_CONSERVATIVE_IFC},
+ {"mrelax-warning", no_argument, NULL, OPTION_RELAX_WARN},
+ {"mno-mulcall", no_argument, NULL, OPTION_NO_MULCALL_RELAX},
+ {"mno-pltgotcall", no_argument, NULL, OPTION_NO_PLTGOT_CALL_RELAX},
+ {"mrelax-jal", required_argument, NULL, OPTION_RELAX_JAL},
+ {"mno-hint-func-args", no_argument, NULL, OPTION_NO_HINT_FUNC_ARGS},
+ {"mno-omit-fp", no_argument, NULL, OPTION_NO_OMIT_FP},
+
+ /* These are obsolete options. Remove them in the future. */
+ {"KPIC", no_argument, NULL, OPTION_NO_PIC_CODE},
+ {"mbaseline", required_argument, NULL, OPTION_MARCH},
+ {"warn-unmatched-high", no_argument, NULL, OPTION_WARN_UNMATCHED},
+ {"Wuh", no_argument, NULL, OPTION_WARN_UNMATCHED},
+ {"no-warn-unmatched-high", no_argument, NULL, OPTION_NO_WARN_UNMATCHED},
+ {"Wnuh", no_argument, NULL, OPTION_NO_WARN_UNMATCHED},
+ {"malarm-block", required_argument, NULL, OPTION_ALARM_BLOCK},
+ {"mno-16bit", no_argument, NULL, OPTION_NO_16BIT},
+ {"m16bit", no_argument, NULL, OPTION_16BIT},
+ {"mext-all", no_argument, NULL, OPTION_EXT_ALL},
+ {"mext-perf", no_argument, NULL, OPTION_EXT_PERF},
+ {"mno-ext-perf", no_argument, NULL, OPTION_NO_EXT_PERF},
+ {"mext-perf2", no_argument, NULL, OPTION_EXT_PERF2},
+ {"mno-ext-perf2", no_argument, NULL, OPTION_NO_EXT_PERF2},
+ {"mext-dsp", no_argument, NULL, OPTION_EXT_DSP},
+ {"mno-ext-dsp", no_argument, NULL, OPTION_NO_EXT_DSP},
+ {"mext-audio", no_argument, NULL, OPTION_EXT_AUDIO},
+ {"mno-ext-audio", no_argument, NULL, OPTION_NO_EXT_AUDIO},
+ {"mext-mac", no_argument, NULL, OPTION_EXT_MAC},
+ {"mno-ext-mac", no_argument, NULL, OPTION_NO_EXT_MAC},
+ {"mext-string", no_argument, NULL, OPTION_EXT_STRING},
+ {"mno-ext-string", no_argument, NULL, OPTION_NO_EXT_STRING},
+ {"mext-fpu-sp", no_argument, NULL, OPTION_FPU_SP},
+ {"mext-fpu-dp", no_argument, NULL, OPTION_FPU_DP},
+ {"mext-fpu-mac", no_argument, NULL, OPTION_FPU_FMA},
+ {"mno-ext-fpu-sp", no_argument, NULL, OPTION_NO_FPU_SP},
+ {"mno-ext-fpu-dp", no_argument, NULL, OPTION_NO_FPU_DP},
+ {"mno-ext-fpu-mac", no_argument, NULL, OPTION_NO_FPU_FMA},
+ {"mconfig-fpu", required_argument, NULL, OPTION_FPU_FREG},
+ {"mreduce-regs", no_argument, NULL, OPTION_REDUCED_REGS},
+ {"mno-reduce-regs", no_argument, NULL, OPTION_NO_REDUCED_REGS},
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* strcasestr is not supported by MinGW toolchain.
+ This function is copied from tc-rx.c. */
+
+static char *
+nds32_strcasestr (const char *string, const char *sub)
+{
+ int subl;
+ int strl;
+
+ if (!sub || !sub[0])
+ return (char *) string;
+
+ subl = strlen (sub);
+ strl = strlen (string);
+
+ while (strl >= subl)
+ {
+ /* strncasecmp is in libiberty. */
+ if (strncasecmp (string, sub, subl) == 0)
+ return (char *) string;
+
+ string++;
+ strl--;
+ }
+ return NULL;
+}
+
+/* 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)
+{
+ int err = had_errors ();
+
+ if (march_cpu_opt == ARCH_V1)
+ {
+ /* V1 soft core. */
+ if (enable_dx_regs_extension)
+ as_bad (_("CPU option -mdx-reg conflicts with V1 Architecture"));
+ if (had_errors () != err)
+ exit (OPT_EXIT_FAILURE);
+ }
+
+ if (march_cpu_opt == ARCH_V1)
+ {
+ nds32_flags = E_NDS32_HAS_NO_MAC_INST;
+ }
+
+ if (enable_all_ext)
+ {
+ enable_c_extension = 1;
+ enable_c_extension2 = 1;
+ enable_fpu_sp_extension = 1;
+ enable_fpu_dp_extension = 1;
+ enable_fpu_mac_extension = 1;
+ enable_audio_extension = 1;
+ enable_string_extension = 1;
+ enable_mac_extension = 1;
+ enable_div_extension = 1;
+ enable_dx_regs_extension = 1;
+ }
+
+ if (disable_16bit)
+ {
+ /* Not able to optimize at all. */
+ optimize = 0;
+ optimize_for_space = 0;
+ optimize_for_space_no_align = 0;
+ }
+
+ if (relax_jal_bound < 0)
+ {
+ as_bad (_("relax jal bound cannot be negative."));
+ exit (OPT_EXIT_FAILURE);
+ }
+}
+
+/* This function is called when printing usage message (--help). */
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, "\
+ -EL, -mel or -little Produce little endian output\n\
+ -EB, -meb or -big Produce big endian output\n\
+ -O Optimize for performance\n\
+ -Os Optimize for space\n\
+ -mcpu=CPU Assemble for CPU CPU\n\
+ -misa=ISA Assemble for ISA\n\
+ v1, v2, v3, v3m)\n\
+ -mabi=ABI Override default ABI flag in ELF header\n\
+ 1, 2, 2fp\n\
+ -mall-ext Allow all instruction extensions\n\
+ -m[no-]16-bit Disable/enable 16-bit instructions\n\
+ -m[no-]perf-ext Disable/enable Performance Extension\n\
+ -m[no-]perf2-ext Disable/enable Performance Extension V2\n\
+ -m[no-]string-ext Disable/enable String Extension\n\
+ -m[no-]dsp-ext Disable/enable DSP Extension\n\
+ -m[no-]mac Disable/enable Multiply/Multiply-Add instructions\n\
+ (using register d0/d1)\n\
+ -m[no-]div Disable/enable DIV/DIVS instructions instructions\n\
+ (using register d0/d1)\n\
+ -m[no-]audio-isa-ext Disable/enable AUDIO ISA Extension\n\
+ -m[no-]fpu-sp-ext Disable/enable FPU SP Extension\n\
+ -m[no-]fpu-dp-ext Disable/enable FPU DP Extension\n\
+ -m[no-]fpu-fma Disable/enable FPU Fused-Multiply-Add instructions\n\
+ -mfpu-freg=FREG Specify a FPU configuration\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\n\
+ -mreduced-regs Only reduced-set registers are allowed\n\
+ -mfull-regs Full-set registers are allowed\n\
+ -m[no-]dx-regs Reject/allow d0/d1 registers\n\
+ -mpic Generate PIC\n\
+\n\
+Relaxation related options:\n\
+ -mno-relax Suppress relaxation for this file\n\
+ -mb2bb Back-to-back branch optimization\n\
+");
+}
+
+/* Instruction operand access macros. */
+#define CGEN_FIELDS_RT5(fields) ((fields).f_32_rt5)
+#define CGEN_FIELDS_RA5(fields) ((fields).f_32_ra5)
+#define CGEN_FIELDS_RB5(fields) ((fields).f_32_rb5)
+#define CGEN_FIELDS_RT5E(fields) ((fields).f_16_rt5e)
+#define CGEN_FIELDS_RA5E(fields) ((fields).f_16_ra5e)
+#define CGEN_FIELDS_RD1HL(fields) ((fields).f_32_rd1hl)
+#define CGEN_FIELDS_DISP24(fields) ((fields).f_32t0_disp24)
+#define CGEN_FIELDS_SIMM20(fields) ((fields).f_32t1_slo20)
+#define CGEN_FIELDS_UHI20(fields) ((fields).f_32t1_uhi20)
+#define CGEN_FIELDS_DISP16(fields) ((fields).f_32t1_disp16)
+#define CGEN_FIELDS_SLO15D(fields) ((fields).f_32t2_slo15d)
+#define CGEN_FIELDS_ULO15D(fields) ((fields).f_32t2_ulo15d)
+#define CGEN_FIELDS_SLO15W(fields) ((fields).f_32t2_slo15w)
+#define CGEN_FIELDS_ULO15W(fields) ((fields).f_32t2_ulo15w)
+#define CGEN_FIELDS_SLO15H(fields) ((fields).f_32t2_slo15h)
+#define CGEN_FIELDS_ULO15H(fields) ((fields).f_32t2_ulo15h)
+#define CGEN_FIELDS_SLO15B(fields) ((fields).f_32t2_slo15b)
+#define CGEN_FIELDS_ULO15B(fields) ((fields).f_32t2_ulo15b)
+#define CGEN_FIELDS_DISP14(fields) ((fields).f_32t2_disp14)
+#define CGEN_FIELDS_32UIMM5(fields) ((fields).f_32t3_uimm5)
+#define CGEN_FIELDS_RT5H(fields) ((fields).f_16_rt5h)
+#define CGEN_FIELDS_RA5H(fields) ((fields).f_16_ra5h)
+#define CGEN_FIELDS_RB5H(fields) ((fields).f_16_rb5h)
+#define CGEN_FIELDS_RT4(fields) ((fields).f_16_rt4)
+#define CGEN_FIELDS_RA4(fields) ((fields).f_16_ra4)
+#define CGEN_FIELDS_RT3(fields) ((fields).f_16_rt3)
+#define CGEN_FIELDS_RT37(fields) ((fields).f_16_rt3_7)
+#define CGEN_FIELDS_RA3(fields) ((fields).f_16_ra3)
+#define CGEN_FIELDS_RB3(fields) ((fields).f_16_rb3)
+#define CGEN_FIELDS_UIMM3(fields) ((fields).f_16_uimm3)
+#define CGEN_FIELDS_16UIMM5(fields) ((fields).f_16_uimm5)
+#define CGEN_FIELDS_SIMM5(fields) ((fields).f_16_simm5)
+#define CGEN_FIELDS_UIMM7(fields) ((fields).f_16_rt3_7)
+#define CGEN_FIELDS_HSDISP8(fields) ((fields).f_16_hsdisp8)
+#define CGEN_FIELDS_MASK4(fields) ((fields).f_32t5_mask4)
+#define CGEN_FIELDS_USRIDX(fields) ((fields).f_32_usr)
+#define CGEN_FIELDS_GROUPIDX(fields) ((fields).f_32_group)
+#define CGEN_FIELDS_SIMM11(fields) ((fields).f_32t2_simm11)
+
+/* Fragment subtype definitions. */
+
+/* It's an 16-bit frag if bit 0 and bit 10 are both 0. */
+/* frag[0] */
+#define RELAX_RT_MASK 0xf8000000 /* bit 31..27 for
register rt5. */
+#define RELAX_RA_MASK 0x07c00000 /* bit 26..22 for
register ra5. */
+#define RELAX_RB_MASK 0x003e0000 /* bit 21..17 for
register rb5. */
+#define RELAX_RELAXABLE_MASK 0x00010000 /* bit 16; frag is relaxable. */
+#define RELAX_RELAXED_MASK 0x00008000 /* bit 15; frag relaxed. */
+#define RELAX_FORCE_NO_16BIT_MASK 0x00004000 /* bit 14; only 32-bit
instruction is used in frag. */
+#define RELAX_SEQ_TYPE_MASK 0x00003c00 /* bit 13..10 ; branch
code seq. */
+#define RELAX_BRANCH_MASK 0x00000001 /* bit 0; set if branch
instruction. */
+/* flag[1] */
+#define RELAX_SIMM11_MASK 0xffe00000 /* bit 31..21 for
register simm11. */
+#define RELAX_OPC_NUM_MASK 0xffffffff /* bit 31..0; opcode number. */
+
+/* Relaxed branch sequence types:
+
+ BR_UCOND_CALL_S16M
+ jal label
+
+ BR_UCOND_CALL_U4G
+ sethi $ta, hi20(label)
+ ori $ta, [$ta + lo12(label)]
+ jral $ta ; linker may contract it to 16-bit for label alignment
+
+ BR_COND_CALL_S64K
+ bgezal rt, label ; or bltzal rt, label
+
+ BR_COND_CALL_S16M
+ bltz rt, $1 ; or bgez rt, $1
+ jal label
+ $1:
+
+ BR_COND_CALL_U4G
+ bltz rt, $1 ; or bgez rt, $1
+ sethi $ta, hi20(label)
+ ori $ta, [$ta + lo12(label)]
+ jral $ta ; linker may contract it to 16-bit for label alignment
+ $1:
+
+ BR_UCOND_BR_S256,
+ j label ; linker may contract it to 16-bit for label alignment
+
+ BR_UCOND_BR_S16M,
+ j label
+
+ BR_UCOND_BR_U4G,
+ sethi $ta, hi20(label)
+ ori $ta, [$ta + lo12(label)]
+ jr $ta ; linker may contract it to 16-bit for label alignment
+
+ BR_COND_BR_S256
+ beqs38 rt3, label ; or other 16-bit conditional branches
+
+ BR_COND_BR_S16K,
+ beq rt5, ra5, label ; or bne rt5, ra5, label
+
+ BR_COND_BR_S64K,
+ beqz rt5, label ; or other 32-bit conditional branch against 0
+
+ BR_COND_BR_S16M,
+ bnes38 rt3, label
+ j label
+ $1:
+
+ BR_COND_BR_U4G
+ bnes38 rt3, label
+ sethi $ta, hi20(label)
+ ori $ta, [$ta + lo12(label)]
+ jral $ta ; linker may contract it to 16-bit for label alignment
+ */
+
+/* Relaxed memory access with label.
+
+ MEM_ACC_LONG
+ sethi rt, hi20(label)
+ lwi rt, [rt + lo12(label)]
+
+ MEM_ACC_SHORT
+ lwi rt, [$gp + sda(label)]
+ */
+
+typedef enum br_insn_type
+{
+ BR_UCOND_BR, /* Unconditional branch. */
+ BR_COND_BR, /* Conditional branch. */
+ BR_UCOND_CALL, /* Unconditional call. */
+ BR_COND_CALL /* Conditional call. */
+} BR_INSN_TYPE;
+
+typedef enum br_range_type
+{
+ BR_RANGE_S256, /* PC relative -256 to 254. */
+ BR_RANGE_S16K, /* PC relative -16K to 16K - 2. */
+ BR_RANGE_S64K, /* PC relative -64K to 64K - 2. */
+ BR_RANGE_S16M, /* PC relative -16M to 16M - 2. */
+ BR_RANGE_U4G /* Absolute 0 to 4G - 2. */
+} BR_RANGE_TYPE;
+
+typedef enum br_seq_type
+{
+ BR_SEQ_NONE,
+ BR_UCOND_CALL_S16M, /* PC relative -16M to 16M -2 unconditional call. */
+ BR_UCOND_CALL_U4G, /* Absolute 0 to 4G - 2 unconditional call. */
+ BR_COND_CALL_S64K, /* PC relative -64K to 64K -2 conditional call. */
+ BR_COND_CALL_S16M, /* PC relative -16M to 16M -2 conditional call. */
+ BR_COND_CALL_U4G, /* Absolute 0 to 4G - 2 conditional call. */
+ BR_UCOND_BR_S256, /* PC relative -256 to 254 unconditional branch. */
+ BR_UCOND_BR_S16M, /* PC relative -16M to 16M -2 unconditional branch. */
+ BR_UCOND_BR_U4G, /* Absolute 0 to 4G - 2 unconditional branch. */
+ BR_COND_BR_S256, /* PC relative -256 to 254 conditional branch. */
+ BR_COND_BR_S16K, /* PC relative -16K to 16K - 2 conditional branch. */
+ BR_COND_BR_S64K, /* PC relative -64K to 64K - 2 conditional branch. */
+ BR_COND_BR_S16M, /* PC relative -16M to 16M - 2 conditional branch. */
+ BR_COND_BR_U4G /* Absolute 0 to 4G - 2 conditional branch. */
+} BR_SEQ_TYPE;
+
+typedef enum ls_type
+{
+ LS_SHORT,
+ LS_LONG
+} LS_TYPE;
+
+/* Set branch code of fr_subtype. */
+#define SET_BR_CODE(code) ((code & 0xf) << 10)
+
+/* Is a frag relaxable? */
+#define RELAX_RELAXABLE(fragp) \
+ (fragp->tc_frag_data.flag[0] & RELAX_RELAXABLE_MASK)
+/* Is a frag relaxed? */
+#define RELAX_RELAXED(fragp) \
+ (fragp->tc_frag_data.flag[0] & RELAX_RELAXED_MASK)
+/* Force frag to use 32-bit instruction? */
+#define RELAX_USE_32BIT(fragp) \
+ (fragp->tc_frag_data.flag[0] & RELAX_FORCE_NO_16BIT_MASK)
+/* Mark a frag as relaxable. */
+#define RELAX_SET_RELAXABLE(fragp) \
+ (fragp->tc_frag_data.flag[0] |= RELAX_RELAXABLE_MASK)
+
+/* Mark a frag as not relaxable. */
+#define RELAX_CLEAR_RELAXABLE(fragp) \
+ (fragp->tc_frag_data.flag[0] &= (~RELAX_RELAXABLE_MASK))
+
+/* Mark a frag as relaxed. */
+#define RELAX_SET_RELAXED(fragp) \
+ (fragp->tc_frag_data.flag[0] |= RELAX_RELAXED_MASK)
+
+#define RELAX_SET_INSN(fragp, insn) \
+ do { fragp->tc_frag_data.insn32 = insn; } while (0)
+#define RELAX_GET_INSN(fragp) fragp->tc_frag_data.insn32
+
+/* Mark a frag as not relaxed. */
+#define RELAX_CLEAR_RELAXED(fragp) \
+ (fragp->tc_frag_data.flag[0] &= (~RELAX_RELAXED_MASK))
+
+/* Get branch bit of an instruction. */
+#define RELAX_BRANCH(fragp) \
+ (((fragp)->tc_frag_data.flag[0] & RELAX_BRANCH_MASK) >> 0)
+
+/* Get original rt5 register. */
+#define RELAX_RT(fragp) (((fragp)->tc_frag_data.flag[0] & RELAX_RT_MASK) >> 27)
+
+/* Get original ra5 register. */
+#define RELAX_RA(fragp) (((fragp)->tc_frag_data.flag[0] & RELAX_RA_MASK) >> 22)
+
+/* Get original rb5 register. */
+#define RELAX_RB(fragp) (((fragp)->tc_frag_data.flag[0] & RELAX_RB_MASK) >> 17)
+
+/* Get original simm11 immediate (beqc/bnec). */
+#define RELAX_SIMM11(fragp) \
+ (long) __SEXT ((((fragp)->tc_frag_data.flag[1] & RELAX_SIMM11_MASK)
>> 21), 11)
+
+/* Get original opcode number of a branch. */
+#define RELAX_32(fragp) (((fragp)->tc_frag_data.flag[0] & RELAX_32_MASK) >> 16)
+
+/* Get original opcode number of a branch. */
+#define RELAX_OPC_NUM(fragp) (((fragp)->tc_frag_data.insn_num &
RELAX_OPC_NUM_MASK))
+
+/* Get relaxation code sequence of a branch. */
+#define RELAX_SEQ_TYPE(fragp) \
+ (((fragp)->tc_frag_data.flag[0] & RELAX_SEQ_TYPE_MASK) >> 10)
+
+/* Set all relaxation flag. */
+#define RELAX_SET_FRAG_TC_FLAGS(fragp, flagp) \
+do { \
+ int __i = 0; \
+ for (;__i < NDS32_FRAG_FLAG_NUM;__i++) { \
+ fragp->tc_frag_data.flag[__i] = flagp[__i]; \
+ } \
+} while (0)
+
+/* Set relaxation flag. */
+#define RELAX_SET_FRAG_TC_FLAG(nump, fragp, flagp) \
+ (fragp->tc_frag_data.flag[nump] = flagp)
+
+/* Get relaxation flag. */
+#define RELAX_GET_FRAG_TC_FLAG(nump, fragp) \
+ (fragp->tc_frag_data.flag[nump])
+
+/* Set relaxation opcode number. */
+#define RELAX_SET_FRAG_TC_OPCNUM(fragp, opcnum) \
+ ((fragp)->tc_frag_data.insn_num = \
+ (((fragp)->tc_frag_data.insn_num & (~RELAX_OPC_NUM_MASK)) \
+ | (opcnum & RELAX_OPC_NUM_MASK)))
+
+#define SET_ADDEND( size, convertible, optimize, insn16_on ) \
+ (((size) & 0xff) | ((convertible) ? 1 << 31 : 0) \
+ | ((optimize) ? 1<< 30 : 0) | (insn16_on ? 1 << 29 : 0))
+
+void
+nds32_frag_init (fragS *fragp)
+{
+ int i = 0;
+
+ for (; i < NDS32_FRAG_FLAG_NUM; i++)
+ fragp->tc_frag_data.flag[i] = 0;
+ fragp->tc_frag_data.insn_num = 0;
+ RELAX_SET_INSN (fragp, 0);
+ fragp->tc_frag_data.fixup = NULL;
+}
+
+typedef struct insn16_to_32
+{
+ CGEN_INSN_TYPE insn16;
+ CGEN_INSN_TYPE insn32;
+} INSN_MAP_ENTRY;
+
+INSN_MAP_ENTRY insn16_to_32[] = {
+ {NDS32_INSN_MOV55, NDS32_INSN_ORI},
+ {NDS32_INSN_MOVI55, NDS32_INSN_MOVI},
+ {NDS32_INSN_ADDI45, NDS32_INSN_ADDI},
+ {NDS32_INSN_ADD45, NDS32_INSN_ADD},
+ {NDS32_INSN_SUBI45, NDS32_INSN_ADDI},
+ {NDS32_INSN_SUB45, NDS32_INSN_SUB},
+ {NDS32_INSN_SRAI45, NDS32_INSN_SRAI},
+ {NDS32_INSN_SRLI45, NDS32_INSN_SRLI},
+ {NDS32_INSN_SLLI333, NDS32_INSN_SLLI},
+ {NDS32_INSN_ADDI333, NDS32_INSN_ADDI},
+ {NDS32_INSN_ADD333, NDS32_INSN_ADD},
+ {NDS32_INSN_SUBI333, NDS32_INSN_ADDI},
+ {NDS32_INSN_SUB333, NDS32_INSN_SUB},
+ {NDS32_INSN_LWI333, NDS32_INSN_LWI},
+ {NDS32_INSN_LWI333_BI, NDS32_INSN_LWI_BI},
+ {NDS32_INSN_LHI333, NDS32_INSN_LHI},
+ {NDS32_INSN_LBI333, NDS32_INSN_LBI},
+ {NDS32_INSN_SWI333, NDS32_INSN_SWI},
+ {NDS32_INSN_SWI333_BI, NDS32_INSN_SWI_BI},
+ {NDS32_INSN_SHI333, NDS32_INSN_SHI},
+ {NDS32_INSN_SBI333, NDS32_INSN_SBI},
+ {NDS32_INSN_LWI450, NDS32_INSN_LWI},
+ {NDS32_INSN_SWI450, NDS32_INSN_SWI},
+ {NDS32_INSN_LWI37, NDS32_INSN_LWI},
+ {NDS32_INSN_SWI37, NDS32_INSN_SWI},
+ {NDS32_INSN_BEQZ38, NDS32_INSN_BEQZ},
+ {NDS32_INSN_BNEZ38, NDS32_INSN_BNEZ},
+ {NDS32_INSN_BEQS38, NDS32_INSN_BEQ},
+ {NDS32_INSN_BNES38, NDS32_INSN_BNE},
+ {NDS32_INSN_J8, NDS32_INSN_J},
+ {NDS32_INSN_JR5, NDS32_INSN_JR},
+ {NDS32_INSN_RET5, NDS32_INSN_RET},
+ {NDS32_INSN_JRAL5, NDS32_INSN_JRAL},
+ {NDS32_INSN_SLTI45, NDS32_INSN_SLTI},
+ {NDS32_INSN_SLTSI45, NDS32_INSN_SLTSI},
+ {NDS32_INSN_SLT45, NDS32_INSN_SLT},
+ {NDS32_INSN_SLTS45, NDS32_INSN_SLTS},
+ {NDS32_INSN_BEQZS8, NDS32_INSN_BEQZ},
+ {NDS32_INSN_BNEZS8, NDS32_INSN_BNEZ},
+ {NDS32_INSN_BREAK16, NDS32_INSN_BREAK}
+};
+
+static int is_convertible (fragS *);
+
+static int
+get_frag_insn_size (fragS *fragP, int offset)
+{
+ uint16_t insn16;
+ char *where;
+
+ if (fragP->fr_fix < 2)
+ return 0;
+
+ where = fragP->fr_literal + offset;
+
+ /* Offset must be even number. */
+ gas_assert ((offset & 1) == 0);
+ insn16 = bfd_getb16 (where);
+
+ if ((insn16 & 0x8000) >> 15)
+ {
+ /* 2 byte insn, is it relaxed? */
+ if (offset == fragP->fr_fix - 2
+ && (RELAX_RELAXED (fragP) || RELAX_BRANCH (fragP)))
+ return 4;
+ else
+ return 2;
+ }
+ else
+ return 4;
+}
+
+static valueT
+md_chars_to_number (char *buf, int n)
+{
+ valueT result = 0;
+ unsigned char *where = (unsigned char *) buf;
+
+ if (target_big_endian)
+ {
+ while (n--)
+ {
+ result <<= 8;
+ result |= (*where++ & 255);
+ }
+ }
+ else
+ {
+ while (n--)
+ {
+ result <<= 8;
+ result |= (where[n] & 255);
+ }
+ }
+
+ return result;
+}
+
+/* The assembler tries to optimize the code size through relaxation
+ processing, so that conversion from 32-bit instruction to
+ corresponding 16-bit instruction is done whenever possible. */
+
+static int
+convert_32_to_16 (fragS *fragP, uint16_t *pinsn,
+ relax_substateT *subtype, int *pinsn_type)
+{
+ char *buf;
+ uint16_t insn16 = 0;
+ uint32_t insn = 0;
+ int insn_type = 0;
+
+ /* FIXME: This piece of code is borrowed from bfd/elf32-nds32.c
+ This function converts a 32-bit instruction to 16-bit one. */
+
+ if (subtype[0] & RELAX_FORCE_NO_16BIT_MASK)
+ return 0;
+
+ /* FIXME: The way to check instruction availability can be dangerous
+ if this instruction doesn't exist in a future ISA,
+ but anyway, there is no way for assembler to know.
+ Besides, I don't think conversion should be done in assembler,
+ since CGEN is the only one who knows the encoding details.
+ This function brings maintenance issues. */
+
+ if (fragP->fr_symbol != NULL)
+ {
+ /* Instruction with symbol is not convertible
+ because symbol is relocatable. */
+ return 0;
+ }
+
+ /* Where is the 32-bit instruction. */
+ if (fragP == frag_now)
+ {
+ buf = fragP->fr_literal + frag_now_fix_octets () - 4;
+ }
+ else
+ {
+ /* Frag has been closed, so fragP->fr_fix is valid. */
+ buf = fragP->fr_literal + fragP->fr_fix - 4;
+ }
+
+ /* Get instruction contents. */
+ insn = bfd_getb32 (buf);
+
+ nds32_convert_32_to_16 (stdoutput, insn, &insn16, &insn_type);
+
+ if (insn16 != 0)
+ {
+ if (insn_type == 0)
+ as_warn (_("Internal warning: Forget to set insn_num in "
+ "convert_32_to_16 (insn=0x%x).\n"), (int) insn);
+ if (pinsn)
+ {
+ *pinsn = insn16;
+ *pinsn_type = insn_type;
+ }
+
+ if (subtype)
+ {
+ *pinsn = insn16;
+ *pinsn_type = insn_type;
+ }
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/* This function convert 16-bit instruction to 32-bit. */
+
+static int
+convert_16_to_32 (fragS *fragP, uint32_t *insn_p)
+{
+ /* Where is the 16-bit instruction. */
+ uint16_t insn16;
+
+ insn16 = bfd_getb16 (fragP->fr_literal + fragP->fr_fix - 2);
+ if (RELAX_GET_INSN (fragP))
+ {
+ *insn_p = RELAX_GET_INSN (fragP);
+ return TRUE;
+ }
+
+ return nds32_convert_16_to_32 (stdoutput, insn16, insn_p);
+}
+
+static char *nds32_cgen_nomap_table[][2] = {
+ {"neg", "subri"},
+ {"zeb", "andi"},
+ {"lbi.p", "lbi.bi"},
+ {"lb.p", "lb.bi"},
+ {"lhi.p", "lhi.bi"},
+ {"lh.p", "lh.bi"},
+ {"lwi.p", "lwi.bi"},
+ {"lw.p", "lw.bi"},
+ {"lbsi.p", "lbsi.bi"},
+ {"lbs.p", "lbs.bi"},
+ {"lhsi.p", "lhsi.bi"},
+ {"lhs.p", "lhs.bi"},
+ {"sbi.p", "sbi.bi"},
+ {"sb.p", "sb.bi"},
+ {"shi.p", "shi.bi"},
+ {"sh.p", "sh.bi"},
+ {"swi.p", "swi.bi"},
+ {"sw.p", "sw.bi"},
+ {"nop", "srli"},
+ {"nop16", "srli45"},
+ {"beq_r", "beq"},
+ {"bne_r", "bne"},
+ {"beqz_r", "beqz"},
+ {"bnez_r", "bnez"},
+ {"bgez_r", "bgez"},
+ {"bltz_r", "bltz"},
+ {"bgtz_r", "bgtz"},
+ {"blez_r", "blez"},
+ {"beqs38_r", "beqs38"},
+ {"bnes38_r", "bnes38"},
+ {"beqz38_r", "beqz38"},
+ {"bnez38_r", "bnez38"},
+ {"beqzs8_r", "beqzs8"},
+ {"bnezs8_r", "bnezs8"},
+ {"bgezal_r", "bgezal"},
+ {"bltzal_r", "bltzal"},
+ {"j_r", "j"},
+ {"j8_r", "j8"},
+ {"jal_r", "jal"},
+ {"jral_r", "jral"},
+ {"jral5_r", "jral5"},
+ {"flsi1.bi", "flsi.bi"},
+ {"ifret16", "ifret"},
+ {"beqc_r", "beqc"},
+ {"bnec_r", "bnec"},
+ {0, 0}
+};
+
+static int forbid_insn_num[MAX_INSNS];
+
+static void
+init_forbid_insn_num (void)
+{
+ int i;
+
+ /* 0: Do nothing.
+ 1: Forbid and alarm.
+ 2: Do translation if could. */
+
+ for (i = NDS32_INSN_INVALID; i < MAX_INSNS; i++)
+ {
+ /* legacy macro expand instructions
+ * beq/bne
+ * beqz/bnez/bgez/bltz/bgtz/blez
+ * bgezal/bltzal */
+ if ((i >= NDS32_INSN_BEQ && i <= NDS32_INSN_BLTZAL)
+ /* beqs38/beqz38/bnes38/bnez38 */
+ || (i >= NDS32_INSN_BEQZ38 && i <= NDS32_INSN_BNES38)
+ || i == NDS32_INSN_BEQZS8
+ || i == NDS32_INSN_BNEZS8
+ || i == NDS32_INSN_BEQC
+ || i == NDS32_INSN_BNEC
+ || i == NDS32_INSN_JRAL5)
+ {
+ forbid_insn_num[i] = 2;
+ }
+ else
+ forbid_insn_num[i] = 0;
+
+ if (march_cpu_opt != ARCH_V3M)
+ continue;
+
+ /* lmwa/smwa */
+ else if (i >= NDS32_INSN_LMWA_BI && i <= NDS32_INSN_SMWA_ADM)
+ forbid_insn_num[i] = 1;
+ /* mulr64/mulsr64 */
+ else if (i == NDS32_INSN_MULR64 || i == NDS32_INSN_MULSR64)
+ forbid_insn_num[i] = 1;
+
+ /* Dx related instructions.
+ * madd32/madd64/madds64
+ * msub32/msub64/msubs64
+ * mult32/mult64/mults64 */
+ else if (i >= NDS32_INSN_MULTS64 && i <= NDS32_INSN_MSUB32)
+ forbid_insn_num[i] = 1;
+ /* divs/div */
+ else if (i == NDS32_INSN_DIVS || i == NDS32_INSN_DIV)
+ forbid_insn_num[i] = 1;
+ /* mfusr/mtusr */
+ else if (i == NDS32_INSN_MFUSR || i == NDS32_INSN_MTUSR)
+ forbid_insn_num[i] = 1;
+ /* jrnez/jralnez/add5.pc */
+ else if (i == NDS32_INSN_JRNEZ || i == NDS32_INSN_JRALNEZ
+ || i == NDS32_INSN_ADD5_PC)
+ forbid_insn_num[i] = 1;
+ /* tlbop/cctl */
+ else if (i == NDS32_INSN_CCTL)
+ forbid_insn_num[i] = 1;
+ /* dprefi/dpref */
+ else if (i >= NDS32_INSN_DPREFI_D && i <= NDS32_INSN_DPREF)
+ forbid_insn_num[i] = 1;
+ /* llw/scw */
+ else if (i == NDS32_INSN_LLW || i == NDS32_INSN_SCW)
+ forbid_insn_num[i] = 1;
+ /* lwup/swup */
+ else if (i == NDS32_INSN_LWUP || i == NDS32_INSN_SWUP)
+ forbid_insn_num[i] = 1;
+ /* trap/teqz/tnez */
+ else if (i >= NDS32_INSN_TRAP && i <= NDS32_INSN_TNEZ)
+ forbid_insn_num[i] = 1;
+ /* lbup/sbup */
+ else if (i == NDS32_INSN_LBUP || i == NDS32_INSN_SBUP)
+ forbid_insn_num[i] = 1;
+ /* jr.toff/jr.itoff */
+ /* ret.toff/ret.itoff */
+ else if (i >= NDS32_INSN_JR_ITOFF && i <= NDS32_INSN_RET_TOFF)
+ forbid_insn_num[i] = 1;
+ /* jral.iton/jral.ton */
+ else if (i == NDS32_INSN_JRAL_ITON || i == NDS32_INSN_JRAL_TON)
+ forbid_insn_num[i] = 1;
+ }
+}
+
+static void
+alarm_forbid_insn (char const *opcode)
+{
+ if (alarm_block == ALARM_BLOCK_NONE)
+ ;
+ else if (alarm_block == ALARM_BLOCK_BAD)
+ as_bad ("[V3m error]: %s is unsupported instruction", opcode);
+ else if (alarm_block == ALARM_BLOCK_FATAL)
+ as_fatal ("[V3m error]: %s is unsupported instruction", opcode);
+}
+
+static int
+check_forbid_insn (char const *opcode, int insn_num)
+{
+ static int forbid_insn_not_init = 1;
+ CGEN_INSN *insn_entry;
+
+ /* 0: Assemble instruction
+ 1: Forbid and alarm
+ 2: Do translation if could. */
+
+ if (forbid_insn_not_init)
+ {
+ init_forbid_insn_num ();
+ forbid_insn_not_init = 0;
+ }
+
+ if (insn_num < 0)
+ {
+ if (opcode)
+ {
+ insn_entry = hash_find (insn_mnem_hash, opcode);
+ if (insn_entry)
+ {
+ if (forbid_insn_num[insn_entry->base->num] == 1)
+ alarm_forbid_insn (opcode);
+ return forbid_insn_num[insn_entry->base->num];
+ }
+ }
+ return 2;
+ }
+
+ if (forbid_insn_num[insn_num] == 1)
+ alarm_forbid_insn (opcode);
+ return forbid_insn_num[insn_num];
+}
+
+static CGEN_INSN_TABLE *
+nds32_init_insn_num_map (void)
+{
+ unsigned int i;
+ CGEN_INSN_TABLE *insn_table;
+ CGEN_INSN_TABLE *macro_insn_table;
+ CGEN_INSN *insn_entry;
+ char *alias_name;
+
+ insn_table = &gas_cgen_cpu_desc->insn_table;
+ macro_insn_table = &gas_cgen_cpu_desc->macro_insn_table;
+
+ if (insn_mnem_hash)
+ return NULL;
+
+ insn_mnem_hash = hash_new ();
+ for (i = 0; insn_table && i < insn_table->num_init_entries; i++)
+ {
+ if (insn_table->init_entries[i].base == NULL
+ || insn_table->init_entries[i].base->mnemonic == NULL)
+ continue;
+ hash_insert (insn_mnem_hash, insn_table->init_entries[i].base->mnemonic,
+ (void *) &insn_table->init_entries[i]);
+ }
+
+ nds32_cgen_nomap_hash = hash_new ();
+ for (i = 0; nds32_cgen_nomap_table[i][0] != NULL; i++)
+ {
+ hash_insert (nds32_cgen_nomap_hash, nds32_cgen_nomap_table[i][0],
+ (void *) nds32_cgen_nomap_table[i][1]);
+ }
+
+ for (i = 0; macro_insn_table && i < macro_insn_table->num_init_entries; i++)
+ {
+ if (macro_insn_table->init_entries[i].base == NULL
+ || macro_insn_table->init_entries[i].base->mnemonic == NULL)
+ continue;
+ insn_entry = hash_find (insn_mnem_hash,
+ macro_insn_table->init_entries[i].base->mnemonic);
+
+ if (insn_entry == NULL)
+ {
+ alias_name = hash_find (nds32_cgen_nomap_hash,
+ macro_insn_table->init_entries[i].base->mnemonic);
+ /* COLE: Why alias matters assembler? */
+ if (alias_name == NULL)
+ continue;
+
+ insn_entry = hash_find (insn_mnem_hash, alias_name);
+ if (insn_entry)
+ hash_insert (insn_mnem_hash,
+ macro_insn_table->init_entries[i].base->mnemonic,
+ insn_entry);
+ }
+ else if (insn_entry->base->num == -1)
+ {
+ fprintf (stderr, "Mnemonic \"%s\" not found in instruction table\n",
+ insn_entry->base->mnemonic);
+ }
+ }
+ return insn_table;
+}
+
+/* Andes built-in functions for macro expansion.
+
+ We support up to MAX_MACRO_LEVEL levels of nested macro expansion. Also,
+ each level can have its own local variables and labels.
+
+ Keep track of local labels so we can substitute them before GAS sees them
+ since macros use their own 'namespace' for local labels and a separate hash.
+
+ We do our own local label handling because it's subtly different from the
+ stock GAS handling.
+
+ We use our own macro nesting counter, since GAS overloads it when expanding
+ other things (like conditionals and repeat loops). */
+
+/* Registers are colored. */
+#define ADDRESSABLE_3BIT 0x1
+#define ADDRESSABLE_4BIT 0x2
+#define ADDRESSABLE_5BIT 0x4
+
+typedef struct
+{
+ const char *name;
+ const int index;
+ const int flag;
+ /* 0x01 - 3-bit only.
+ 0x02 - 4-bit only.
+ 0x04 - 5-bit only.
+ 0x06 - 4/5-bit only.
+ 0x07 - all 3 modes. */
+} nds32_regs;
+
+nds32_regs reduce_regs[] = {
+ /* Standard names. */
+ {"$r0", 0, 7}, {"$r1", 1, 7}, {"$r2", 2, 7}, {"$r3", 3, 7},
+ {"$r4", 4, 7}, {"$r5", 5, 7}, {"$r6", 6, 7}, {"$r7", 7, 7},
+ {"$r8", 8, 6}, {"$r9", 9, 6}, {"$r10", 10, 6},
+ {"$r15", 15, 4},
+ {"$r28", 28, 4}, {"$r29", 29, 4}, {"$r30", 30, 4}, {"$r31", 31, 4},
+ /* Names for parameter passing. */
+ {"$a0", 0, 7}, {"$a1", 1, 7}, {"$a2", 2, 7}, {"$a3", 3, 7},
+ {"$a4", 4, 7}, {"$a5", 5, 7},
+ /* Names reserved for 5-bit addressing only. */
+ {"$s0", 6, 4}, {"$s1", 7, 4}, {"$s2", 8, 4}, {"$s3", 9, 4},
+ {"$s4", 10, 4},
+ {"$s9", 28, 4},
+ {"$ta", 15, 4},
+ {"$fp", 28, 4}, {"$gp", 29, 4}, {"$lp", 30, 4}, {"$sp", 31, 4},
+ /* Names reserved for 4-bit addressing only. */
+ {"$h0", 0, 2}, {"$h1", 1, 2}, {"$h2", 2, 2}, {"$h3", 3, 2},
+ {"$h4", 4, 2}, {"$h5", 5, 2}, {"$h6", 6, 2}, {"$h7", 7, 2},
+ {"$h8", 8, 2}, {"$h9", 9, 2}, {"$h10", 10, 2},
+ /* Names reserved for 3-bit addressing only. */
+ {"$o0", 0, 1}, {"$o1", 1, 1}, {"$o2", 2, 1}, {"$o3", 3, 1},
+ {"$o4", 4, 1}, {"$o5", 5, 1}, {"$o6", 6, 1}, {"$o7", 7, 1},
+ /* Names reserved for multiply and divide. */
+ {"$d0", 0, 7}, {"$d0.lo", 0, 7}, {"$d0.hi", 0, 7}, {"$d1", 0, 7},
+ {"$d1.lo", 0, 7}, {"$d1.hi", 0, 7},
+ {NULL, 0, 0}
+};
+
+nds32_regs fs_regs[] = {
+ /* Standard names. */
+ {"$fs0", 0, 7}, {"$fs1", 1, 7}, {"$fs2", 2, 7}, {"$fs3", 3, 7},
+ {"$fs4", 4, 7}, {"$fs5", 5, 7}, {"$fs6", 6, 7}, {"$fs7", 7, 7},
+ {"$fs8", 8, 6}, {"$fs9", 9, 6}, {"$fs10", 10, 6}, {"$fs11", 11, 6},
+ {"$fs12", 12, 4}, {"$fs13", 13, 4}, {"$fs14", 14, 4}, {"$fs15", 15, 4},
+ {"$fs16", 16, 6}, {"$fs17", 17, 6}, {"$fs18", 18, 6}, {"$fs19", 19, 6},
+ {"$fs20", 20, 4}, {"$fs21", 21, 4}, {"$fs22", 22, 4}, {"$fs23", 23, 4},
+ {"$fs24", 24, 4}, {"$fs25", 25, 4}, {"$fs26", 26, 4}, {"$fs27", 27, 4},
+ {"$fs28", 28, 4}, {"$fs29", 29, 4}, {"$fs30", 30, 4}, {"$fs31", 31, 4},
+ {NULL, 0, 0}
+};
+
+nds32_regs fd_regs[] = {
+ /* Standard names. */
+ {"$fd0", 0, 7}, {"$fd1", 1, 7}, {"$fd2", 2, 7}, {"$fd3", 3, 7},
+ {"$fd4", 4, 7}, {"$fd5", 5, 7}, {"$fd6", 6, 7}, {"$fd7", 7, 7},
+ {"$fd8", 8, 6}, {"$fd9", 9, 6}, {"$fd10", 10, 6}, {"$fd11", 11, 6},
+ {"$fd12", 12, 4}, {"$fd13", 13, 4}, {"$fd14", 14, 4}, {"$fd15", 15, 4},
+ {"$fd16", 16, 6}, {"$fd17", 17, 6}, {"$fd18", 18, 6}, {"$fd19", 19, 6},
+ {"$fd20", 20, 4}, {"$fd21", 21, 4}, {"$fd22", 22, 4}, {"$fd23", 23, 4},
+ {"$fd24", 24, 4}, {"$fd25", 25, 4}, {"$fd26", 26, 4}, {"$fd27", 27, 4},
+ {"$fd28", 28, 4}, {"$fd29", 29, 4}, {"$fd30", 30, 4}, {"$fd31", 31, 4},
+ {NULL, 0, 0}
+};
+
+nds32_regs regs[] = {
+ /* Standard names. */
+ {"$r0", 0, 7}, {"$r1", 1, 7}, {"$r2", 2, 7}, {"$r3", 3, 7},
+ {"$r4", 4, 7}, {"$r5", 5, 7}, {"$r6", 6, 7}, {"$r7", 7, 7},
+ {"$r8", 8, 6}, {"$r9", 9, 6}, {"$r10", 10, 6}, {"$r11", 11, 6},
+ {"$r12", 12, 4}, {"$r13", 13, 4}, {"$r14", 14, 4}, {"$r15", 15, 4},
+ {"$r16", 16, 6}, {"$r17", 17, 6}, {"$r18", 18, 6}, {"$r19", 19, 6},
+ {"$r20", 20, 4}, {"$r21", 21, 4}, {"$r22", 22, 4}, {"$r23", 23, 4},
+ {"$r24", 24, 4}, {"$r25", 25, 4}, {"$r26", 26, 4}, {"$r27", 27, 4},
+ {"$r28", 28, 4}, {"$r29", 29, 4}, {"$r30", 30, 4}, {"$r31", 31, 4},
+ /* Names for parameter passing. */
+ {"$a0", 0, 7}, {"$a1", 1, 7}, {"$a2", 2, 7}, {"$a3", 3, 7},
+ {"$a4", 4, 7}, {"$a5", 5, 7},
+ /* Names reserved for 5-bit addressing only. */
+ {"$s0", 6, 4}, {"$s1", 7, 4}, {"$s2", 8, 4}, {"$s3", 9, 4},
+ {"$s4", 10, 4}, {"$s5", 11, 4}, {"$s6", 12, 4}, {"$s7", 13, 4},
+ {"$s8", 14, 4}, {"$s9", 28, 4},
+ {"$ta", 15, 4},
+ {"$t0", 16, 4}, {"$t1", 17, 4}, {"$t2", 18, 4}, {"$t3", 19, 4},
+ {"$t4", 20, 4}, {"$t5", 21, 4}, {"$t6", 22, 4}, {"$t7", 23, 4},
+ {"$t8", 24, 4}, {"$t9", 25, 4},
+ {"$p0", 26, 4}, {"$p1", 27, 4},
+ {"$fp", 28, 4}, {"$gp", 29, 4}, {"$lp", 30, 4}, {"$sp", 31, 4},
+ /* Names reserved for 4-bit addressing only. */
+ {"$h0", 0, 2}, {"$h1", 1, 2}, {"$h2", 2, 2}, {"$h3", 3, 2},
+ {"$h4", 4, 2}, {"$h5", 5, 2}, {"$h6", 6, 2}, {"$h7", 7, 2},
+ {"$h8", 8, 2}, {"$h9", 9, 2}, {"$h10", 10, 2}, {"$h11", 11, 2},
+ {"$h12", 16, 2}, {"$h13", 17, 2}, {"$h14", 18, 2}, {"$h15", 19, 2},
+ /* Names reserved for 3-bit addressing only. */
+ {"$o0", 0, 1}, {"$o1", 1, 1}, {"$o2", 2, 1}, {"$o3", 3, 1},
+ {"$o4", 4, 1}, {"$o5", 5, 1}, {"$o6", 6, 1}, {"$o7", 7, 1},
+ {NULL, 0, 0}
+};
+
+#define MAX_MACRO_LEVEL 32
+static int macro_level = 0;
+static struct hash_control *local_label_hash[MAX_MACRO_LEVEL];
+/* Prevent infinite recurse. */
+static struct hash_control *builtin_recurse_hash;
+/* Level 0 is the main substitution symbol table. */
+static struct hash_control *builtin_hash[MAX_MACRO_LEVEL];
+/* Registers. */
+static struct hash_control *reg5_hash;
+static struct hash_control *reg4_hash;
+static struct hash_control *reg3_hash;
+static struct hash_control *fs5_hash;
+static struct hash_control *fd5_hash;
+
+static struct hash_control *bfdsym_hash;
+
+/* This ensures that all new labels are unique. */
+static int local_label_id = 0;
+
+/* Built-in substitution symbol functions. */
+typedef struct
+{
+ char *name;
+ int (*proc) (char *, char *);
+ int nargs;
+} builtin_proc_entry;
+
+/* This function looks up the substitution string replacement for the given
+ symbol. It starts with the innermost macro substitution table
given and work
+ outwards. */
+
+static char *
+builtin_lookup (char *name, int nest_level)
+{
+ char *value;
+
+ value = hash_find (builtin_hash[nest_level], name);
+
+ if (value || nest_level == 0)
+ return value;
+
+ return builtin_lookup (name, nest_level - 1);
+}
+
+/* This function replaces the given substitution string. We start at the
+ innermost macro level, so that existing locals remain local.
+ Note: we're treating macro arguments identically to .var's. */
+
+static void
+builtin_create_or_replace (char *name, char *value)
+{
+ int i;
+
+ for (i = macro_level; i >= 0; i--)
+ {
+ if (hash_find (builtin_hash[i], name))
+ {
+ hash_replace (builtin_hash[i], name, value);
+ return;
+ }
+ }
+ hash_insert (builtin_hash[0], name, value);
+}
+
+/* Mutually recursive with builtin_get_arg. */
+static char *builtin_substitute (char *line, int forced);
+
+/* This function returns copy of line up to closing quote if quote found;
+ otherwise up until terminator. If it's a string, pass as-is; otherwise
+ attempt substitution symbol replacement on the value. */
+
+static char *
+builtin_get_arg (char *line, char *terminators, char **str, int nosub)
+{
+ char *ptr = line;
+ char *endp;
+ int is_string = *line == '"';
+ int is_char = ISDIGIT (*line);
+
+ if (is_char)
+ {
+ /* What other form of digits? */
+ if (ptr[0] == '0' && (TOLOWER (ptr[1]) == 'x' || TOLOWER
(ptr[1]) == 'b'))
+ ptr += 2;
+ while (ISXDIGIT (*ptr))
+ ++ptr;
+ if (*ptr == 'b' || *ptr == 'f')
+ /* Local label? Accept it. */
+ ++ ptr;
+ endp = ptr;
+ *str = xmalloc (ptr - line + 1);
+ strncpy (*str, line, ptr - line);
+ (*str)[ptr - line] = 0;
+ }
+ else if (is_string)
+ {
+ char *savedp = input_line_pointer;
+ char *tmp;
+ int len;
+
+ input_line_pointer = ptr;
+ tmp = demand_copy_C_string (&len);
+ endp = input_line_pointer;
+ input_line_pointer = savedp;
+
+ /* Do forced substitutions if requested. */
+ if (!nosub && *tmp == ':')
+ {
+ *str = builtin_substitute (tmp, 1);
+ if (*str != tmp)
+ free (tmp);
+ }
+ else
+ *str = tmp;
+ }
+ else
+ {
+ char *term = terminators;
+ char *value = NULL;
+ int need_right_pren = 0;
+
+ while (1)
+ {
+ while (*ptr && *ptr != *term)
+ {
+ if (!*term)
+ {
+ term = terminators;
+ if (*ptr == '(')
+ need_right_pren++;
+ ++ptr;
+ }
+ else
+ ++term;
+ }
+
+ if (*ptr == 0)
+ {
+ break;
+ }
+ else if (need_right_pren)
+ {
+ term = terminators;
+ if (*ptr == ')')
+ need_right_pren--;
+ ptr++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ endp = ptr;
+ *str = xmalloc (ptr - line + 1);
+ strncpy (*str, line, ptr - line);
+ (*str)[ptr - line] = 0;
+ /* Do simple substitution, if available. */
+ if (!nosub && (value = builtin_lookup (*str, macro_level)) != NULL)
+ *str = value;
+ }
+
+ return endp;
+}
+
+/* This functions does substitution-symbol replacement on the given line
+ (recursively). It returns the argument if no substitution was
done. It also
+ looks for built-in functions ($func (arg)) and local labels. If FORCED is
+ set, look for forced substitutions of the form ':SYMBOL:'. */
+
+static char *
+builtin_substitute (char *line, int forced)
+{
+ /* For each apparent symbol, see if it's a substitution symbol,
+ and if so, replace it in the input. */
+ char *replacement; /* Current replacement for LINE. */
+ char *head; /* Start of line. */
+ char *ptr; /* Current examination point. */
+ int changed = 0; /* Did we make a substitution? */
+ int eval_line = 0; /* Is this line a .eval/.asg statement? */
+ int eval_symbol = 0; /* Are we in the middle of the symbol for
.eval/.asg? */
+ char *eval_end = NULL;
+ int recurse = 1;
+ int inRI5 = 0;
+ char *tmp;
+
+ /* If it's a macro definition, don't do substitution on the argument names.
+ If it's a character string, don't do substitution on the arguments. */
+ if (nds32_strcasestr (line, ".macro") || nds32_strcasestr (line, ".ascii")
+ || nds32_strcasestr (line, ".string") || nds32_strcasestr
(line, ".asciz"))
+ return line;
+
+ /* Watch out for .eval, so that we avoid doing substitution on the
+ symbol being assigned a value. */
+ if (nds32_strcasestr (line, ".eval") || nds32_strcasestr (line, ".asg"))
+ eval_line = 1;
+
+ /* Work with a copy of the input line. */
+ replacement = xmalloc (strlen (line) + 4);
+ strcpy (replacement, line);
+
+ ptr = head = replacement;
+
+ while (!is_end_of_line[(unsigned char) *ptr])
+ {
+ int current_char = *ptr;
+
+ /* Need to update this since LINE may have been modified. */
+ if (eval_line)
+ eval_end = strrchr (ptr, ',');
+
+ /* Replace triple double quotes with bounding quote/escapes. */
+ if (current_char == '"' && ptr[1] == '"' && ptr[2] == '"')
+ {
+ ptr[1] = '\\';
+ tmp = strstr (ptr + 2, "\"\"\"");
+ if (tmp)
+ tmp[0] = '\\';
+ changed = 1;
+ }
+
+ /* Flag when we've reached the symbol part of .eval/.asg. */
+ if (eval_line && ptr >= eval_end)
+ eval_symbol = 1;
+
+ /* For each apparent symbol, see if it's a substitution symbol,
+ and if so, replace it in the input. */
+ if ((forced && current_char == ':')
+ || (!forced && is_name_beginner (current_char)))
+ {
+ char *name; /* Symbol to be replaced. */
+ char *savedp = input_line_pointer;
+ int c;
+ char *value = NULL;
+ char *tail; /* Rest of line after symbol. */
+
+ /* Skip the colon. */
+ if (forced)
+ ++ptr;
+
+ name = input_line_pointer = ptr;
+ c = get_symbol_end ();
+
+ /* Avoid infinite recursion; if a symbol shows up a second time for
+ substitution, leave it as is. */
+ if (hash_find (builtin_recurse_hash, name) == NULL)
+ value = builtin_lookup (name, macro_level);
+ else
+ as_warn (_("%s symbol recursion stopped at "
+ "second appearance of '%s'"),
+ forced ? "Forced substitution" : "Substitution", name);
+ ptr = tail = input_line_pointer;
+ input_line_pointer = savedp;
+
+ /* Check for local labels; replace them with the appropriate
substitution. */
+ if ((*name == '$' && ISDIGIT (name[1]) && name[2] == '\0')
+ || name[strlen (name) - 1] == '?')
+ {
+ /* Use an existing identifier for that label if, available, or
+ create a new, unique identifier. */
+ value = hash_find (local_label_hash[macro_level], name);
+ if (value == NULL)
+ {
+ char digit[11];
+ char *namecopy = strcpy (xmalloc (strlen (name) + 1), name);
+
+ value = strcpy (xmalloc (strlen (name) + sizeof (digit) + 1),
+ name);
+ if (*value != '$')
+ value[strlen (value) - 1] = '\0';
+ sprintf (digit, ".%d", local_label_id++);
+ strcat (value, digit);
+ hash_insert (local_label_hash[macro_level], namecopy,
+ value);
+ }
+ /* Indicate where to continue looking for substitutions. */
+ ptr = tail;
+ }
+ /* Check for built-in subsym and math functions. */
+ else if (value != NULL && *name == '$')
+ {
+ if (macro_level == 0)
+ {
+ as_bad (_("Built-in function is for writing macro only"));
+ break;
+ }
+ else
+ {
+ builtin_proc_entry *entry = (builtin_proc_entry *) value;
+ char *arg1, *arg2 = NULL;
+ int val;
+
+ *ptr = c;
+ if (entry == NULL)
+ {
+ as_bad (_("Unrecognized substitution symbol function"));
+ break;
+ }
+ else if (*ptr != '(')
+ {
+ as_bad (_("Missing '(' after substitution symbol function"));
+ break;
+ }
+ ++ptr;
+
+ if (entry->nargs == 0)
+ arg1 = NULL;
+ else
+ {
+ int arg_type[2] = {0};
+ int ismember = !strcmp (entry->name, "$ismember");
+
+ arg_type[0] = *ptr == '"';
+
+ /* Parse one or two args, which must be a substitution
+ symbol, string or a character-string constant.
+ For all functions, a string or substitution symbol may be
+ used, with the following exceptions:
+ firstch/lastch: 2nd arg must be character constant
+ ismember: both args must be substitution symbols. */
+ ptr = builtin_get_arg (ptr, ",)", &arg1, ismember);
+ if (!arg1)
+ break;
+ if (entry->nargs == 2)
+ {
+ if (*ptr++ != ',')
+ {
+ as_bad (_("Function expects two arguments"));
+ break;
+ }
+ /* Character constants are converted to numerics
+ by the preprocessor. */
+ arg_type[1] = (ISDIGIT (*ptr)) ? 2 : (*ptr == '"');
+ ptr = builtin_get_arg (ptr, ")", &arg2, ismember);
+ }
+
+ /* Args checking. */
+ if ((!strcmp (entry->name, "$firstch")
+ || !strcmp (entry->name, "$lastch"))
+ && arg_type[1] != 2)
+ {
+ as_bad (_("Expecting character constant argument"));
+ break;
+ }
+ if (ismember && (arg_type[0] != 0 || arg_type[1] != 0))
+ {
+ as_bad (_("Both arguments must be substitution symbols"));
+ break;
+ }
+ }
+ if (*ptr++ != ')')
+ {
+ as_bad (_("Extra junk in function call, expecting ')'"));
+ break;
+ }
+ val = (*entry->proc) (arg1, arg2);
+ value = xmalloc (64);
+ sprintf (value, "%d", val);
+
+ /* Fix things up to replace the entire expression,
+ not just the function name. */
+ tail = ptr;
+ c = *tail;
+ }
+ }
+
+ if (value != NULL && !eval_symbol)
+ {
+ /* Replace the symbol with its string replacement and
+ continue. Recursively replace VALUE until either no
+ substitutions are performed, or a substitution that has been
+ previously made is encountered again. */
+
+ /* Put the symbol into the recursion hash table so we only
+ try to replace a symbol once. */
+ if (recurse)
+ {
+ char *old_value = value;
+
+ hash_insert (builtin_recurse_hash, name, name);
+ value = builtin_substitute (old_value, macro_level > 0);
+ if (old_value != value)
+ free (old_value);
+ hash_delete (builtin_recurse_hash, name, FALSE);
+ }
+
+ /* Temporarily zero-terminate where the symbol started. */
+ *name = 0;
+ if (forced)
+ {
+ if (c == '(')
+ {
+ /* Subscripted substitution symbol -- use just the
+ indicated portion of the string; the description
+ kinda indicates that forced substitution is not
+ supposed to be recursive, but I'm not sure. */
+
+ /* Default to a single char. */
+ unsigned beg, len = 1;
+ char *newval = strcpy (xmalloc (strlen (value) + 1), value);
+
+ savedp = input_line_pointer;
+ input_line_pointer = tail + 1;
+ beg = get_absolute_expression ();
+ if (beg < 1)
+ {
+ as_bad (_("Invalid subscript (use 1 to %d)"),
+ (int) strlen (value));
+ break;
+ }
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ len = get_absolute_expression ();
+ if (beg + len > strlen (value))
+ {
+ as_bad (_("Invalid length (use 0 to %d"),
+ (int) (strlen (value) - beg));
+ break;
+ }
+ }
+ newval += beg - 1;
+ newval[len] = 0;
+ tail = input_line_pointer;
+ if (*tail++ != ')')
+ {
+ as_bad (_("Missing ')' in subscripted substitution symbol expression"));
+ break;
+ }
+ c = *tail;
+ input_line_pointer = savedp;
+
+ value = newval;
+ }
+ name[-1] = 0;
+ }
+ tmp = xmalloc (strlen (head) + strlen (value) + strlen (tail + 1)
+ + 2);
+ strcpy (tmp, head);
+ strcat (tmp, value);
+ /* Make sure forced substitutions are properly terminated. */
+ if (forced)
+ {
+ if (c != ':')
+ {
+ as_bad (_("Missing forced substitution terminator ':'"));
+ break;
+ }
+ ++tail;
+ }
+ else
+ *tail = c; /* Restore the character after the symbol end. */
+ strcat (tmp, tail);
+ /* Continue examining after the replacement value. */
+ ptr = tmp + strlen (head) + strlen (value);
+ free (replacement);
+ head = replacement = tmp;
+ changed = 1;
+ }
+ else
+ *ptr = c;
+ }
+ else
+ {
+ /* Need to take care of [reg-offset] issue. */
+ if (ptr[0] == '[')
+ inRI5 = 1;
+ else if (inRI5)
+ {
+ if (ptr[0] == '-')
+ {
+ char *fstr, *tstr;
+
+ if (ISDIGIT (ptr[1]) || ptr[1] == '(')
+ {
+ /* "[reg-offset]" --> "[reg+(-offset)]" */
+ tstr = ptr + strlen (ptr);
+ fstr = strchr (ptr, ']');
+
+ for (; tstr >= fstr; tstr--)
+ tstr[3] = tstr[0];
+ tstr[3] = ')';
+ for (fstr--; fstr >= ptr; fstr--)
+ fstr[2] = fstr[0];
+ ptr[0] = '+';
+ ptr[1] = '(';
+ changed = 1;
+ }
+ else if (ptr[1] == '-')
+ {
+ /* "[reg--offset]" --> "[reg+offset]" */
+ fstr = ptr++;
+ for (*fstr++ = '+'; fstr[1] != '\0'; fstr++)
+ fstr[0] = fstr[1];
+ *fstr = fstr[1];
+ fstr++;
+ *fstr = '\0';
+ changed = 1;
+ }
+ else if (ptr[1] == '+')
+ {
+ /* "[reg-+offset]" --> "[reg+(-offset)]" */
+ tstr = ptr + strlen (ptr);
+ fstr = strchr (ptr, ']');
+
+ for (; tstr >= fstr; tstr--)
+ tstr[2] = tstr[0];
+ tstr[2] = ')';
+ for (fstr--; fstr > ptr; fstr--)
+ fstr[1] = fstr[0];
+ ptr[0] = '+';
+ ptr[1] = '(';
+ ptr[2] = '-';
+ changed = 1;
+ }
+ }
+ inRI5 = 0;
+ }
+ ++ptr;
+ }
+ }
+
+ if (changed)
+ return replacement;
+ else
+ {
+ /* No changes. Free work copy first. */
+ free (replacement);
+ return line;
+ }
+}
+
+/* This function returns the length of symbol A. */
+
+static int
+builtin_strlen (char *a, char *ignore ATTRIBUTE_UNUSED)
+{
+ return strlen (a);
+}
+
+/* This function compares symbol A to string B. */
+
+static int
+builtin_strcmp (char *a, char *b)
+{
+ return strcmp (a, b);
+}
+
+/* This function compares symbol A to string B ignoring case. */
+
+static int
+builtin_strcasecmp (char *a, char *b)
+{
+ return strcasecmp (a, b);
+}
+
+/* This function checks whether char B is found in string A. */
+
+static int
+builtin_strchr (char *a, char *b)
+{
+ return strchr (a, *b) != NULL;
+}
+
+/* This function checks whether string B is found in string A. */
+
+static int
+builtin_strstr (char *a, char *b)
+{
+ return strstr (a, b) != NULL;
+}
+
+/* This function checks whether string B is found in string A
ignoring case. */
+
+static int
+builtin_strcasestr (char *a, char *b)
+{
+ return nds32_strcasestr (a, b) != NULL;
+}
+
+/* This function returns the index of the first occurrence of B in A, or zero
+ if none assumes B is an integer char value as a string. Index is
one-based. */
+
+static int
+builtin_firstch (char *a, char *b)
+{
+ int val = atoi (b);
+ char *tmp = strchr (a, val);
+
+ return tmp ? tmp - a + 1 : 0;
+}
+
+/* This function returns index of last occurrence of B in A. */
+
+static int
+builtin_lastch (char *a, char *b)
+{
+ int val = atoi (b);
+ char *tmp = strrchr (a, val);
+
+ return tmp ? tmp - a + 1 : 0;
+}
+
+/* This function returns 1 if string A is defined in the symbol table
+ (NOT the substitution symbol table). */
+
+static int
+builtin_isdefed (char *a, char *ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *symbolP = symbol_find (a);
+
+ return symbolP != NULL;
+}
+
+/* This function assigns first member of comma-separated list B (e.g. "1,2,3")
+ to the symbol A, or zero if B is a null string. Both arguments *must* be
+ substitution symbols, unsubstituted. */
+
+static int
+builtin_ismember (char *sym, char *list)
+{
+ char *elem, *ptr, *listv;
+
+ if (!list)
+ return 0;
+
+ listv = builtin_lookup (list, macro_level);
+ if (!listv)
+ {
+ as_bad (_("Undefined substitution symbol '%s'"), list);
+ ignore_rest_of_line ();
+ return 0;
+ }
+
+ ptr = elem = xmalloc (strlen (listv) + 1);
+ strcpy (elem, listv);
+ while (*ptr && *ptr != ',')
+ ++ptr;
+ *ptr++ = 0;
+
+ builtin_create_or_replace (sym, elem);
+
+ /* Reassign the list. */
+ builtin_create_or_replace (list, ptr);
+
+ /* Assume this value, docs aren't clear. */
+ return *list != 0;
+}
+
+/* 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);
+ s = input_line_pointer;
+ input_line_pointer = tmp; /* Restore line pointer. */
+
+ return s; /* Return pointer to where parsing stopped. */
+}
+
+/* This function returns zero if not a constant; otherwise:
+ 1 if binary
+ 2 if octal
+ 3 if hexadecimal
+ 4 if character
+ 5 if decimal. */
+
+static int
+builtin_iscons (char *a, char *ignore ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+
+ parse_expression (a, &exp);
+
+ if (exp.X_op == O_constant)
+ {
+ int len = strlen (a);
+
+ /* No suffix; either octal, hex, or decimal. */
+ if (*a == '0' && len > 1)
+ {
+ if (TOUPPER (a[1]) == 'B')
+ return 1;
+ else if (TOUPPER (a[1]) == 'X')
+ return 3;
+ else
+ return 2;
+ }
+
+ return 5;
+ }
+
+ return 0;
+}
+
+/* This function returns 1 if given argument is an odd integer and 0
otherwise. */
+
+static int
+builtin_isodd (char *a, char *ignore ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+
+ parse_expression (a, &exp);
+
+ if (exp.X_op == O_constant)
+ return exp.X_add_number % 2;
+ else
+ return 0;
+}
+
+/* This function returns 1 if A is a valid symbol name. */
+
+static int
+builtin_isname (char *a, char *ignore ATTRIBUTE_UNUSED)
+{
+ if (!is_name_beginner (*a))
+ return 0;
+ while (*a)
+ {
+ if (!is_part_of_name (*a))
+ return 0;
+ ++a;
+ }
+
+ return 1;
+}
+
+/* This function returns whether the string is a 5-bit addressable
register. */
+
+static int
+builtin_isreg5 (char *a, char *ignore ATTRIBUTE_UNUSED)
+{
+ if (hash_find (reg5_hash, a))
+ return 1;
+
+ return 0;
+}
+
+/* This function returns whether the string is an RI expression
+ - an expression of register +/- immediate. */
+
+static int
+builtin_isRI5 (char *a, char *ignore ATTRIBUTE_UNUSED)
+{
+ char *pstr = strpbrk (a, "+-");
+
+ if (pstr == NULL)
+ return builtin_isreg5 (a, ignore);
+ else
+ {
+ char ch = *pstr;
+
+ /* R+I? */
+ *pstr = '\0';
+ if (hash_find (reg5_hash, a))
+ {
+ *pstr = ch;
+ return builtin_iscons (pstr + 1, ignore);
+ }
+
+ return 0;
+ }
+}
+
+/* This function returns whether the string is a 4-bit addressed register. */
+
+static int
+builtin_isreg4 (char *a, char *ignore ATTRIBUTE_UNUSED)
+{
+ if (hash_find (reg4_hash, a))
+ return 1;
+
+ return 0;
+}
+
+/* This function returns whether the string is a 3-bit addressed register. */
+
+static int
+builtin_isreg3 (char *a, char *ignore ATTRIBUTE_UNUSED)
+{
+ if (hash_find (reg3_hash, a))
+ return 1;
+
+ return 0;
+}
+
+/* This function returns whether the string is a register. */
+
+static int
+builtin_isreg (char *a, char *ignore ATTRIBUTE_UNUSED)
+{
+ if (hash_find (reg5_hash, a)
+ || hash_find (reg4_hash, a) || hash_find (reg3_hash, a))
+ return 1;
+
+ return 0;
+}
+
+/* This function returns the register number of the given GPR. */
+
+static int
+builtin_regnum (char *a, char *ignore ATTRIBUTE_UNUSED)
+{
+ nds32_regs *sym;
+
+ if ((sym = (nds32_regs *) hash_find (reg5_hash, a))
+ || (sym = (nds32_regs *) hash_find (reg4_hash, a))
+ || (sym = (nds32_regs *) hash_find (reg3_hash, a)))
+ return sym->index;
+ else
+ return -1;
+}
+
+/* This function returns the register number of the given GPR. */
+
+static int
+builtin_mask4bit (char *a, char *b)
+{
+ nds32_regs *sym1 = (nds32_regs *) hash_find (reg5_hash, a);
+ nds32_regs *sym2 = (nds32_regs *) hash_find (reg5_hash, b);
+
+ if (sym1 == NULL || sym2 == NULL || sym1->index < 28 || sym2->index < 28)
+ return -1;
+ else
+ {
+ int i, j, k;
+
+ for (i = sym1->index, j = 8 >> (sym1->index - 28), k = 0;
+ i <= sym2->index; i++)
+ {
+ k |= j;
+ j >>= 1;
+ }
+
+ return k;
+ }
+}
+
+
+/* This function returns the pic_code flag. */
+
+static int
+builtin_genpic (char *a ATTRIBUTE_UNUSED, char *ignore ATTRIBUTE_UNUSED)
+{
+ return pic_code;
+}
+
+/* Extract addend after symbol.
+ FIXME: Remove this. */
+
+static int
+builtin_addend (const char *sym, char *ignore ATTRIBUTE_UNUSED)
+{
+ while (*sym != '+' && *sym != '-' && *sym)
+ ++sym;
+
+ if (*sym == 0)
+ return 0;
+ else
+ return strtol (sym, NULL, 0);
+}
+
+/* Assembler built-in string substitution functions. */
+static const builtin_proc_entry builtin_procs[] = {
+ {"$strlen", builtin_strlen, 1},
+ {"$strcmp", builtin_strcmp, 2},
+ {"$strcasecmp", builtin_strcasecmp, 2},
+ {"$strchr", builtin_strchr, 2},
+ {"$strstr", builtin_strstr, 2},
+ {"$strcasestr", builtin_strcasestr, 2},
+ {"$firstch", builtin_firstch, 2},
+ {"$lastch", builtin_lastch, 2},
+ {"$isdefed", builtin_isdefed, 1},
+ {"$ismember", builtin_ismember, 2},
+ {"$iscons", builtin_iscons, 1},
+ {"$isodd", builtin_isodd, 1},
+ {"$isname", builtin_isname, 1},
+ {"$isreg5", builtin_isreg5, 1},
+ {"$isRI5", builtin_isRI5, 1},
+ {"$isreg4", builtin_isreg4, 1},
+ {"$isreg3", builtin_isreg3, 1},
+ {"$isreg", builtin_isreg, 1},
+ {"$regnum", builtin_regnum, 1},
+ {"$mask4bit", builtin_mask4bit, 2},
+ {"$genpic", builtin_genpic, 0},
+ {NULL, NULL, 0}
+};
+
+/*
+ * Pseudo opcodes
+ */
+
+typedef void (*nds32_pseudo_opcode_func) (int argc, char *argv[], int pv);
+struct nds32_pseudo_opcode
+{
+ const char *opcode;
+ int argc;
+ nds32_pseudo_opcode_func proc;
+ int pseudo_val;
+
+ /* Some instructions are not pseudo opcode, but they might sill 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 'compiler_generated_asm' is nozero and this opcode
+ is a physical instruction, we should not expand it. */
+ int physical_op;
+};
+#define PV_DONT_CARE 0
+
+static struct hash_control *nds32_pseudo_opcode_hash = NULL;
+
+static void
+md_assemblef (char *format, ...)
+{
+ char line[1024]; /* FIXME: hope this is long enough. */
+ va_list ap;
+
+ va_start (ap, format);
+ vsnprintf (line, sizeof (line), format, ap);
+ md_assemble (line);
+}
+
+static void
+as_bad_no16 (void)
+{
+ as_bad (_("option specified no 16-bit instructions."));
+}
+
+/* Some prototypes here, since some op may use another op. */
+static void do_pseudo_li_internal (char *rt, int imm32s);
+static void do_pseudo_move_reg_internal (char *dst, char *src);
+static void do_pseudo_jral (int argc, char *argv[], int pv);
+
+static void
+do_pseudo_b (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ char *arg_label = argv[0];
+ /* b label */
+ if (pic_code
+ && (strstr (arg_label, "@GOT") || strstr (arg_label, "@PLT")))
+ {
+ md_assemblef ("sethi $ta,hi20(%s)", arg_label);
+ md_assemblef ("ori $ta,$ta,lo12(%s)", arg_label);
+ md_assemble ("add $ta,$ta,$gp");
+ md_assemblef ("jr $ta");
+ }
+ else
+ {
+ if (disable_16bit)
+ md_assemblef ("j_r %s", arg_label);
+ else
+ md_assemblef ("j8_r %s", arg_label);
+ }
+}
+
+static void
+do_pseudo_bal (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ char *arg_label = argv[0];
+ /* bal|call label */
+ if (pic_code
+ && (strstr (arg_label, "@GOT") || strstr (arg_label, "@PLT")))
+ {
+ md_assemblef ("sethi $ta,hi20(%s)", arg_label);
+ md_assemblef ("ori $ta,$ta,lo12(%s)", arg_label);
+ md_assemble ("add $ta,$ta,$gp");
+ md_assemblef ("jral $ta");
+ }
+ else
+ {
+ md_assemblef ("jal_r %s", arg_label);
+ }
+}
+
+static void
+do_pseudo_beq (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* beq rt5, ra5, label */
+ /* TODO check rt5 == ra5 */
+ md_assemblef ("beq_r %s,%s,%s", argv[0], argv[1], argv[2]);
+}
+
+static void
+do_pseudo_beqs38 (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* beqs38 rt3, label */
+ /* TODO check rt3 == R5 */
+ if (disable_16bit)
+ as_bad_no16 ();
+ md_assemblef ("beqs38_r %s,%s", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_beqz (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ md_assemblef ("beqz_r %s,%s", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_beqz38 (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ if (disable_16bit)
+ as_bad_no16 ();
+ md_assemblef ("beqz38_r %s,%s", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_beqzs8 (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ if (disable_16bit)
+ as_bad_no16 ();
+ md_assemblef ("beqzs8_r %s", argv[0]);
+}
+
+static void
+do_pseudo_bge (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* rt5, ra5, label */
+ if (!disable_16bit && builtin_isreg4 (argv[0], NULL))
+ {
+ md_assemblef ("slt45 %s,%s", argv[0], argv[1]);
+ md_assemblef ("beqzs8_r %s", argv[2]);
+ }
+ else
+ {
+ md_assemblef ("slt $ta,%s,%s", argv[0], argv[1]);
+ md_assemblef ("beqz_r $ta,%s", argv[2]);
+ }
+}
+
+static void
+do_pseudo_bges (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* rt5, ra5, label */
+ if (!disable_16bit && builtin_isreg4 (argv[0], NULL))
+ {
+ md_assemblef ("slts45 %s,%s", argv[0], argv[1]);
+ md_assemblef ("beqzs8_r %s", argv[2]);
+ }
+ else
+ {
+ md_assemblef ("slts $ta,%s,%s", argv[0], argv[1]);
+ md_assemblef ("beqz_r $ta,%s", argv[2]);
+ }
+}
+
+static void
+do_pseudo_bgez (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* rt5, label */
+ md_assemblef ("bgez_r %s,%s", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_bgezal (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* rt5, label */
+ md_assemblef ("bgezal_r %s,%s", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_bgt (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* bgt rt5, ra5, label */
+ if (!disable_16bit && builtin_isreg4 (argv[1], NULL))
+ {
+ md_assemblef ("slt45 %s,%s", argv[1], argv[0]);
+ md_assemblef ("bnezs8_r %s", argv[2]);
+ }
+ else
+ {
+ md_assemblef ("slt $ta,%s,%s", argv[1], argv[0]);
+ md_assemblef ("bnez_r $ta,%s", argv[2]);
+ }
+}
+
+static void
+do_pseudo_bgts (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* bgt rt5, ra5, label */
+ if (!disable_16bit && builtin_isreg4 (argv[1], NULL))
+ {
+ md_assemblef ("slts45 %s,%s", argv[1], argv[0]);
+ md_assemblef ("bnezs8_r %s", argv[2]);
+ }
+ else
+ {
+ md_assemblef ("slts $ta,%s,%s", argv[1], argv[0]);
+ md_assemblef ("bnez_r $ta,%s", argv[2]);
+ }
+}
+
+static void
+do_pseudo_bgtz (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* bgtz rt5, label */
+ md_assemblef ("bgtz_r %s,%s", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_ble (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* bgt rt5, ra5, label */
+ if (!disable_16bit && builtin_isreg4 (argv[1], NULL))
+ {
+ md_assemblef ("slt45 %s,%s", argv[1], argv[0]);
+ md_assemblef ("beqzs8_r %s", argv[2]);
+ }
+ else
+ {
+ md_assemblef ("slt $ta,%s,%s", argv[1], argv[0]);
+ md_assemblef ("beqz_r $ta,%s", argv[2]);
+ }
+}
+
+static void
+do_pseudo_bles (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* bgt rt5, ra5, label */
+ if (!disable_16bit && builtin_isreg4 (argv[1], NULL))
+ {
+ md_assemblef ("slts45 %s,%s", argv[1], argv[0]);
+ md_assemblef ("beqzs8_r %s", argv[2]);
+ }
+ else
+ {
+ md_assemblef ("slts $ta,%s,%s", argv[1], argv[0]);
+ md_assemblef ("beqz_r $ta,%s", argv[2]);
+ }
+}
+
+static void
+do_pseudo_blez (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* bgtz rt5, label */
+ md_assemblef ("blez_r %s,%s", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_blt (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* rt5, ra5, label */
+ if (!disable_16bit && builtin_isreg4 (argv[0], NULL))
+ {
+ md_assemblef ("slt45 %s,%s", argv[0], argv[1]);
+ md_assemblef ("bnezs8_r %s", argv[2]);
+ }
+ else
+ {
+ md_assemblef ("slt $ta,%s,%s", argv[0], argv[1]);
+ md_assemblef ("bnez_r $ta,%s", argv[2]);
+ }
+}
+
+static void
+do_pseudo_blts (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* rt5, ra5, label */
+ if (!disable_16bit && builtin_isreg4 (argv[0], NULL))
+ {
+ md_assemblef ("slts45 %s,%s", argv[0], argv[1]);
+ md_assemblef ("bnezs8_r %s", argv[2]);
+ }
+ else
+ {
+ md_assemblef ("slts $ta,%s,%s", argv[0], argv[1]);
+ md_assemblef ("bnez_r $ta,%s", argv[2]);
+ }
+}
+
+static void
+do_pseudo_bltz (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* rt5, label */
+ md_assemblef ("bltz_r %s,%s", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_bltzal (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* rt5, label */
+ md_assemblef ("bltzal_r %s,%s", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_bne (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* rt5, label */
+ /* TODO check argv[0] == argv[1] for always take */
+ md_assemblef ("bne_r %s,%s,%s", argv[0], argv[1], argv[2]);
+}
+
+static void
+do_pseudo_bnes38 (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ /* rt3, label */
+ if (disable_16bit)
+ as_bad_no16 ();
+ /* TODO: check r3, check rt3 == $r3 */
+ md_assemblef ("bnes38_r %s,%s", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_bnez (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ md_assemblef ("bnez_r %s,%s", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_bnez38 (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ if (disable_16bit)
+ as_bad_no16 ();
+ md_assemblef ("bnez38_r %s,%s", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_bnezs8 (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ if (disable_16bit)
+ as_bad_no16 ();
+ md_assemblef ("bnezs8_r %s", argv[0]);
+}
+
+static void
+do_pseudo_br (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ if (disable_16bit)
+ md_assemblef ("jr %s", argv[0]);
+ else
+ md_assemblef ("jr5 %s", argv[0]);
+}
+
+static void
+do_pseudo_bral (int argc, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ if (argc != 1)
+ as_bad ("unrecognized form of instruction. %s", argv[argc]);
+
+ if (!disable_16bit && relax_jal_bound > 0
+ && (multi_call_relax || pltgot_call_relax))
+ md_assemblef ("jral_r $lp,%s", argv[0]);
+ else
+ do_pseudo_jral (argc, argv, PV_DONT_CARE);
+}
+
+static void
+do_pseudo_jral (int argc, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ /* jral rt=lp, rb */
+ /* jral5 rb5 */
+ /* If there are only argc, it must be jral5 rb5 (rt==lp). */
+ int rt = 30, rb;
+
+ if (argc == 1)
+ rb = builtin_regnum (argv[0], NULL);
+ else if (argc == 2)
+ {
+ rt = builtin_regnum (argv[0], NULL);
+ rb = builtin_regnum (argv[1], NULL);
+ }
+ else
+ {
+ as_bad ("Unexpected number of arguments of jral. %s", argv[argc]);
+ return;
+ }
+
+ if (!disable_16bit && rt == 30)
+ md_assemblef ("jral5_r $r%d", rb);
+ else
+ md_assemblef ("jral_r $r%d,$r%d", rt, rb);
+}
+
+static void
+do_pseudo_la_internal (const char *arg_reg, const char *arg_label,
const char *line)
+{
+ /* rt, label */
+ if (!pic_code)
+ {
+ 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, "@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 (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);
+}
+
+static void
+do_pseudo_la (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ do_pseudo_la_internal (argv[0], argv[1], argv[argc]);
+}
+
+static void
+do_pseudo_li_internal (char *rt, int imm32s)
+{
+ if (!disable_16bit && imm32s <= 0xf && imm32s >= -0x10)
+ md_assemblef ("movi55 %s,lo20(%d)", rt, imm32s);
+ else if (imm32s <= 0x7ffff && imm32s >= -0x80000)
+ md_assemblef ("movi %s,lo20(%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[], 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[], 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%c%si %s,[%s]", ls, size, argv[0], argv[1]);
+ }
+ else if (!pic_code)
+ {
+ /* 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]);
+ }
+ else
+ {
+ /* 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_assemblef ("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]);
+ }
+ }
+}
+
+static void
+do_pseudo_ls_bhwp (int argc ATTRIBUTE_UNUSED, char *argv[], 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_move_reg_internal (char *dst, char *src)
+{
+ if (disable_16bit)
+ md_assemblef ("ori %s,%s,0", dst, src);
+ else
+ md_assemblef ("mov55 %s,%s", dst, src);
+}
+
+static void
+do_pseudo_move (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ if (builtin_isreg (argv[1], NULL))
+ do_pseudo_move_reg_internal (argv[0], argv[1]);
+ else if (builtin_iscons (argv[1], NULL))
+ /* move $rt, imm -> li $rt, imm */
+ do_pseudo_li (argc, argv, PV_DONT_CARE);
+ else
+ /* move $rt, label -> l.w $rt, label */
+ do_pseudo_ls_bhw (argc, argv, 2); /* for load word */
+}
+
+static void
+do_pseudo_neg (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ md_assemble ("movi $ta,0");
+ md_assemblef ("sub %s,$ta,%s", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_not (int argc ATTRIBUTE_UNUSED, char *argv[], 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[], 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;
+ 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 (re >= 28)
+ re = 27;
+
+ md_assemblef ("%s $r%d,[$sp],$r%d,%d", opc, rb, re, en4);
+}
+
+static void
+do_pseudo_pushpop (int argc, char *argv[], 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);
+#if 0
+ 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]);
+
+ ra5 = builtin_regnum (argv[0], NULL);
+ if (ra5 >= 28)
+ {
+ int mask = 1 << (3 - (ra5 - 28));
+ md_assemblef ("%s $sp,[$sp],$sp,%d", opc, mask);
+ }
+ else
+ {
+ md_assemblef ("%s %s,[$sp],%s", opc, argv[0], argv[0]);
+ }
+#endif
+}
+
+static void
+do_pseudo_v3push (int argc ATTRIBUTE_UNUSED, char *argv[], int pv
ATTRIBUTE_UNUSED)
+{
+ md_assemblef ("push25 %s,%s", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_v3pop (int argc ATTRIBUTE_UNUSED, char *argv[], 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[], 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;
+ 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);
+}
+
+
+/* TODO: validate arguments */
+struct nds32_pseudo_opcode nds32_pseudo_opcode_table[] = {
+ {"b", 1, do_pseudo_b, 0, 0},
+ {"bal", 1, do_pseudo_bal, 0, 0},
+
+ {"beq", 3, do_pseudo_beq, 0, 1},
+ {"beqs38", 2, do_pseudo_beqs38, 0, 1},
+ {"beqz", 2, do_pseudo_beqz, 0, 1},
+ {"beqz38", 2, do_pseudo_beqz38, 0, 1},
+ {"beqzs8", 1, do_pseudo_beqzs8, 0, 1},
+
+ {"bge", 3, do_pseudo_bge, 0, 0},
+ {"bges", 3, do_pseudo_bges, 0, 0},
+ {"bgez", 2, do_pseudo_bgez, 0, 1},
+ {"bgezal", 2, do_pseudo_bgezal, 0, 1},
+
+ {"bgt", 3, do_pseudo_bgt, 0, 0},
+ {"bgts", 3, do_pseudo_bgts, 0, 0},
+ {"bgtz", 2, do_pseudo_bgtz, 0, 1},
+
+ {"ble", 3, do_pseudo_ble, 0, 0},
+ {"bles", 3, do_pseudo_bles, 0, 0},
+ {"blez", 2, do_pseudo_blez, 0, 1},
+
+ {"blt", 3, do_pseudo_blt, 0, 0},
+ {"blts", 3, do_pseudo_blts, 0, 0},
+ {"bltz", 2, do_pseudo_bltz, 0, 1},
+ {"bltzal", 2, do_pseudo_bltzal, 0, 1},
+
+ {"bne", 3, do_pseudo_bne, 0, 1},
+ {"bnes38", 2, do_pseudo_bnes38, 0, 1},
+ {"bnez", 2, do_pseudo_bnez, 0, 1},
+ {"bnez38", 2, do_pseudo_bnez38, 0, 1},
+ {"bnezs8", 1, do_pseudo_bnezs8, 0, 1},
+
+ {"br", 1, do_pseudo_br, 0, 0},
+ {"bral", 1, do_pseudo_bral, 0, 0},
+
+ {"call", 1, do_pseudo_bal, 0, 0},
+ {"jral5", 1, do_pseudo_jral, 0, 1},
+
+ {"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.hp", 3, do_pseudo_ls_bhwp, 1, 0},
+ {"l.wp", 3, do_pseudo_ls_bhwp, 2, 0},
+ {"l.bsp", 3, do_pseudo_ls_bhwp, 0 | 0x10, 0},
+ {"l.hsp", 3, do_pseudo_ls_bhwp, 1 | 0x10, 0},
+ {"s.bp", 3, do_pseudo_ls_bhwp, 0 | 0x80000000, 0},
+ {"s.hp", 3, do_pseudo_ls_bhwp, 1 | 0x80000000, 0},
+ {"s.wp", 3, do_pseudo_ls_bhwp, 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},
+
+#if 0
+ {"lb.p", },
+ {"lh.p", },
+ {"lw.p", },
+ {"lbi.p", },
+ {"lhi.p", },
+ {"lwi.p", },
+ {"lbsi.p", },
+ {"lhsi.p", },
+ {"lbs.p", },
+ {"lhs.p", },
+#endif
+
+
+ {"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},
+
+ /* jasonwu:
+ 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 },
+
+ {NULL, 0, NULL, 0, 0}
+};
+
+static void
+nds32_init_nds32_pseudo_opcodes (void)
+{
+ struct nds32_pseudo_opcode *opcode = nds32_pseudo_opcode_table;
+ nds32_pseudo_opcode_hash = hash_new ();
+ for ( ; opcode->opcode; opcode++)
+ {
+ void *op;
+ op = hash_find (nds32_pseudo_opcode_hash, opcode->opcode);
+ if (op != NULL)
+ {
+ as_warn (_("Duplicated pseudo-opcode %s."), opcode->opcode);
+ continue;
+ }
+ hash_insert (nds32_pseudo_opcode_hash, opcode->opcode, opcode);
+ }
+}
+
+static struct nds32_pseudo_opcode *
+nds32_lookup_pseudo_opcode (char *str)
+{
+ int i = 0;
+ /* Assume pseudo-opcode are less than 16-char in length. */
+ char op[16] = {0};
+
+ for (i = 0; i < (int)ARRAY_SIZE (op); i++)
+ {
+ if (ISSPACE (op[i] = str[i]))
+ break;
+ }
+
+ if (i >= (int)ARRAY_SIZE (op))
+ return NULL;
+
+ op[i] = '\0';
+
+ return hash_find (nds32_pseudo_opcode_hash, op);
+}
+
+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 parses CPU specified. */
+
+static int
+nds32_parse_cpu (char *str)
+{
+ if (nds32_strcasestr (str, "n12") || nds32_strcasestr (str, "n10")
+ || nds32_strcasestr (str, "n9") || nds32_strcasestr (str, "n8")
+ || nds32_strcasestr (str, "n13"))
+ {
+ return 1;
+ }
+
+ /* Logic here rejects the input CPU model. */
+ as_bad (_("unknown cpu `%s'"), str);
+ 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, char *arg)
+{
+ switch (c)
+ {
+ case 'O':
+ case OPTION_OPTIMIZE:
+ optimize = 1;
+ optimize_for_space = 0;
+ optimize_for_space_no_align = 0;
+ break;
+ case OPTION_BIG:
+ case OPTION_LITTLE:
+ target_big_endian = (c == OPTION_BIG);
+ break;
+ case OPTION_ABI:
+ {
+ if (!strcmp (arg, "1"))
+ abi_ver = E_NDS_ABI_V1;
+ else if (!strcmp (arg, "2"))
+ abi_ver = E_NDS_ABI_AABI;
+ else if (!strcmp (arg, "2fp"))
+ abi_ver = E_NDS_ABI_V2FP;
+ else
+ as_bad (_("ABI option must be -mabi={1|2|2fp}"));
+ break;
+ }
+ case OPTION_16BIT:
+ case OPTION_NO_16BIT:
+ disable_16bit = (c == OPTION_16BIT);
+ saved_disable_16bit = (c == OPTION_16BIT);
+ case OPTION_EXT_ALL:
+ enable_all_ext = 1;
+ break;
+ case OPTION_EXT_AUDIO:
+ case OPTION_NO_EXT_AUDIO:
+ enable_audio_extension = (c == OPTION_EXT_AUDIO);
+ break;
+ case OPTION_EXT_STRING:
+ case OPTION_NO_EXT_STRING:
+ enable_string_extension = (c == OPTION_EXT_STRING);
+ break;
+ case OPTION_EXT_MAC:
+ case OPTION_NO_EXT_MAC:
+ enable_mac_extension = (c == OPTION_EXT_MAC);
+ break;
+ case OPTION_DIV:
+ case OPTION_NO_DIV:
+ enable_div_extension = (c == OPTION_DIV);
+ break;
+ case OPTION_EXT_PERF:
+ case OPTION_NO_EXT_PERF:
+ enable_c_extension = (c == OPTION_EXT_PERF);
+ break;
+ case OPTION_EXT_PERF2:
+ case OPTION_NO_EXT_PERF2:
+ enable_c_extension2 = (c == OPTION_EXT_PERF2);
+ break;
+ case OPTION_EXT_DSP:
+ case OPTION_NO_EXT_DSP:
+ enable_dsp_extension = (c == OPTION_EXT_DSP);
+ break;
+ case OPTION_FPU_SP:
+ case OPTION_NO_FPU_SP:
+ enable_fpu_sp_extension = (c == OPTION_FPU_SP);
+ break;
+ case OPTION_FPU_DP:
+ case OPTION_NO_FPU_DP:
+ enable_fpu_dp_extension = (c == OPTION_FPU_DP);
+ break;
+ case OPTION_FPU_FMA:
+ case OPTION_NO_FPU_FMA:
+ enable_fpu_mac_extension = (c == OPTION_FPU_FMA);
+ break;
+ case OPTION_FPU_FREG:
+ switch (atoi (arg))
+ {
+ case 0: case 4:
+ fs_reg_num = 8;
+ fd_reg_num = 4;
+ fpu_config = E_NDS32_FPU_REG_8SP_4DP << E_NDS32_FPU_REG_CONF_SHIFT;
+ break;
+ case 1: case 5:
+ fs_reg_num = 16;
+ fd_reg_num = 8;
+ fpu_config = E_NDS32_FPU_REG_16SP_8DP << E_NDS32_FPU_REG_CONF_SHIFT;
+ break;
+ case 2: case 6:
+ fs_reg_num = 32;
+ fd_reg_num = 16;
+ fpu_config = E_NDS32_FPU_REG_32SP_16DP << E_NDS32_FPU_REG_CONF_SHIFT;
+ break;
+ case 3: case 7:
+ fs_reg_num = 32;
+ fd_reg_num = 32;
+ fpu_config = E_NDS32_FPU_REG_32SP_32DP << E_NDS32_FPU_REG_CONF_SHIFT;
+ break;
+ default:
+ as_bad (_("FPU config option must be -mconfig-fpu={0-7}"));
+ break;
+ }
+ break;
+ case OPTION_REDUCED_REGS:
+ case OPTION_NO_REDUCED_REGS:
+ enable_reduce_regs = (c == OPTION_REDUCED_REGS);
+ break;
+ case OPTION_PIC_CODE:
+ case OPTION_NO_PIC_CODE:
+ pic_code = (c == OPTION_PIC_CODE);
+ break;
+ case OPTION_OPTIMIZE_SPACE:
+ optimize = 0;
+ optimize_for_space = 1;
+ /* Rufus: Avoid inserting nop16 for label align. */
+ optimize_for_space_no_align = 0;
+ break;
+ case OPTION_WARN_UNMATCHED:
+ case OPTION_NO_WARN_UNMATCHED:
+ warn_unmatched_high = (c == OPTION_WARN_UNMATCHED);
+ break;
+ case OPTION_CPU:
+ nds32_parse_cpu (arg);
+ break;
+ case OPTION_MARCH:
+ if (!strcasecmp (arg, "v2"))
+ {
+ enable_mac_extension = 1;
+ enable_div_extension = 1;
+ march_cpu_opt = ARCH_V2;
+ }
+ else if (!strcasecmp (arg, "v3"))
+ {
+ enable_mac_extension = 1;
+ enable_div_extension = 1;
+ march_cpu_opt = ARCH_V3;
+ }
+ else if (!strcasecmp (arg, "v3m"))
+ {
+ march_cpu_opt = ARCH_V3M;
+ }
+ else if (!strcasecmp (arg, "v1"))
+ {
+ march_cpu_opt = ARCH_V1;
+ }
+ else
+ {
+ as_bad (_("Baseline option must be -march={v1|v2|v3|v3m}"));
+ }
+ break;
+ case OPTION_DX_REGS:
+ case OPTION_NO_DX_REGS:
+ enable_dx_regs_extension = (c == OPTION_DX_REGS);
+ break;
+ case OPTION_NO_RELAX:
+ enable_relax_relocs = 0;
+ break;
+ case OPTION_RELAX_WARN:
+ enable_relax_warning = 1;
+ break;
+ case OPTION_NO_MULCALL_RELAX:
+ multi_call_relax = 0;
+ break;
+ case OPTION_NO_PLTGOT_CALL_RELAX:
+ pltgot_call_relax = 0;
+ break;
+ case OPTION_RELAX_JAL:
+ relax_jal_bound = atoi (arg);
+ break;
+ case OPTION_NO_HINT_FUNC_ARGS:
+ use_hint_func_args = 0;
+ break;
+ case OPTION_NO_OMIT_FP:
+ omit_frame_pointer = 0;
+ break;
+ case OPTION_CONSERVATIVE_IFC:
+ case OPTION_NO_CONSERVATIVE_IFC:
+ is_conservative_ifc = (c == OPTION_CONSERVATIVE_IFC);
+ break;
+ case OPTION_ALARM_BLOCK:
+ if (!strcmp (arg, "none"))
+ alarm_block = ALARM_BLOCK_NONE;
+ else if (!strcmp (arg, "bad"))
+ alarm_block = ALARM_BLOCK_BAD;
+ else if (!strcmp (arg, "fatal"))
+ alarm_block = ALARM_BLOCK_FATAL;
+ else
+ as_bad (_("Alarm block must be -malarm-block={none|bad|fatal}"));
+ break;
+ case OPTION_B2BB:
+ case OPTION_NO_B2BB:
+ opt_b2bb = (c == OPTION_B2BB);
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+typedef struct insn_label_list
+{
+ symbolS *label;
+ struct insn_label_list *next;
+} insn_label_list;
+
+#define INDIRECT_BRANCH_MASK 0x00000001
+#define DIRECT_BRANCH_MASK 0x00000002
+#define UNCONDITIONAL_BRANCH_MASK 0x00000001
+#define CONDITIONAL_BRANCH_MASK 0x00000002
+
+#define IS_FUNC_RETURN_MASK 0x1
+#define IS_FUNC_CALL_MASK 0x2
+#define IS_COND_FUNC_CALL_MASK 0x3
+#define IS_COND_FUNC_RETURN_MASK 0x4
+
+static int ifc_mode = 0;
+
+static int
+is_func_branch (int insn_num)
+{
+ switch (insn_num)
+ {
+ case NDS32_INSN_IFRET:
+ return IS_COND_FUNC_RETURN_MASK;
+ case NDS32_INSN_RET:
+ case NDS32_INSN_RET5:
+ case NDS32_INSN_POP25:
+ return IS_FUNC_RETURN_MASK;
+ case NDS32_INSN_JAL:
+ case NDS32_INSN_JRAL:
+ case NDS32_INSN_JRAL5:
+ return IS_FUNC_CALL_MASK;
+ case NDS32_INSN_IFCALL:
+ case NDS32_INSN_IFCALL9:
+ return IS_COND_FUNC_CALL_MASK;
+ default:
+ return 0;
+ }
+}
+
+static int
+is_direct_branch_instruction (int insn_num)
+{
+ switch (insn_num)
+ {
+ case NDS32_INSN_J:
+ case NDS32_INSN_JAL:
+ case NDS32_INSN_IFCALL:
+ case NDS32_INSN_IFCALL9:
+ case NDS32_INSN_J8:
+ case NDS32_INSN_BEQZ:
+ case NDS32_INSN_BNEZ:
+ case NDS32_INSN_BGEZ:
+ case NDS32_INSN_BLTZ:
+ case NDS32_INSN_BGTZ:
+ case NDS32_INSN_BLEZ:
+ case NDS32_INSN_BGEZAL:
+ case NDS32_INSN_BLTZAL:
+ case NDS32_INSN_BEQ:
+ case NDS32_INSN_BNE:
+ case NDS32_INSN_BEQZ38:
+ case NDS32_INSN_BNEZ38:
+ case NDS32_INSN_BEQS38:
+ case NDS32_INSN_BNES38:
+ case NDS32_INSN_BEQZS8:
+ case NDS32_INSN_BNEZS8:
+ case NDS32_INSN_BEQC:
+ case NDS32_INSN_BNEC:
+ return DIRECT_BRANCH_MASK;
+ case NDS32_INSN_JR:
+ case NDS32_INSN_JR5:
+ case NDS32_INSN_IFRET:
+ case NDS32_INSN_RET:
+ case NDS32_INSN_RET5:
+ case NDS32_INSN_POP25:
+ case NDS32_INSN_JRAL:
+ case NDS32_INSN_JRAL5:
+ return INDIRECT_BRANCH_MASK;
+ default:
+ ;
+ }
+ return 0;
+}
+
+static int
+is_conditional_branch_instruction (int insn_num)
+{
+ switch (insn_num)
+ {
+ case NDS32_INSN_BEQZ:
+ case NDS32_INSN_BNEZ:
+ case NDS32_INSN_BGEZ:
+ case NDS32_INSN_BLTZ:
+ case NDS32_INSN_BGTZ:
+ case NDS32_INSN_BLEZ:
+ case NDS32_INSN_BGEZAL:
+ case NDS32_INSN_BLTZAL:
+ case NDS32_INSN_BEQ:
+ case NDS32_INSN_BNE:
+ case NDS32_INSN_BEQZ38:
+ case NDS32_INSN_BNEZ38:
+ case NDS32_INSN_BEQS38:
+ case NDS32_INSN_BNES38:
+ case NDS32_INSN_BEQZS8:
+ case NDS32_INSN_BNEZS8:
+ case NDS32_INSN_IFRET:
+ case NDS32_INSN_BEQC:
+ case NDS32_INSN_BNEC:
+ return CONDITIONAL_BRANCH_MASK;
+ case NDS32_INSN_J:
+ case NDS32_INSN_JAL:
+ case NDS32_INSN_IFCALL:
+ case NDS32_INSN_IFCALL9:
+ case NDS32_INSN_J8:
+ case NDS32_INSN_JR:
+ case NDS32_INSN_JR5:
+ case NDS32_INSN_RET:
+ case NDS32_INSN_RET5:
+ case NDS32_INSN_POP25:
+ case NDS32_INSN_JRAL:
+ case NDS32_INSN_JRAL5:
+ return UNCONDITIONAL_BRANCH_MASK;
+ default:
+ ;
+ }
+ return 0;
+}
+
+static int
+is_lo12_mem_reg_instruction (int insn_num)
+{
+ switch (insn_num)
+ {
+ case NDS32_INSN_LB:
+ case NDS32_INSN_LBS:
+ case NDS32_INSN_LH:
+ case NDS32_INSN_LHS:
+ case NDS32_INSN_LW:
+ case NDS32_INSN_SB:
+ case NDS32_INSN_SH:
+ case NDS32_INSN_SW:
+ return 1;
+ default:
+ ;
+ }
+ return 0;
+}
+
+static int
+is_lo12_mem_imm_instruction (int insn_num)
+{
+ switch (insn_num)
+ {
+ case NDS32_INSN_LBI:
+ case NDS32_INSN_LHI:
+ case NDS32_INSN_LWI:
+ case NDS32_INSN_LBSI:
+ case NDS32_INSN_LHSI:
+ case NDS32_INSN_SBI:
+ case NDS32_INSN_SHI:
+ case NDS32_INSN_SWI:
+ case NDS32_INSN_FLSI:
+ case NDS32_INSN_FLDI:
+ case NDS32_INSN_FSSI:
+ case NDS32_INSN_FSDI:
+ return 1;
+ default:
+ ;
+ }
+ return 0;
+}
+
+static int
+is_hint_func_args (int hint_p, int regP)
+{
+ return hint_p & (1 << regP);
+}
+
+/* Branch instruction attribute (end). */
+typedef struct dep_node dep_node;
+typedef struct nds32_insn_instant nds32_insn_instant;
+typedef struct nds32_insn_list nds32_insn_list;
+typedef struct basic_block basic_block;
+typedef struct nds32_bb_list nds32_bb_list;
+typedef struct reloc_tree_node reloc_tree_node;
+typedef struct bfdsym_bb_map bfdsym_bb_map;
+typedef struct lp_bb_node lp_bb_node;
+
+static nds32_insn_instant *prev_insn_s;
+static reloc_tree_node *nds32_reloc_node_pool;
+static lp_bb_node *lp_bb_node_list = NULL;
+
+static void init_insn_list (void);
+static nds32_insn_list *get_free_insn_list (void);
+static void push_insn_to_list (nds32_insn_list **, nds32_insn_instant *);
+static void push_to_insn_list_all (nds32_insn_list **, nds32_insn_list **);
+static void free_nds32_insn_list (nds32_insn_list **);
+
+static void append_basic_block (basic_block **);
+static void create_new_basic_block (void);
+static void init_basic_block (void);
+static nds32_bb_list *get_free_bb_list (void);
+static void push_to_bb_list_all (nds32_bb_list **, nds32_bb_list **);
+static void push_bb_to_bb_list (nds32_bb_list **, basic_block *);
+
+static void free_nds32_reloc_node_list (reloc_tree_node *, reloc_tree_node *);
+static reloc_tree_node *fetch_reloc_node_from_pool (void);
+static void return_reloc_node_to_pool (reloc_tree_node *);
+static void free_reloc_node_sibling (reloc_tree_node **);
+static void free_reloc_node_next (reloc_tree_node *);
+static reloc_tree_node *new_reloc_node_sibling (reloc_tree_node *,
+ nds32_insn_instant *);
+static void bind_reloc_list_insn (reloc_tree_node *);
+static void bind_reloc_node_child_sibling (reloc_tree_node *,
+ reloc_tree_node *);
+static void taint_reloc_node_tree (reloc_tree_node *);
+static void insert_relax_type (nds32_insn_instant *insn_p, int reloc_type,
+ int data_size, int arg, expressionS *exp_p);
+
+static void
+init_cfg_var (void)
+{
+ init_basic_block ();
+ prev_insn_s = NULL;
+ init_insn_list ();
+ nds32_reloc_node_pool = NULL;
+}
+
+/* dep_node
+ Record the use of the general purpose register. */
+
+struct dep_node
+{
+ short reg_num;
+ short flag;
+ struct dep_node *next;
+#define CGEN_OPINST_INPUT_MASK 0x00000001
+#define CGEN_OPINST_OUTPUT_MASK 0x00000002
+};
+
+
+/* Record the use of the instructions. */
+
+struct nds32_insn_instant
+{
+ /* Opcode of this instruction. */
+ enum cgen_insn_type insn_num;
+ char *addr;
+ fragS *frag;
+ int num_fixups;
+ fixS *fixups[GAS_CGEN_MAX_FIXUPS];
+ /* Operands of this instructions. */
+ dep_node *gr_dep_list;
+ /* Where his instruction belongs to. */
+ basic_block *parent;
+ struct nds32_insn_instant *next;
+ struct nds32_insn_instant *prev;
+ short byte_sz;
+ short insn_sz;
+ int hint_func_args;
+ int md_relax;
+ reloc_tree_node *reloc_node;
+};
+
+/* A list used to chain several instructions
+ with the same attribute up. */
+
+struct nds32_insn_list
+{
+ nds32_insn_instant *insn;
+ struct nds32_insn_list *next;
+};
+
+static nds32_insn_list *nds32_hi_insn_list;
+static nds32_insn_list *nds32_got20_insn_list;
+static nds32_insn_list *nds32_slt_insn_list;
+static nds32_insn_list *nds32_free_insn_list;
+static nds32_insn_list *nds32_ta_insn_list;
+
+static void
+init_insn_list (void)
+{
+ nds32_hi_insn_list = NULL;
+ nds32_got20_insn_list = NULL;
+ nds32_slt_insn_list = NULL;
+ nds32_ta_insn_list = NULL;
+ nds32_free_insn_list = NULL;
+}
+
+static nds32_insn_list *
+get_free_insn_list (void)
+{
+ nds32_insn_list *tmp_insn_list;
+
+ if (nds32_free_insn_list == NULL)
+ return xmalloc (sizeof (nds32_insn_list));
+
+ tmp_insn_list = nds32_free_insn_list;
+ nds32_free_insn_list = nds32_free_insn_list->next;
+ return tmp_insn_list;
+}
+
+static void
+push_insn_to_list (nds32_insn_list ** insn_list_p,
+ nds32_insn_instant *insn_p)
+{
+ nds32_insn_list *tmp_list = get_free_insn_list ();
+ tmp_list->insn = insn_p;
+ tmp_list->next = *insn_list_p;
+ *insn_list_p = tmp_list;
+}
+
+static void
+push_to_insn_list_all (nds32_insn_list ** insn_list_p,
+ nds32_insn_list ** insn_list_q)
+{
+ nds32_insn_list *insn_list_t = *insn_list_p;
+
+ if (insn_list_t == NULL)
+ return;
+
+ while (insn_list_t->next != NULL)
+ insn_list_t = insn_list_t->next;
+
+ insn_list_t->next = *insn_list_q;
+ *insn_list_q = *insn_list_p;
+ *insn_list_p = NULL;
+}
+
+static void
+free_nds32_insn_list (nds32_insn_list ** insn_list_p)
+{
+ nds32_insn_list *insn_list_s, *insn_list_s2;
+
+ insn_list_s = *insn_list_p;
+ while (insn_list_s != NULL)
+ {
+ insn_list_s2 = insn_list_s->next;
+ xfree (insn_list_s);
+ insn_list_s = insn_list_s2;
+ }
+ *insn_list_p = NULL;
+}
+
+
+/* Record the use of a basic block. */
+
+struct basic_block
+{
+ /* Chaining all the basic blocks. */
+ struct basic_block *list_next;
+ struct basic_block *list_prev;
+
+ /* The first instruction in this basic block. */
+ nds32_insn_instant *insn_head;
+ /* The last instruction in this basic block. */
+ nds32_insn_instant *insn_tail;
+
+ /* Next direct bb. For these are sperated by labels
+ or target of non-taken branches. */
+ struct basic_block *cfg_next;
+ /* Target of taken branches. */
+ struct basic_block *cfg_target;
+ /* cfg_next and cfg_target are illustrated as below.
+ cfg_next cfg_target list_next
+ [ bb1 ] ... bb2 -
+ .L0:[ bb2 ] ... bb3 -
+ [ bb3 ] beqz $r0, .L1 bb4 bb5
+ [ bb4 ] jal foo - foo bb5
+ .L1:[ bb5 ] j .L0 - bb2
+
+ */
+
+ struct lp_bb_node *lp_node;
+ insn_label_list *label_list;
+ frchainS *frchain_now;
+ segT now_seg;
+ nds32_insn_instant *set_before_use_gr[32];
+ nds32_insn_instant *use_before_set_gr[32];
+ int known_pred;
+ int walk_through;
+ int in_critical;
+ int in_common;
+ char *filename;
+ unsigned int line_num;
+ unsigned int index;
+};
+
+/*
+ bb_list - global basic block list
+ bb_now - current basic block
+ bb_indirect - just a dummy mark for unknow basic block (indirect jump)
+
+ <- - - list_prev
+ bb_list-> b0 -> b1 -> b2 -> bb_now
+ list_next - - ->
+ */
+static basic_block *bb_indirect;
+static basic_block *bb_list;
+static basic_block *bb_now;
+
+/* A list used to chain several basic blocks with the same attribute up. */
+struct nds32_bb_list
+{
+ basic_block *bb;
+ struct
+ {
+ int walk_through;
+ int in_critical;
+ } trace_data;
+ nds32_bb_list *next;
+};
+
+static nds32_bb_list *nds32_free_bb_list;
+/* nds32_use_bb_list includes all the path we've traversed. */
+static nds32_bb_list *nds32_use_bb_list;
+/* nds32_path_bb_list is the path to reach current basic-block. */
+static nds32_bb_list *nds32_path_bb_list;
+
+static void
+append_basic_block (basic_block ** bb_p)
+{
+ basic_block *bb_new = xmalloc (sizeof (basic_block));
+ bb_new->list_prev = *bb_p;
+ bb_new->cfg_target = NULL;
+ bb_new->insn_head = NULL;
+ bb_new->insn_tail = NULL;
+ bb_new->label_list = NULL;
+ bb_new->lp_node = NULL;
+
+ memset (&bb_new->set_before_use_gr, 0, 32 * sizeof (nds32_insn_instant *));
+ memset (&bb_new->use_before_set_gr, 0, 32 * sizeof (nds32_insn_instant *));
+
+ if ((*bb_p) != NULL)
+ {
+ if ((*bb_p)->list_next != NULL)
+ {
+ (*bb_p)->list_next->list_prev = bb_new;
+ }
+ bb_new->list_next = (*bb_p)->list_next;
+ bb_new->cfg_next = (*bb_p)->cfg_next;
+ (*bb_p)->list_next = bb_new;
+ (*bb_p)->cfg_next = bb_new;
+ }
+ else
+ {
+ bb_new->list_next = NULL;
+ bb_new->cfg_next = NULL;
+ *bb_p = bb_new;
+ }
+ bb_new->frchain_now = frchain_now;
+ bb_new->known_pred = 0;
+ bb_new->walk_through = 0;
+ bb_new->in_critical = 0;
+ bb_new->in_common = 0;
+ bb_new->now_seg = now_seg;
+ as_where (&bb_new->filename, &bb_new->line_num);
+}
+
+static void
+create_new_basic_block (void)
+{
+ append_basic_block (&bb_now);
+ bb_now = bb_now->list_next;
+}
+
+static void
+init_basic_block (void)
+{
+ bb_now = NULL;
+ append_basic_block (&bb_now);
+ bb_list = bb_now;
+ bb_indirect = NULL;
+ append_basic_block (&bb_indirect);
+}
+
+static nds32_bb_list *
+get_free_bb_list (void)
+{
+ nds32_bb_list *tmp_bb_list;
+
+ if (nds32_free_bb_list == NULL)
+ return xmalloc (sizeof (nds32_bb_list));
+
+ tmp_bb_list = nds32_free_bb_list;
+ nds32_free_bb_list = nds32_free_bb_list->next;
+ return tmp_bb_list;
+}
+
+static void
+push_to_bb_list_all (nds32_bb_list ** bb_list_p, nds32_bb_list ** bb_list_q)
+{
+ nds32_bb_list *bb_list_t = *bb_list_p;
+
+ if (bb_list_t == NULL)
+ return;
+
+ while (bb_list_t->next != NULL)
+ bb_list_t = bb_list_t->next;
+
+ bb_list_t->next = *bb_list_q;
+ *bb_list_q = bb_list_t;
+ *bb_list_p = NULL;
+}
+
+static void
+push_bb_to_bb_list (nds32_bb_list ** bb_list_p, basic_block *bb_p)
+{
+ nds32_bb_list *bb_list_t = get_free_bb_list ();
+ bb_list_t->bb = bb_p;
+ bb_list_t->next = *bb_list_p;
+ *bb_list_p = bb_list_t;
+}
+
+static void
+pop_bb_from_bb_list (nds32_bb_list ** bb_list_p)
+{
+ nds32_bb_list *bb_list_t;
+
+ if (!(*bb_list_p))
+ return;
+
+ bb_list_t = *bb_list_p;
+ *bb_list_p = (*bb_list_p)->next;
+ bb_list_t->next = nds32_free_bb_list;
+ nds32_free_bb_list = bb_list_t;
+}
+
+static void
+bb_list_in_critical (nds32_bb_list *bb_list_p)
+{
+ nds32_bb_list *bb_list_t;
+
+ bb_list_t = bb_list_p;
+ while (bb_list_t)
+ {
+ bb_list_t->bb->in_critical = 1;
+ bb_list_t = bb_list_t->next;
+ }
+}
+
+static int
+check_unknown_source_in_critical (nds32_bb_list *bb_list_p)
+{
+ nds32_bb_list *bb_list_t;
+
+ bb_list_t = bb_list_p;
+ while (bb_list_t)
+ {
+ if (bb_list_t->bb->in_critical)
+ {
+ if ((bb_list_t->bb->known_pred - bb_list_t->bb->in_common) >
+ bb_list_t->bb->walk_through)
+ return FALSE;
+ }
+ bb_list_t = bb_list_t->next;
+ }
+ return TRUE;
+}
+
+static void
+clean_temp_var_bb_list (nds32_bb_list *bb_list_p)
+{
+ nds32_bb_list *bb_list_t;
+
+ bb_list_t = bb_list_p;
+ while (bb_list_t)
+ {
+ bb_list_t->bb->walk_through = 0;
+ bb_list_t->bb->in_critical = 0;
+ bb_list_t = bb_list_t->next;
+ }
+}
+
+static void
+save_temp_var_bb_list (nds32_bb_list *bb_list_p)
+{
+ nds32_bb_list *bb_list_t;
+
+ bb_list_t = bb_list_p;
+ while (bb_list_t)
+ {
+ bb_list_t->trace_data.walk_through = bb_list_t->bb->walk_through;
+ bb_list_t->trace_data.in_critical = bb_list_t->bb->in_critical;
+ bb_list_t = bb_list_t->next;
+ }
+}
+
+static void
+restore_temp_var_bb_list (nds32_bb_list *bb_list_p)
+{
+ nds32_bb_list *bb_list_t;
+
+ bb_list_t = bb_list_p;
+ while (bb_list_t)
+ {
+ bb_list_t->bb->walk_through = bb_list_t->trace_data.walk_through;
+ bb_list_t->bb->in_critical = bb_list_t->trace_data.in_critical;
+ bb_list_t = bb_list_t->next;
+ }
+}
+
+
+/* A structure used to record some fixups may be appended
+ to the instruction. */
+struct reloc_tree_node
+{
+ int reloc_type;
+ int type_mask;
+ int leaf_num;
+ nds32_insn_instant *insn;
+ reloc_tree_node *parent;
+ reloc_tree_node *child;
+ reloc_tree_node *sibling;
+ reloc_tree_node *next;
+ short tainted;
+ short convertible;
+};
+
+#define MAX_PATTERN_LEN 4
+static reloc_tree_node *reloc_list_now;
+static reloc_tree_node *current_pattern[MAX_PATTERN_LEN];
+
+static void
+free_nds32_reloc_node_list (reloc_tree_node *reloc_list_p,
+ reloc_tree_node *delimiter)
+{
+ reloc_tree_node *reloc_list_t;
+
+ while (reloc_list_p != delimiter)
+ {
+ reloc_list_t = reloc_list_p;
+ reloc_list_p = reloc_list_p->next;
+ free (reloc_list_t);
+ }
+}
+
+static reloc_tree_node *
+fetch_reloc_node_from_pool (void)
+{
+ reloc_tree_node *reloc_node_t;
+
+ if (!nds32_reloc_node_pool)
+ return (reloc_tree_node *) calloc (1, sizeof (reloc_tree_node));
+
+ reloc_node_t = nds32_reloc_node_pool;
+ nds32_reloc_node_pool = nds32_reloc_node_pool->next;
+ return reloc_node_t;
+}
+
+static void
+return_reloc_node_to_pool (reloc_tree_node *reloc_node_p)
+{
+ reloc_tree_node *reloc_node_t;
+ if (!reloc_node_p)
+ return;
+
+ reloc_node_t = reloc_node_p->next;
+ reloc_node_p->next = nds32_reloc_node_pool;
+ nds32_reloc_node_pool = reloc_node_t;
+}
+
+static void
+free_reloc_node_sibling (reloc_tree_node ** reloc_node_p)
+{
+ reloc_tree_node *reloc_node_t = *reloc_node_p;
+
+ while (reloc_node_t)
+ {
+ return_reloc_node_to_pool (reloc_node_t);
+ reloc_node_t = reloc_node_t->sibling;
+ }
+ *reloc_node_p = NULL;
+}
+
+static void
+free_reloc_node_next (reloc_tree_node *reloc_node_p)
+{
+ reloc_tree_node *reloc_node_t = reloc_node_p;
+
+ while (reloc_node_t)
+ {
+ return_reloc_node_to_pool (reloc_node_t);
+ reloc_node_t = reloc_node_t->next;
+ }
+}
+
+static reloc_tree_node *
+new_reloc_node_sibling (reloc_tree_node *list_head,
+ nds32_insn_instant *insn_p)
+{
+ reloc_tree_node *reloc_node_t = fetch_reloc_node_from_pool ();
+
+ memset (reloc_node_t, 0, sizeof (reloc_tree_node));
+ reloc_node_t->sibling = list_head;
+ reloc_node_t->insn = insn_p;
+ return reloc_node_t;
+}
+
+static void
+bind_reloc_list_insn (reloc_tree_node *reloc_node_p)
+{
+ reloc_tree_node *reloc_node_t = reloc_node_p;
+
+ reloc_node_t->next = reloc_node_t->insn->reloc_node;
+ reloc_node_t->insn->reloc_node = reloc_node_t;
+}
+
+static void
+bind_reloc_node_child_sibling (reloc_tree_node *reloc_parent,
+ reloc_tree_node *reloc_child)
+{
+ reloc_parent->child = reloc_child;
+
+ while (reloc_child != NULL)
+ {
+ reloc_child->parent = reloc_parent;
+ reloc_child = reloc_child->sibling;
+ }
+}
+
+static void
+taint_reloc_node_tree (reloc_tree_node *reloc_node_p)
+{
+ reloc_tree_node *reloc_node_t;
+
+ if (reloc_node_p->tainted)
+ return;
+
+ reloc_node_t = reloc_node_p->parent;
+
+ /* If reloc_node_p is root, taint the root node.
+ Otherwise, taint all siblings of reloc_node_p. */
+ if (reloc_node_t)
+ reloc_node_t = reloc_node_t->child;
+ else
+ reloc_node_t = reloc_node_p;
+
+ while (reloc_node_t)
+ {
+ reloc_node_t->tainted = 1;
+ reloc_node_t = reloc_node_t->sibling;
+ }
+}
+
+struct bfdsym_bb_map
+{
+ asymbol *bfdsym;
+ basic_block *bb;
+ bfdsym_bb_map *next;
+};
+
+static bfdsym_bb_map *
+new_bfdsym_bb_map (basic_block *bb_p, asymbol *bfdsym_p,
+ bfdsym_bb_map *next_p)
+{
+ bfdsym_bb_map *bfdsym_bb_map_t;
+
+ bfdsym_bb_map_t = xmalloc (sizeof (bfdsym_bb_map));
+ bfdsym_bb_map_t->bb = bb_p;
+ bfdsym_bb_map_t->bfdsym = bfdsym_p;
+ bfdsym_bb_map_t->next = next_p;
+
+ return bfdsym_bb_map_t;
+
+}
+
+static bfdsym_bb_map *
+find_bfdsym_bb_map (bfdsym_bb_map *map_list_p, asymbol *bfdsym_p)
+{
+ while (map_list_p != NULL)
+ {
+ if (map_list_p->bfdsym == bfdsym_p)
+ return map_list_p;
+ map_list_p = map_list_p->next;
+ }
+
+ return NULL;
+}
+
+static void
+bfdsym_bb_map_delete (const char *key ATTRIBUTE_UNUSED, PTR bfdsym_bb_map_p)
+{
+ bfdsym_bb_map *bfdsym_bb_map_t = bfdsym_bb_map_p, *bfdsym_bb_map_t2 =
+ bfdsym_bb_map_p;
+
+ while (bfdsym_bb_map_t2 != NULL)
+ {
+ bfdsym_bb_map_t = bfdsym_bb_map_t2->next;
+ xfree (bfdsym_bb_map_t2);
+ bfdsym_bb_map_t2 = bfdsym_bb_map_t;
+ }
+}
+
+
+/* Record basic blocks of landing pads recorded in .gcc_except_table. */
+struct lp_bb_node
+{
+ basic_block *bb;
+ lp_bb_node *next;
+};
+
+static void
+clean_lp_bb_list (lp_bb_node *lp_bb_list)
+{
+ lp_bb_node *node_t, *node_t2;
+
+ node_t = lp_bb_list;
+ while (node_t)
+ {
+ node_t2 = node_t;
+ node_t = node_t->next;
+ free (node_t2);
+ }
+}
+
+/* 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
+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
+restore_16bit (int ignore ATTRIBUTE_UNUSED)
+{
+ disable_16bit = saved_disable_16bit;
+}
+
+static void
+off_16bit (int ignore ATTRIBUTE_UNUSED)
+{
+ /* Save default 16-bit conversion setting and turn the conversion off. */
+ saved_disable_16bit = disable_16bit;
+ disable_16bit = 1;
+}
+
+enum
+{
+ NDS32_SEC_NONE,
+ NDS32_SEC_SDATA_SBSS,
+ NDS32_SEC_EFLAG_ISR,
+ NDS32_SEC_MAX
+};
+
+static segT sec_eflag = NULL;
+
+static void
+create_nds32_seg (int nds32_sec_type)
+{
+
+ switch (nds32_sec_type)
+ {
+ case NDS32_SEC_SDATA_SBSS:
+ break;
+ case NDS32_SEC_EFLAG_ISR:
+ {
+ segT sec_t;
+
+ sec_eflag = sec_t = subseg_new (".nds32_e_flags", (subsegT) 0);
+
+ bfd_set_section_flags (stdoutput, sec_t,
+ SEC_HAS_CONTENTS | SEC_READONLY);
+
+ bfd_set_section_alignment (stdoutput, sec_t, 2);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/* 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
+};
+
+/* COLE: 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 (stdoutput, 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 aligment. */
+
+/* This code is referred from D30V for adjust label to be with pedning
+ aligment. For example,
+ LBYTE: .byte 0x12
+ LHALF: .half 0x12
+ LWORD: .word 0x12
+ Without this, the above label will not attatch to incoming data. */
+
+static void
+nds32_adjust_label (int n)
+{
+ /* FIXME: I think adjust lable and alignment is
+ the programmer's obligation. Saddly, 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 implmentation 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))
+ 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)
+{
+ /* COLE, Mar 24, 2012
+
+ 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 incommng 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
+nds32_aligned_cons (int idx)
+{
+ nds32_adjust_label (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 EX9 optimization replacing data. */
+ expressionS exp;
+ fixS *fixP;
+
+ exp.X_add_number = 0;
+ exp.X_op = O_constant;
+ fixP = fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0,
+ BFD_RELOC_NDS32_DATA);
+ fixP->fx_where -= (1 << idx);
+ fixP->fx_size = (1 << idx);
+ }
+}
+
+/* `.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_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. */
+ pic_code = 1;
+}
+
+static void
+nds32_abi (int ver)
+{
+ /* Another way to do -mabi.
+ This is for GCC internal use and should always be at the beginning
+ of code, otherwise, the effect is not determined. */
+ abi_ver = ver;
+}
+
+static void
+nds32_relax_relocs (int relax)
+{
+ char saved_char;
+ char *name;
+ int i;
+ char *subtype_relax[] =
+ {"", "", "ex9", "ifc"};
+
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE (*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;
+ enable_relax_ex9 = relax & enable_relax_ex9;
+ enable_relax_ifc = relax & enable_relax_ifc;
+ break;
+ case 2:
+ enable_relax_ex9 = relax;
+ break;
+ case 3:
+ enable_relax_ifc = relax;
+ 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)
+{
+ expressionS exp;
+
+ expression (&exp);
+ if (!use_hint_func_args)
+ return;
+
+ if (exp.X_op == O_constant)
+ {
+ if (exp.X_add_number >= 0 && exp.X_add_number < (1 << 6))
+ hint_func_args = exp.X_add_number;
+ else
+ as_warn (_("[gas error] Argument of .hint_func_args is out of range : %x."),
+ (unsigned int) exp.X_add_number);
+ }
+ else
+ as_warn (_("[gas error] Argument of .hint_func_args is not a constant."));
+}
+
+/* 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 (omit_frame_pointer == 0)
+ return;
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = abs_section_sym;
+ if (mode == 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
+ {
+ 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);
+ }
+}
+
+/* Insert relocations to mark the begin and end of ex9 region,
+ for further relaxation use.
+ bit[i] for $ri */
+
+static void
+nds32_no_ex9_begin (int mode)
+{
+ 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_NO_EX9_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_NO_EX9_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);
+ }
+}
+
+/* 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 (_("[gas warn] Different arguments of .vec_size are found, "
+ "previous %d, current %d"),
+ (int) vec_size,
+ (int) exp.X_add_number);
+ }
+ else
+ as_warn (_("[gas warn] Argument of .vec_size is expected 4 or 16,
actual: %d."),
+ (int) exp.X_add_number);
+ }
+ else
+ as_warn (_("[gas 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;
+ char *possible_flags[] = {"compiler_generated_asm"};
+
+ /* Skip whitespaces. */
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE (*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: compiler_generated_asm */
+ compiler_generated_asm = 1;
+ break;
+ default:
+ break;
+ }
+ break; /* Already found the flag, no need to continue next loop. */
+ }
+ }
+
+ *input_line_pointer = saved_char;
+ ignore_rest_of_line ();
+}
+
+/* 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", little, 1},
+ {"big", little, 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_pic, 0},
+ {"abi_1", nds32_abi, E_NDS_ABI_V1},
+ {"abi_2", nds32_abi, E_NDS_ABI_AABI},
+ {"abi_2fp", nds32_abi, E_NDS_ABI_V2FP},
+ {"relax", nds32_relax_relocs, 1},
+ {"no_relax", nds32_relax_relocs, 0},
+ {"hint_func_args", nds32_set_hint_func_args, 0},
+ {"omit_fp_begin", nds32_omit_fp_begin, 1},
+ {"omit_fp_end", nds32_omit_fp_begin, 0},
+ {"no_ex9_begin", nds32_no_ex9_begin, 1},
+ {"no_ex9_end", nds32_no_ex9_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},
+ {NULL, NULL, 0}
+};
+
+static void
+nds32_insert_align_reloc (int n)
+{
+ /* Optimize for space and label exists. */
+ fixS *fixP;
+ expressionS exp;
+
+ /* COLE: 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 = 0;
+ fixP = fix_new_exp (frag_now,
+ frag_now_fix (), 0, &exp, 0, BFD_RELOC_NDS32_LABEL);
+ /* Chain the fixup together. */
+ fixP->tc_fix_data = (fixS*) get_prev_fix ();
+ fixP->fx_offset = n;
+
+ set_prev_fix (fixP);
+}
+
+void
+nds32_pre_do_align (int n, char *fill, int len, int max)
+{
+ /* Only make a frag if we HAVE to... */
+ if (n != 0 && !need_pass_2)
+ {
+ if (fill == NULL)
+ {
+ if (subseg_text_p (now_seg))
+ frag_align_code (n, max);
+ 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)
+{
+ nds32_insert_align_reloc (n);
+}
+
+/* Supported Andes machines. */
+struct nds32_machs
+{
+ enum bfd_architecture bfd_mach;
+ int mach_flags;
+};
+
+/* 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 builtin_proc_entry *builtin_proc;
+ const char *hash_err = NULL;
+ nds32_regs *sym;
+ int i;
+
+ if (stdoutput != NULL)
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, march_cpu_opt);
+
+ /* Initialize the `cgen' interface.
+ Set the machine number and endian. */
+ gas_cgen_cpu_desc = nds32_cgen_cpu_open (CGEN_CPU_OPEN_BFDMACH,
+ bfd_printable_name (stdoutput),
+ CGEN_CPU_OPEN_ENDIAN,
+ (target_big_endian ?
+ CGEN_ENDIAN_BIG : CGEN_ENDIAN_LITTLE),
+ CGEN_CPU_OPEN_END);
+ nds32_cgen_init_asm (gas_cgen_cpu_desc);
+
+ /* The operand instance table is used during optimization to determine
+ which instructions can be executed in parallel. It is also used to give
+ warnings regarding operand interference in parallel instructions. */
+ nds32_cgen_init_opinst_table (gas_cgen_cpu_desc);
+
+ /* This is a callback from cgen to gas to parse operands. */
+ cgen_set_parse_operand_fn (gas_cgen_cpu_desc, gas_cgen_parse_operand);
+
+ gas_cgen_initialize_saved_fixups_array ();
+
+ /* Only the base substitution table and local label table are initialized;
+ the others (for local macro substitution) get instantiated as needed. */
+ local_label_hash[0] = hash_new ();
+ builtin_hash[0] = hash_new ();
+ for (builtin_proc = builtin_procs; builtin_proc->name; builtin_proc++)
+ hash_err = hash_insert (builtin_hash[0], builtin_proc->name,
+ (char *) builtin_proc);
+ builtin_recurse_hash = hash_new ();
+
+ reg5_hash = hash_new ();
+ reg4_hash = hash_new ();
+ reg3_hash = hash_new ();
+ for (sym = enable_reduce_regs ? reduce_regs : regs; sym->name; sym++)
+ {
+ /* Add basic registers to the symbol table. */
+ symbol_table_insert (symbol_new (sym->name, reg_section,
+ (valueT) sym->index,
+ &zero_address_frag));
+
+ if (sym->flag & ADDRESSABLE_5BIT)
+ hash_err = hash_insert (reg5_hash, sym->name, (char *) sym);
+ if (sym->flag & ADDRESSABLE_4BIT)
+ hash_err = hash_insert (reg4_hash, sym->name, (char *) sym);
+ if (sym->flag & ADDRESSABLE_3BIT)
+ hash_err = hash_insert (reg3_hash, sym->name, (char *) sym);
+ }
+
+ nds32_init_nds32_pseudo_opcodes ();
+
+ if (enable_fpu_sp_extension || enable_fpu_dp_extension)
+ {
+ fs5_hash = hash_new ();
+ for (sym = fs_regs, i = 0; i < fs_reg_num; sym++, i++)
+ {
+ symbol_table_insert (symbol_new (sym->name, reg_section,
+ (valueT) sym->index,
+ &zero_address_frag));
+
+ hash_err = hash_insert (fs5_hash, sym->name, (char *) sym);
+ }
+
+ fd5_hash = hash_new ();
+ for (sym = fd_regs, i = 0; i < fd_reg_num; sym++, i++)
+ {
+ /* Add basic registers to the symbol table. */
+ symbol_table_insert (symbol_new (sym->name, reg_section,
+ (valueT) sym->index,
+ &zero_address_frag));
+
+ hash_err = hash_insert (fd5_hash, sym->name, (char *) sym);
+ }
+ }
+
+ if (hash_err)
+ as_bad ("Fail to initialize hash table for register "
+ "symbols in md_begin (). Reason: %s.", hash_err);
+
+ nds32_init_insn_num_map ();
+
+ init_cfg_var ();
+}
+
+/* This function counts number of operands in front of subtype. */
+
+static int
+parse_reg_count (char *insn, char **pos)
+{
+ int n;
+
+ for (*pos = insn, n = 0; *pos != NULL; n++)
+ {
+ char *tmp;
+
+ while (ISSPACE ((*pos)[0]))
+ (*pos)++;
+ if (!strncmp (*pos, "$", 1))
+ {
+ if ((tmp = strchr (*pos, ',')) == NULL)
+ break;
+ else
+ *pos = tmp + 1;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return n;
+}
+
+/* COLE: This should be the responsibility of cgen. */
+/* This function performs special checking on cctl and tlbop instructions. */
+
+static int
+is_cctl_or_tlbop (char *insn)
+{
+ char *file; /* filename of current source. */
+ unsigned int line; /* line number of current source. */
+
+ /* First idea to fixup bugzilla 1127.
+ Since current (Harry@Apr.10.2007) nds32_start_line_hook() grab
+ line pointer to local, the line number is wrong.
+ Thus, I get current filename ans line number using as_where(),
+ then, plus line number by one.
+ So, use this workaround for in nds32_start_line_hook() and
+ is_cctl_or_tlbop().
+ Also we need to use as_bad_where() instead of as_bad(). */
+ as_where (&file, &line);
+ line += 1;
+
+ while (ISSPACE (insn[0]))
+ insn++;
+
+ if (strncasecmp (insn, "cctl", 4) == 0 && ISSPACE (insn[4]))
+ {
+ /* Check cctl. */
+ int n, i;
+ char *pos, *end;
+ char *subtype_tbl[] = {
+ "L1D_IX_INVAL", "L1D_IX_WB", "L1D_IX_WBINVAL", "L1D_IX_RTAG",
+ "L1D_IX_RWD", "L1D_IX_WTAG", "L1D_IX_WWD", "L1D_INVALALL",
+ "L1D_VA_INVAL", "L1D_VA_WB", "L1D_VA_WBINVAL", "L1D_VA_FILLCK",
+ "L1D_VA_ULCK", "", "", "L1D_WBALL",
+ "L1I_IX_INVAL", "", "", "L1I_IX_RTAG",
+ "L1I_IX_RWD", "L1I_IX_WTAG", "L1I_IX_WWD", "",
+ "L1I_VA_INVAL", "", "", "L1I_VA_FILLCK",
+ "L1I_VA_ULCK", "", "", "",
+ };
+ int subtype = -1;
+ int level;
+ char opline[64] = { 0 };
+
+ for (insn += 5; ISSPACE (insn[0]); insn++)
+ /* Skip space. */ ;
+
+ /* 'pos' pointers to SubType. */
+ n = parse_reg_count (insn, &pos);
+
+ strncpy (opline, pos, sizeof (opline) - 1);
+ for (end = opline + strlen (opline) - 1; end > opline; end--)
+ if (ISSPACE (*end) || is_end_of_line[(unsigned char) *end])
+ *end = '\0';
+ else
+ break;
+
+ /* Convert SubType to numeric value. */
+ subtype = strtol (opline, &end, 10);
+
+ if (*end != '\0' && *end != ',')
+ {
+ /* Parse mnemonic string. */
+ subtype = -1;
+ end = strchr (opline, ',');
+ if (end)
+ *end++ = '\0';
+ else
+ end = strlen (opline) + opline;
+ /* To upper. */
+ for (pos = opline; *pos; pos++)
+ *pos = TOUPPER (*pos);
+ for (i = 0; i < (int) ARRAY_SIZE (subtype_tbl); i++)
+ if (strcmp (subtype_tbl[i], opline) == 0)
+ subtype = i;
+ }
+ else if (*end == ',')
+ end++;
+ pos = end;
+
+ if (subtype == -1)
+ as_bad_where (file, line, _("Unknown CCTL subtype field. %s"), insn);
+
+ /* Parse level. */
+ if (!pos)
+ /* Default 1level. */
+ level = 0;
+ else if (strcmp (pos, "1level") == 0)
+ level = 0;
+ else if (strcmp (pos, "alevel") == 0)
+ level = 1;
+ else
+ {
+ level = strtol (pos, &end, 10);
+ if (*end != '\0')
+ as_bad_where (file, line, _("Unknown CCTL level field. %s"),
+ insn);
+ }
+ if (level)
+ nds32_flags |= E_NDS32_HAS_L2C_INST;
+
+ /* Check level only for VA write-back or invalidation. */
+ if (level && (((subtype & 8) == 0) || (subtype % 8 >= 3)))
+ as_warn_where (file, line,
+ _("CCTL alevel is only allowed for VA write-back "
+ "or invalidate operations. %s\n"), insn);
+
+
+ /* Check register number. */
+ switch (subtype)
+ {
+ case 8: case 9: case 24:
+ /* 1 or 2 registers. */
+ if (n != 1 && n != 2)
+ as_bad_where (file, line,
+ _("Invalid syntax for the specified subtype"));
+ break;
+ case 0: case 1: case 16: case 27: case 28:
+ /* 1 register. */
+ if (n != 1)
+ as_bad_where (file, line,
+ _("Invalid syntax for the specified subtype"));
+ break;
+ case 3: case 4: case 5: case 6:
+ case 19: case 20: case 21: case 22:
+ /* 2 registers. */
+ if (n != 2)
+ as_bad_where (file, line,
+ _("Invalid syntax for the specified subtype. %s"),
+ insn);
+ break;
+ case 7: case 15:
+ /* 0 registers. */
+ if (n != 0)
+ as_bad_where (file, line,
+ _("Invalid syntax for the specified subtype. %s"),
+ insn);
+ break;
+ default:
+ as_warn_where (file, line, _("Unknow CCTL subtype. %s\n"), insn);
+ break;
+ }
+
+ return 1;
+ }
+ else if (strncasecmp (insn, "tlbop", 5) == 0 && ISSPACE (insn[5]))
+ {
+ /* Check tlbop. */
+ char *subtype;
+
+ for (insn += 6; ISSPACE (insn[0]); insn++)
+ /* Skip white space. */ ;
+
+ subtype = nds32_strcasestr (insn, "FLUA");
+ if (subtype == NULL)
+ subtype = nds32_strcasestr (insn, "FlushAll");
+ if (subtype != NULL)
+ {
+ /* FlushAll or FLUA expects no register. */
+ if (subtype != insn)
+ as_bad_where (file, line,
+ _("Invalid syntax for the specified subtype"));
+ }
+ else if (!(insn[0] == '7' && ISSPACE (insn[1])))
+ {
+ char *pos;
+ int n = parse_reg_count (insn, &pos);
+
+ if (ISDIGIT (pos[0]))
+ {
+ int stno = atoi (pos);
+
+ if ((stno == 5 && n != 2)
+ || (((stno >= 0 && stno <= 4) || stno == 6) && n != 1))
+ as_bad_where (file, line,
+ _("Invalid syntax for the specified subtype"));
+ }
+ else
+ {
+ subtype = nds32_strcasestr (pos, "PB");
+ if (subtype == NULL)
+ subtype = nds32_strcasestr (pos, "Probe");
+ if ((n != 1 && subtype == NULL) || (n != 2 && subtype != NULL))
+ as_bad_where (file, line,
+ _("Invalid syntax for the specified subtype"));
+ }
+ }
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* md_start_line_hook, used to substitution symbols.
+ It hijacks input_line_pointer, replacing it with our
+ substituted string and returns the new buffer limit. */
+
+void
+nds32_start_line_hook (void)
+{
+ char *line, *endp;
+ char *replacement = NULL;
+
+ /* Work with a copy of the input line, including EOL char. */
+ endp = input_line_pointer;
+ /* Skip line-separate-chars. E.g., ;, \n. */
+ while (!is_end_of_line[(unsigned char) *endp++])
+ ;
+ line = xmalloc (endp - input_line_pointer + 1);
+ strncpy (line, input_line_pointer, endp - input_line_pointer + 1);
+ line[endp - input_line_pointer] = 0;
+
+ /* Need to handle cctl and tlbop specially. */
+ if (!is_cctl_or_tlbop (line))
+ {
+ /* Within a macro or not? */
+ if (macro_level > 0)
+ {
+ /* Yes, first process forced replacements. */
+ char *tmp = builtin_substitute (line, 1);
+
+ if (tmp == line)
+ replacement = builtin_substitute (line, 0);
+ else
+ {
+ replacement = builtin_substitute (tmp, 0);
+ if (replacement != tmp)
+ free (tmp);
+ }
+ }
+ else
+ /* No. */
+ replacement = builtin_substitute (line, 0);
+
+ if (replacement != line)
+ {
+ char *tmp = replacement;
+ char *comment = strpbrk (replacement, comment_chars);
+ char endc = replacement[strlen (replacement) - 1];
+
+ /* Clean up the replacement; we'd prefer to have this done by the
+ standard preprocessing equipment (maybe do_scrub_chars?)
+ but for now, do a quick-and-dirty. */
+ if (comment != NULL)
+ {
+ comment[0] = endc;
+ comment[1] = 0;
+ --comment;
+ }
+ else
+ comment = replacement + strlen (replacement) - 1;
+
+ /* Trim trailing whitespace. */
+ while (ISSPACE (*comment))
+ {
+ comment[0] = endc;
+ comment[1] = 0;
+ --comment;
+ }
+
+ /* Compact leading whitespace. */
+ while (ISSPACE (tmp[0]) && ISSPACE (tmp[1]))
+ ++tmp;
+
+ /* It is ugly - we need to call bump_line_counters,
+ if it is not macro expansion. */
+ if (macro_level == 0)
+ bump_line_counters ();
+
+ input_line_pointer = endp;
+ input_scrub_insert_line (tmp);
+ free (replacement);
+ }
+ }
+ free (line);
+}
+
+/* Non-zero if we've seen a relaxable insn since the last 32 bit alignment
+ request. */
+static int seen_relaxable_p = 0;
+
+/* Structure to hold all of the different components describing an individual
+ instruction. */
+
+typedef struct
+{
+ const CGEN_INSN *insn;
+ const CGEN_INSN *orig_insn;
+ CGEN_FIELDS fields;
+#if CGEN_INT_INSN_P
+ CGEN_INSN_INT buffer[1];
+#define INSN_VALUE(buf) (*(buf))
+#else
+ unsigned char buffer[CGEN_MAX_INSN_SIZE];
+#define INSN_VALUE(buf) (buf)
+#endif
+ char *addr;
+ fragS *frag;
+ int num_fixups;
+ fixS *fixups[GAS_CGEN_MAX_FIXUPS];
+ int indices[MAX_OPERAND_INSTANCES];
+} nds32_insn;
+
+/* 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, fix;
+ 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;
+ fix = 0;
+
+ if (bytes & 1)
+ {
+ /* Cannot be in code sections, so pack a 0x00. */
+ *p++ = 0;
+ bytes--;
+ fix = 1;
+ }
+
+ if (bytes & 2)
+ {
+ /* Can be in any sections, so better pack a 16-bit nop. */
+ 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;
+ fix += 2;
+ }
+
+ /* We need to pack a 32-bit nop. */
+ while (bytes >= 4)
+ {
+ /* To save the trouble, pack a 32-bit nop. */
+ memcpy (p, nop32, 4);
+ p += 4;
+ bytes -= 4;
+ fix += 4;
+ }
+
+ fragp->fr_fix += fix;
+ fragp->fr_var = 4;
+}
+
+/* md_flush_pending_output */
+
+void
+nds32_flush_pending_output (void)
+{
+ nds32_last_label = NULL;
+}
+
+void
+nds32_frob_label (symbolS *label)
+{
+ struct insn_label_list *insn_label;
+
+ if (optimize && subseg_text_p (now_seg))
+ {
+ label_exist = 1;
+ }
+
+ nds32_last_label = label;
+
+ /* COLE:
+ Used for BB analysis for relaxation?
+ I think this should be put in nds32_frob_label,
+ because it calls this function too and
+ tc_frob_label is call once a label is created. */
+ if (bb_now->insn_head != NULL || bb_now->frchain_now != frchain_now)
+ create_new_basic_block ();
+ insn_label = xmalloc (sizeof (insn_label_list));
+ insn_label->label = label;
+ insn_label->next = bb_now->label_list;
+ bb_now->label_list = insn_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
+}
+
+
+/* remove _r of relaxable instruction for display. */
+
+static char *
+rem__r (char *str)
+{
+ char *tmp = str;
+
+ /* Skip label if any. */
+ while (*str && *str != ':')
+ {
+ str++;
+ }
+
+ if (*str == 0)
+ {
+ /* No label, start from beginning. */
+ str = tmp;
+ }
+ else
+ {
+ /* Label found, skip it. */
+ str++;
+ }
+
+ /* Skip white space. */
+ while (*str == ' ' || *str == '\t')
+ {
+ str++;
+ }
+
+ /* Search _r until white space. */
+ while (*str != ' ' && *str != '\t')
+ {
+ if (*str == '_' && *(str + 1) == 'r')
+ {
+ *str = ' ';
+ *(str + 1) = ' ';
+ break;
+ }
+
+ str++;
+ }
+
+ return tmp;
+}
+
+static int
+reg_out_of_range (char *str)
+{
+ char *p;
+ int check_one_operand = 0;
+ int got_opcode = 0;
+
+ p = alloca (strlen (str) + 1);
+ strcpy (p, str);
+ p = strtok (p, " ,;+-<>[]");
+ while (p)
+ {
+ if (*p == '$')
+ {
+ /* Register found? */
+ if (!builtin_isreg (p, 0))
+ {
+ /* Invalid register used. */
+ return 1;
+ }
+ else if (check_one_operand)
+ {
+ return 0;
+ }
+ }
+ else if (!got_opcode)
+ {
+ got_opcode = 1;
+ if (!
+ (strcmp (p, "mfusr") && strcmp (p, "mtusr")
+ && strcmp (p, "mfsr") && strcmp (p, "mtsr")))
+ {
+ check_one_operand = 1;
+ }
+ }
+
+ p = strtok (NULL, " ,;+-<>[]");
+ }
+
+ return 0;
+}
+
+static dep_node *
+append_dep_node (dep_node ** node, short reg, short flag)
+{
+ dep_node *next = xmalloc (sizeof (dep_node));
+ next->reg_num = reg;
+ next->flag = flag;
+ if (*node != NULL)
+ {
+ next->next = (*node)->next;
+ (*node)->next = next;
+ }
+ else
+ {
+ next->next = NULL;
+ *node = next;
+ }
+ return next;
+}
+
+static void
+insert_reg_to_node_list (dep_node ** node_list, short reg, short flag,
+ int merge)
+{
+ dep_node *node = *node_list, *node2 = NULL;
+
+ while (node != NULL)
+ {
+ node2 = node;
+ if (node->reg_num == reg)
+ {
+ if (merge)
+ node->flag |= flag;
+ return;
+ }
+ node = node->next;
+ }
+
+ if (node2 == NULL)
+ append_dep_node (node_list, reg, flag);
+ else
+ append_dep_node (&node2, reg, flag);
+}
+
+static void
+analysis_regdep (nds32_insn_instant *insn_s, nds32_insn *insn,
+ CGEN_FIELDS *fields, const CGEN_OPINST *oi_p)
+{
+ const CGEN_OPINST *oi, *oi_bak;
+ int i, mask4, type;
+ dep_node *gr_dep_list;
+ short reg;
+ char *reg_name;
+
+ if (CGEN_INSN_NUM (insn->insn) == -1)
+ oi = oi_p;
+ else
+ oi = insn->insn->opinst;
+ oi_bak = oi;
+
+ switch (insn_s->insn_num)
+ {
+ case NDS32_INSN_LMW_BI:
+ case NDS32_INSN_LMW_BIM:
+ case NDS32_INSN_LMW_BD:
+ case NDS32_INSN_LMW_BDM:
+ case NDS32_INSN_LMW_AI:
+ case NDS32_INSN_LMW_AIM:
+ case NDS32_INSN_LMW_AD:
+ case NDS32_INSN_LMW_ADM:
+ case NDS32_INSN_LMWA_BI:
+ case NDS32_INSN_LMWA_BIM:
+ case NDS32_INSN_LMWA_BD:
+ case NDS32_INSN_LMWA_BDM:
+ case NDS32_INSN_LMWA_AI:
+ case NDS32_INSN_LMWA_AIM:
+ case NDS32_INSN_LMWA_AD:
+ case NDS32_INSN_LMWA_ADM:
+ case NDS32_INSN_LMWZB_B:
+ case NDS32_INSN_LMWZB_BM:
+ case NDS32_INSN_LMWZB_A:
+ case NDS32_INSN_LMWZB_AM:
+ if (insn->insn->
+ base->mnemonic[strlen (insn->insn->base->mnemonic) - 1] == 'm')
+ gr_dep_list = append_dep_node (&insn_s->gr_dep_list,
+ (short) CGEN_FIELDS_RA5 (*fields),
+ (short) (CGEN_OPINST_INPUT_MASK
+ | CGEN_OPINST_OUTPUT_MASK));
+ else
+ gr_dep_list = append_dep_node (&insn_s->gr_dep_list,
+ (short) CGEN_FIELDS_RA5 (*fields),
+ (short) CGEN_OPINST_INPUT_MASK);
+ for (i = CGEN_FIELDS_RT5 (*fields); i <= CGEN_FIELDS_RB5 (*fields); i++)
+ {
+ gr_dep_list = append_dep_node (&gr_dep_list, (short) i,
+ (short) CGEN_OPINST_OUTPUT_MASK);
+ }
+ mask4 = CGEN_FIELDS_MASK4 (*fields);
+ for (i = 28; i < 32; i++)
+ {
+ if ((mask4 & 0x1000) > 0)
+ {
+ gr_dep_list = append_dep_node (&gr_dep_list, (short) i,
+ (short) CGEN_OPINST_OUTPUT_MASK);
+ }
+ mask4 = mask4 << 1;
+ }
+ return;
+ case NDS32_INSN_SMW_BI:
+ case NDS32_INSN_SMW_BIM:
+ case NDS32_INSN_SMW_BD:
+ case NDS32_INSN_SMW_BDM:
+ case NDS32_INSN_SMW_AI:
+ case NDS32_INSN_SMW_AIM:
+ case NDS32_INSN_SMW_AD:
+ case NDS32_INSN_SMW_ADM:
+ case NDS32_INSN_SMWA_BI:
+ case NDS32_INSN_SMWA_BIM:
+ case NDS32_INSN_SMWA_BD:
+ case NDS32_INSN_SMWA_BDM:
+ case NDS32_INSN_SMWA_AI:
+ case NDS32_INSN_SMWA_AIM:
+ case NDS32_INSN_SMWA_AD:
+ case NDS32_INSN_SMWA_ADM:
+ case NDS32_INSN_SMWZB_B:
+ case NDS32_INSN_SMWZB_BM:
+ case NDS32_INSN_SMWZB_A:
+ case NDS32_INSN_SMWZB_AM:
+ if (insn->insn->
+ base->mnemonic[strlen (insn->insn->base->mnemonic) - 1] == 'm')
+ gr_dep_list = append_dep_node (&insn_s->gr_dep_list,
+ (short) CGEN_FIELDS_RA5 (*fields),
+ (short) (CGEN_OPINST_INPUT_MASK
+ | CGEN_OPINST_OUTPUT_MASK));
+ else
+ gr_dep_list = append_dep_node (&insn_s->gr_dep_list,
+ (short) CGEN_FIELDS_RA5 (*fields),
+ (short) CGEN_OPINST_INPUT_MASK);
+ for (i = CGEN_FIELDS_RT5 (*fields); i <= CGEN_FIELDS_RB5 (*fields); i++)
+ {
+ gr_dep_list = append_dep_node (&gr_dep_list, (short) i,
+ (short) CGEN_OPINST_INPUT_MASK);
+ }
+ mask4 = CGEN_FIELDS_MASK4 (*fields);
+ for (i = 28; i < 32; i++)
+ {
+ if ((mask4 & 0x1000) > 0)
+ {
+ gr_dep_list = append_dep_node (&gr_dep_list, (short) i,
+ (short) CGEN_OPINST_INPUT_MASK);
+ }
+ mask4 = mask4 << 1;
+ }
+ return;
+ case NDS32_INSN_SYSCALL:
+ gr_dep_list = append_dep_node (&insn_s->gr_dep_list, (short) 0,
+ (short) (CGEN_OPINST_INPUT_MASK
+ | CGEN_OPINST_OUTPUT_MASK));
+ for (i = 1; i <= 5; i++)
+ {
+ gr_dep_list = append_dep_node (&gr_dep_list, (short) i,
+ (short) CGEN_OPINST_INPUT_MASK);
+ }
+ return;
+ /* Coprocessor instruction set. */
+ case NDS32_INSN_MFCPW_CP1:
+ case NDS32_INSN_MFCPW_CP2:
+ case NDS32_INSN_MFCPW_CP3:
+ gr_dep_list = append_dep_node (&insn_s->gr_dep_list,
+ (short) CGEN_FIELDS_RT5 (*fields),
+ (short) (CGEN_OPINST_OUTPUT_MASK));
+ return;
+ case NDS32_INSN_BSE:
+ case NDS32_INSN_BSP:
+ gr_dep_list = append_dep_node (&insn_s->gr_dep_list,
+ (short) CGEN_FIELDS_RT5 (*fields),
+ (short) (CGEN_OPINST_INPUT_MASK
+ | CGEN_OPINST_OUTPUT_MASK));
+ gr_dep_list = append_dep_node (&gr_dep_list,
+ (short) CGEN_FIELDS_RT5 (*fields),
+ (short) CGEN_OPINST_OUTPUT_MASK);
+ gr_dep_list = append_dep_node (&gr_dep_list,
+ (short) CGEN_FIELDS_RA5 (*fields),
+ (short) CGEN_OPINST_INPUT_MASK);
+ gr_dep_list = append_dep_node (&gr_dep_list,
+ (short) CGEN_FIELDS_RB5 (*fields),
+ (short) (CGEN_OPINST_INPUT_MASK
+ | CGEN_OPINST_OUTPUT_MASK));
+ return;
+
+ case NDS32_INSN_FMFSR:
+ case NDS32_INSN_FMFDR:
+ gr_dep_list = append_dep_node (&insn_s->gr_dep_list,
+ (short) CGEN_FIELDS_RT5 (*fields),
+ (short) CGEN_OPINST_OUTPUT_MASK);
+ return;
+ case NDS32_INSN_MOVD44:
+ /* See CGEN_OPINST sfmt_movd44_ops in opcode/nds32-opinst.c. */
+ gr_dep_list = append_dep_node (&insn_s->gr_dep_list,
+ (short) CGEN_FIELDS_RT5E (*fields) * 2,
+ (short) CGEN_OPINST_OUTPUT_MASK);
+ gr_dep_list = append_dep_node (&insn_s->gr_dep_list,
+ (short) CGEN_FIELDS_RT5E (*fields) * 2 + 1,
+ (short) CGEN_OPINST_OUTPUT_MASK);
+ gr_dep_list = append_dep_node (&insn_s->gr_dep_list,
+ (short) CGEN_FIELDS_RA5E (*fields) * 2,
+ (short) CGEN_OPINST_INPUT_MASK);
+ gr_dep_list = append_dep_node (&insn_s->gr_dep_list,
+ (short) CGEN_FIELDS_RA5E (*fields) * 2 + 1,
+ (short) CGEN_OPINST_INPUT_MASK);
+ return;
+ default:
+ ;
+ }
+
+ for (reg = -1; oi->type != CGEN_OPINST_END; oi++, reg = -1)
+ {
+ gr_dep_list = insn_s->gr_dep_list;
+ switch (oi->hw_type)
+ {
+ case HW_H_GR:
+ case HW_H_GR16:
+ case HW_H_GR8:
+ case HW_H_GR8E5:
+ case HW_H_GR_EVEN:
+ case HW_H_GR_ODD:
+ case HW_H_GR_LO:
+ case HW_H_GR_LO_EVEN:
+ case HW_H_GR_LO_ODD:
+ case HW_H_GR_BOTTOM:
+ case HW_H_GR_TOP:
+ case HW_H_GR_LO_BOTTOM:
+ case HW_H_GR_LO_TOP:
+ if (!oi->op_type)
+ {
+ if (oi->index)
+ reg = oi->index;
+ else
+ {
+ reg_name = (char *) (oi->name + strlen (oi->name) - 2);
+ if (strcmp (reg_name, "FP") == 0)
+ reg = 28;
+ else if (strcmp (reg_name, "GP") == 0)
+ reg = 29;
+ else if (strcmp (reg_name, "LP") == 0)
+ reg = 30;
+ else if (strcmp (reg_name, "SP") == 0)
+ reg = 31;
+ }
+ }
+ else
+ reg = nds32_cgen_get_int_operand (gas_cgen_cpu_desc, oi->op_type,
+ fields);
+ break;
+ default:
+ break;
+ }
+
+ if (reg < 0 || reg > 31)
+ continue;
+
+ if (oi->type == CGEN_OPINST_INPUT)
+ insert_reg_to_node_list (&insn_s->gr_dep_list, reg,
+ (short) CGEN_OPINST_INPUT_MASK, TRUE);
+ else if (oi->type == CGEN_OPINST_OUTPUT)
+ insert_reg_to_node_list (&insn_s->gr_dep_list, reg,
+ (short) CGEN_OPINST_OUTPUT_MASK, TRUE);
+ }
+
+ for (oi = oi_bak, type = 0, reg = -1; oi->type != CGEN_OPINST_END;
+ oi++, type = 0, reg = -1)
+ {
+ gr_dep_list = insn_s->gr_dep_list;
+ switch (oi->hw_type)
+ {
+ case HW_H_UINT:
+ if (strncmp (oi->name, "f_32_r", 6) == 0)
+ {
+ /* Insn->insn->opcode->syntax.syntax[1]. */
+ if (strncmp (oi->name + 6, "t5", 2) == 0)
+ {
+ for (i = 0; insn->insn->opcode->syntax.syntax[i] != 0; i++)
+ if ((CGEN_SYNTAX_CHAR_P
+ (insn->insn->opcode->syntax.syntax[i]) == 0)
+ && (insn->insn->opcode->syntax.syntax[i]
+ == CGEN_SYNTAX_MAKE_FIELD (NDS32_OPERAND_RT5)
+ || (insn->insn->opcode->syntax.syntax[i]
+ == CGEN_SYNTAX_MAKE_FIELD (NDS32_OPERAND_RT5H))))
+ break;
+ }
+ else if (strncmp (oi->name + 6, "a5", 2) == 0)
+ {
+ for (i = 0; insn->insn->opcode->syntax.syntax[i] != 0; i++)
+ if ((CGEN_SYNTAX_CHAR_P
+ (insn->insn->opcode->syntax.syntax[i]) == 0)
+ && (insn->insn->opcode->syntax.syntax[i]
+ == CGEN_SYNTAX_MAKE_FIELD (NDS32_OPERAND_RA5)
+ || (insn->insn->opcode->syntax.syntax[i]
+ == CGEN_SYNTAX_MAKE_FIELD (NDS32_OPERAND_RA5H))
+ || (insn->insn->opcode->syntax.syntax[i]
+ >= CGEN_SYNTAX_MAKE_FIELD (NDS32_OPERAND_RA5_A)
+ && insn->insn->opcode->syntax.syntax[i]
+ <= CGEN_SYNTAX_MAKE_FIELD (NDS32_OPERAND_RA5_A_T))))
+ break;
+
+ /* For FPU & COProcessor load/store with .bi form.
+ The definitions in the nds32-opinst only indicate $ra is
+ an input for these instructions.
+ Following process is used to detect if current instruction
+ is in .bi form.
+ This method is a little tricky since there is too much
+ instructions with .bi form, and it is hard to detect them
+ use switch table or sequential if/else checking. */
+ if (oi == gas_cgen_cpu_desc->insn_table.init_entries[NDS32_INSN_FLS].opinst
+ || oi ==
gas_cgen_cpu_desc->insn_table.init_entries[NDS32_INSN_FLSI].opinst
+ || oi ==
gas_cgen_cpu_desc->insn_table.init_entries[NDS32_INSN_FLDI].opinst)
+ {
+ if (strcmp ((insn->insn->base->mnemonic
+ + strlen (insn->insn->base->mnemonic) - 3),
+ ".bi"))
+ type |= CGEN_OPINST_OUTPUT_MASK;
+ }
+ }
+ else if (strncmp (oi->name + 6, "b5", 2) == 0)
+ {
+ for (i = 0; insn->insn->opcode->syntax.syntax[i] != 0; i++)
+ if ((CGEN_SYNTAX_CHAR_P
+ (insn->insn->opcode->syntax.syntax[i] == 0))
+ && (insn->insn->opcode->syntax.syntax[i]
+ == CGEN_SYNTAX_MAKE_FIELD (NDS32_OPERAND_RB5)
+ || (insn->insn->opcode->syntax.syntax[i]
+ == CGEN_SYNTAX_MAKE_FIELD (NDS32_OPERAND_RB5H))
+ || (insn->insn->opcode->syntax.syntax[i]
+ >= CGEN_SYNTAX_MAKE_FIELD (NDS32_OPERAND_RB5_A)
+ && insn->insn->opcode->syntax.syntax[i]
+ <= CGEN_SYNTAX_MAKE_FIELD (NDS32_OPERAND_RB5P_A_T))))
+ break;
+ }
+ else if (strncmp (oi->name + 6, "s5", 2) == 0)
+ {
+ for (i = 0; insn->insn->opcode->syntax.syntax[i] != 0; i++)
+ if (insn->insn->opcode->syntax.syntax[i]
+ == CGEN_SYNTAX_MAKE_FIELD (NDS32_OPERAND_RS5))
+ break;
+ }
+ else
+ break;
+
+ if (insn->insn->opcode->syntax.syntax[i])
+ reg = nds32_cgen_get_int_operand
+ (gas_cgen_cpu_desc,
+ CGEN_SYNTAX_FIELD (insn->insn->opcode->syntax.syntax[i]),
+ fields);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (reg < 0 || reg > 31)
+ continue;
+
+ if (oi->type == CGEN_OPINST_INPUT)
+ type |= CGEN_OPINST_INPUT;
+ else if (oi->type == CGEN_OPINST_OUTPUT)
+ type |= CGEN_OPINST_OUTPUT;
+ insert_reg_to_node_list (&insn_s->gr_dep_list, reg, (short) type,
+ FALSE);
+ }
+}
+
+static void
+assign_instant (nds32_insn_instant *insn_s, int insn_num,
+ finished_insnS *fi, short insn_szp, short byte_szp,
+ nds32_insn *insn, int relaxable, const CGEN_OPINST *oi)
+{
+ int i;
+ CGEN_FIELDS *fields = &insn->fields;
+ insn_s->insn_num = insn_num;
+ insn_s->addr = fi->addr;
+ insn_s->frag = fi->frag;
+ insn_s->num_fixups = fi->num_fixups;
+ insn_s->byte_sz = byte_szp;
+ insn_s->insn_sz = insn_szp;
+ insn_s->gr_dep_list = NULL;
+ insn_s->parent = bb_now;
+ insn_s->md_relax = relaxable;
+ if (is_func_branch (insn_num) == IS_FUNC_CALL_MASK
+ || is_func_branch (insn_num) == IS_COND_FUNC_CALL_MASK)
+ {
+ insn_s->hint_func_args = hint_func_args;
+ hint_func_args = 0;
+ }
+ insn_s->reloc_node = NULL;
+ for (i = 0; i < insn_s->num_fixups; i++)
+ insn_s->fixups[i] = fi->fixups[i];
+ if (insn_num > NDS32_INSN_INVALID && insn_num < MAX_INSNS)
+ analysis_regdep (insn_s, insn, fields, oi);
+ if (prev_insn_s != NULL)
+ prev_insn_s->next = insn_s;
+ insn_s->prev = prev_insn_s;
+ insn_s->next = NULL;
+ prev_insn_s = insn_s;
+}
+
+/* Since the nds32_cgen_macro_insn_table always provide insn_num as -1,
+ it is impossible to get real insn_num of an alias instruction. */
+
+static int
+convert_macro_insn_to_insn (nds32_insn *pinsn, const CGEN_OPINST ** oi)
+{
+ int insn_num = NDS32_INSN_INVALID;
+ CGEN_FIELDS *fields = &pinsn->fields;
+ uint32_t insn;
+
+ if (CGEN_INSN_NUM (pinsn->insn) != -1)
+ return CGEN_INSN_NUM (pinsn->insn);
+
+ insn = INSN_VALUE (pinsn->buffer);
+ if (CGEN_INSN_BITSIZE (pinsn->insn) == 32)
+ {
+ switch (N32_OP6 (insn))
+ {
+ case N32_OP6_LBI:
+ insn_num = NDS32_INSN_LBI;
+ break;
+ case N32_OP6_LHI:
+ insn_num = NDS32_INSN_LHI;
+ break;
+ case N32_OP6_LWI:
+ insn_num = NDS32_INSN_LWI;
+ break;
+ case N32_OP6_LBI_BI:
+ insn_num = NDS32_INSN_LBI_BI;
+ break;
+ case N32_OP6_LHI_BI:
+ insn_num = NDS32_INSN_LHI_BI;
+ break;
+ case N32_OP6_LWI_BI:
+ insn_num = NDS32_INSN_LWI_BI;
+ break;
+ case N32_OP6_SBI:
+ insn_num = NDS32_INSN_SBI;
+ break;
+ case N32_OP6_SHI:
+ insn_num = NDS32_INSN_SHI;
+ break;
+ case N32_OP6_SWI:
+ insn_num = NDS32_INSN_SWI;
+ break;
+ case N32_OP6_SBI_BI:
+ insn_num = NDS32_INSN_SBI_BI;
+ break;
+ case N32_OP6_SHI_BI:
+ insn_num = NDS32_INSN_SHI_BI;
+ break;
+ case N32_OP6_SWI_BI:
+ insn_num = NDS32_INSN_SWI_BI;
+ break;
+ case N32_OP6_DPREFI:
+ if (insn & 0x1000000)
+ insn_num = NDS32_INSN_DPREFI_D;
+ else
+ insn_num = NDS32_INSN_DPREFI_W;
+ break;
+ case N32_OP6_LWC:
+ if ((insn & 0x6000) == 0)
+ {
+ /* CP0 */
+ if (insn & 0x1000)
+ insn_num = NDS32_INSN_FLSI_BI;
+ else
+ insn_num = NDS32_INSN_FLSI;
+ }
+ else
+ insn_num = 0;
+ break;
+ case N32_OP6_LDC:
+ if ((insn & 0x6000) == 0)
+ {
+ if (insn & 0x1000)
+ insn_num = NDS32_INSN_FLDI_BI;
+ else
+ insn_num = NDS32_INSN_FLDI;
+ }
+ else
+ insn_num = 0;
+ break;
+ case N32_OP6_MEM: /* Load and store. */
+ switch (insn & 0xff)
+ {
+ case N32_MEM_LB:
+ insn_num = NDS32_INSN_LB;
+ break;
+ case N32_MEM_LH:
+ insn_num = NDS32_INSN_LH;
+ break;
+ case N32_MEM_LW:
+ insn_num = NDS32_INSN_LW;
+ break;
+ case N32_MEM_LB_BI:
+ insn_num = NDS32_INSN_LB_BI;
+ break;
+ case N32_MEM_LH_BI:
+ insn_num = NDS32_INSN_LH_BI;
+ break;
+ case N32_MEM_LW_BI:
+ insn_num = NDS32_INSN_LW_BI;
+ break;
+ case N32_MEM_SB:
+ insn_num = NDS32_INSN_SB;
+ break;
+ case N32_MEM_SH:
+ insn_num = NDS32_INSN_SH;
+ break;
+ case N32_MEM_SW:
+ insn_num = NDS32_INSN_SW;
+ break;
+ case N32_MEM_SB_BI:
+ insn_num = NDS32_INSN_SB_BI;
+ break;
+ case N32_MEM_SH_BI:
+ insn_num = NDS32_INSN_SH_BI;
+ break;
+ case N32_MEM_SW_BI:
+ insn_num = NDS32_INSN_SW_BI;
+ break;
+ case N32_MEM_LBS:
+ insn_num = NDS32_INSN_LBS;
+ break;
+ case N32_MEM_LHS:
+ insn_num = NDS32_INSN_LHS;
+ break;
+ case N32_MEM_DPREF:
+ insn_num = NDS32_INSN_DPREF;
+ break;
+ case N32_MEM_LBS_BI:
+ insn_num = NDS32_INSN_LBS_BI;
+ break;
+ case N32_MEM_LHS_BI:
+ insn_num = NDS32_INSN_LHS_BI;
+ break;
+ case N32_MEM_LLW:
+ insn_num = NDS32_INSN_LLW;
+ break;
+ case N32_MEM_SCW:
+ insn_num = NDS32_INSN_SCW;
+ break;
+ case N32_MEM_LWUP:
+ insn_num = NDS32_INSN_LWUP;
+ break;
+ case N32_MEM_SWUP:
+ insn_num = NDS32_INSN_SWUP;
+ break;
+ default:
+ insn_num = 0;
+ break;
+ }
+ /* Trick. COLE: For what? */
+ fields->f_32_rb5 = N32_RB5 (insn);
+ break;
+ case N32_OP6_LSMW:
+ if ((insn & 0x3) == 0)
+ {
+ if ((insn & 0x20) == 0)
+ {
+ /* LMW */
+ switch ((insn >> 2) & 0x7)
+ {
+ case N32_LSMW_BI: /* bi */
+ insn_num = NDS32_INSN_LMW_BI;
+ break;
+ case N32_LSMW_BIM: /* bim */
+ insn_num = NDS32_INSN_LMW_BIM;
+ break;
+ case N32_LSMW_BD: /* bd */
+ insn_num = NDS32_INSN_LMW_BD;
+ break;
+ case N32_LSMW_BDM: /* bdm */
+ insn_num = NDS32_INSN_LMW_BDM;
+ break;
+ case N32_LSMW_AI: /* ai */
+ insn_num = NDS32_INSN_LMW_AI;
+ break;
+ case N32_LSMW_AIM: /* aim */
+ insn_num = NDS32_INSN_LMW_AIM;
+ break;
+ case N32_LSMW_AD: /* ad */
+ insn_num = NDS32_INSN_LMW_AD;
+ break;
+ case N32_LSMW_ADM: /* adm */
+ insn_num = NDS32_INSN_LMW_ADM;
+ break;
+ }
+ }
+ else
+ {
+ /* SMW */
+ switch ((insn >> 2) & 0x7)
+ {
+ case N32_LSMW_BI: /* bi */
+ insn_num = NDS32_INSN_SMW_BI;
+ break;
+ case N32_LSMW_BIM: /* bim */
+ insn_num = NDS32_INSN_SMW_BIM;
+ break;
+ case N32_LSMW_BD: /* bd */
+ insn_num = NDS32_INSN_SMW_BD;
+ break;
+ case N32_LSMW_BDM: /* bdm */
+ insn_num = NDS32_INSN_SMW_BDM;
+ break;
+ case N32_LSMW_AI: /* ai */
+ insn_num = NDS32_INSN_SMW_AI;
+ break;
+ case N32_LSMW_AIM: /* aim */
+ insn_num = NDS32_INSN_SMW_AIM;
+ break;
+ case N32_LSMW_AD: /* ad */
+ insn_num = NDS32_INSN_SMW_AD;
+ break;
+ case N32_LSMW_ADM: /* adm */
+ insn_num = NDS32_INSN_SMW_ADM;
+ break;
+ }
+ }
+ }
+ else if ((insn & 0x3) == 1)
+ {
+ if ((insn & 0x20) == 0)
+ {
+ /* LMWA */
+ switch ((insn >> 2) & 0x7)
+ {
+ case N32_LSMW_BI: /* bi */
+ insn_num = NDS32_INSN_LMWA_BI;
+ break;
+ case N32_LSMW_BIM: /* bim */
+ insn_num = NDS32_INSN_LMWA_BIM;
+ break;
+ case N32_LSMW_BD: /* bd */
+ insn_num = NDS32_INSN_LMWA_BD;
+ break;
+ case N32_LSMW_BDM: /* bdm */
+ insn_num = NDS32_INSN_LMWA_BDM;
+ break;
+ case N32_LSMW_AI: /* ai */
+ insn_num = NDS32_INSN_LMWA_AI;
+ break;
+ case N32_LSMW_AIM: /* aim */
+ insn_num = NDS32_INSN_LMWA_AIM;
+ break;
+ case N32_LSMW_AD: /* ad */
+ insn_num = NDS32_INSN_LMWA_AD;
+ break;
+ case N32_LSMW_ADM: /* adm */
+ insn_num = NDS32_INSN_LMWA_ADM;
+ break;
+ }
+ }
+ else
+ {
+ /* SMWA */
+ switch ((insn >> 2) & 0x7)
+ {
+ case N32_LSMW_BI: /* bi */
+ insn_num = NDS32_INSN_SMWA_BI;
+ break;
+ case N32_LSMW_BIM: /* bim */
+ insn_num = NDS32_INSN_SMWA_BIM;
+ break;
+ case N32_LSMW_BD: /* bd */
+ insn_num = NDS32_INSN_SMWA_BD;
+ break;
+ case N32_LSMW_BDM: /* bdm */
+ insn_num = NDS32_INSN_SMWA_BDM;
+ break;
+ case N32_LSMW_AI: /* ai */
+ insn_num = NDS32_INSN_SMWA_AI;
+ break;
+ case N32_LSMW_AIM: /* aim */
+ insn_num = NDS32_INSN_SMWA_AIM;
+ break;
+ case N32_LSMW_AD: /* ad */
+ insn_num = NDS32_INSN_SMWA_AD;
+ break;
+ case N32_LSMW_ADM: /* adm */
+ insn_num = NDS32_INSN_SMWA_ADM;
+ break;
+ }
+ }
+ }
+ else
+ insn_num = 0;
+ break;
+ case N32_OP6_JI:
+ insn_num = insn & 0x1000000 ? NDS32_INSN_JAL :
+ NDS32_INSN_J;
+ break;
+ case N32_OP6_JREG:
+ if ((insn & 0x1f) == 1)
+ {
+ /* JRAL, JRAL.ITON, JRAL.TON */
+ switch ((insn >> 8) & 0x3)
+ {
+ case 0:
+ insn_num = NDS32_INSN_JRAL;
+ break;
+ case 1:
+ insn_num = NDS32_INSN_JRAL_ITON;
+ break;
+ case 2:
+ insn_num = NDS32_INSN_JRAL_TON;
+ break;
+ default:
+ insn_num = 0;
+ break;
+ }
+ }
+ else if ((insn & 0x3f) == 0x20)
+ {
+ /* RET, RET.ITOFF, RET.TOFF */
+ switch ((insn >> 8) & 0x3)
+ {
+ case 0:
+ insn_num = NDS32_INSN_RET;
+ break;
+ case 1:
+ insn_num = NDS32_INSN_RET_ITOFF;
+ break;
+ case 2:
+ insn_num = NDS32_INSN_RET_TOFF;
+ break;
+ default:
+ insn_num = 0;
+ break;
+ }
+ fields->f_32_rb5 = N32_RB5 (insn);
+ }
+ else
+ insn_num = 0;
+ break;
+ case N32_OP6_BR1:
+ insn_num = (insn & 0x4000) ? NDS32_INSN_BNE :
+ NDS32_INSN_BEQ;
+ break;
+ case N32_OP6_BR2:
+ switch (((insn & 0xf0000) >> 16) & 0xf)
+ {
+ case N32_BR2_BEQZ:
+ insn_num = NDS32_INSN_BEQZ;
+ break;
+ case N32_BR2_BNEZ:
+ insn_num = NDS32_INSN_BNEZ;
+ break;
+ case N32_BR2_BGEZ:
+ insn_num = NDS32_INSN_BGEZ;
+ break;
+ case N32_BR2_BLTZ:
+ insn_num = NDS32_INSN_BLTZ;
+ break;
+ case N32_BR2_BGTZ:
+ insn_num = NDS32_INSN_BGTZ;
+ break;
+ case N32_BR2_BLEZ:
+ insn_num = NDS32_INSN_BLEZ;
+ break;
+ case N32_BR2_BGEZAL:
+ insn_num = NDS32_INSN_BGEZAL;
+ break;
+ case N32_BR2_BLTZAL:
+ insn_num = NDS32_INSN_BLTZAL;
+ break;
+ default:
+ insn_num = 0;
+ break;
+ }
+ break;
+ case N32_OP6_BR3:
+ if ((insn & 0x00080000) == 0x0)
+ insn_num = NDS32_INSN_BEQC;
+ else if ((insn & 0x00080000) == 0x00080000)
+ insn_num = NDS32_INSN_BNEC;
+ else
+ insn_num = 0;
+ break;
+ case N32_OP6_SUBRI:
+ insn_num = NDS32_INSN_SUBRI;
+ break;
+ case N32_OP6_ANDI:
+ insn_num = NDS32_INSN_ANDI;
+ break;
+ case N32_OP6_ALU1:
+ switch (insn & 0x1f)
+ {
+ case N32_ALU1_SRLI:
+ insn_num = NDS32_INSN_SRLI;
+ fields->f_32_rt5 = N32_RT5 (insn);
+ fields->f_32_ra5 = N32_RA5 (insn);
+ break;
+ default:
+ insn_num = 0;
+ break;
+ }
+ break;
+ case N32_OP6_ALU2:
+ switch (insn & 0x1f)
+ {
+ case N32_ALU2_BSE:
+ insn_num = NDS32_INSN_BSE;
+ break;
+ case N32_ALU2_BSP:
+ insn_num = NDS32_INSN_BSP;
+ break;
+ default:
+ insn_num = 0;
+ break;
+ }
+ break;
+ case N32_OP6_MISC:
+ switch (insn & 0x1f)
+ {
+ case N32_MISC_CCTL:
+ insn_num = NDS32_INSN_CCTL;
+ fields->f_32_rt5 = N32_RT5 (insn);
+ fields->f_32_ra5 = N32_RA5 (insn);
+ break;
+ case N32_MISC_TRAP:
+ insn_num = NDS32_INSN_TRAP;
+ break;
+ case N32_MISC_TEQZ:
+ insn_num = NDS32_INSN_TEQZ;
+ break;
+ case N32_MISC_TNEZ:
+ insn_num = NDS32_INSN_TNEZ;
+ break;
+ case N32_MISC_BREAK:
+ insn_num = NDS32_INSN_BREAK;
+ break;
+ case N32_MISC_MSYNC:
+ insn_num = NDS32_INSN_MSYNC;
+ break;
+ case N32_MISC_TLBOP:
+ insn_num = NDS32_INSN_TLBOP;
+ fields->f_32_rt5 = N32_RT5 (insn);
+ fields->f_32_ra5 = N32_RA5 (insn);
+ break;
+ default:
+ insn = 0;
+ break;
+ }
+ break;
+ case N32_OP6_COP:
+ if (((insn >> 4) & 3) == 0)
+ switch (insn & 0xf)
+ {
+ case N32_FPU_FLS:
+ if (insn & 0x80)
+ insn_num = NDS32_INSN_FLS_BI;
+ else
+ insn_num = NDS32_INSN_FLS;
+ break;
+ case N32_FPU_FSS:
+ if (insn & 0x80)
+ insn_num = NDS32_INSN_FSS_BI;
+ else
+ insn_num = NDS32_INSN_FSS;
+ break;
+ case N32_FPU_FLD:
+ if (insn & 0x80)
+ insn_num = NDS32_INSN_FLD_BI;
+ else
+ insn_num = NDS32_INSN_FLD;
+ break;
+ case N32_FPU_FSD:
+ if (insn & 0x80)
+ insn_num = NDS32_INSN_FSD_BI;
+ else
+ insn_num = NDS32_INSN_FSD;
+ break;
+ default:
+ insn_num = 0;
+ break;
+ }
+ else
+ insn_num = 0;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch ((insn & 0xf800) >> 11)
+ {
+ case 0x10:
+ /* 0x8000 */
+ if ((insn & 0x7ff) == 0x3ff)
+ {
+ /* FIXME: ?? */
+ insn_num = NDS32_INSN_IFRET;
+ }
+ break;
+ case 0x12:
+ /* 0x9000 */
+ if (((insn >> 9) & 3) == 1)
+ {
+ insn_num = NDS32_INSN_SRLI45;
+ fields->f_16_rt4 = ((insn & 0x01e0) >> 5) & 0xf;
+ }
+ else
+ insn_num = 0;
+ break;
+ case 0x14:
+ /* 0xa000 */
+ switch ((insn >> 9) & 3)
+ {
+ case 0x00:
+ insn_num = NDS32_INSN_LWI333;
+ fields->f_16_ra3 = ((insn & 0x0038) >> 3) & 0x7;
+ fields->f_16_rt3_7 = ((insn & 0x01c0) >> 6) & 0x7;
+ break;
+ case 0x02:
+ insn_num = NDS32_INSN_LHI333;
+ fields->f_16_ra3 = ((insn & 0x0038) >> 3) & 0x7;
+ fields->f_16_rt3_7 = ((insn & 0x01c0) >> 6) & 0x7;
+ break;
+ case 0x03:
+ insn_num = NDS32_INSN_LBI333;
+ fields->f_16_ra3 = ((insn & 0x0038) >> 3) & 0x7;
+ fields->f_16_rt3_7 = ((insn & 0x01c0) >> 6) & 0x7;
+ break;
+ default:
+ insn_num = 0;
+ break;
+ }
+ break;
+ case 0x15:
+ /* 0xa800 */
+ switch ((insn >> 9) & 3)
+ {
+ case 0x00:
+ insn_num = NDS32_INSN_SWI333;
+ fields->f_16_ra3 = ((insn & 0x0038) >> 3) & 0x7;
+ fields->f_16_rt3_7 = ((insn & 0x01c0) >> 6) & 0x7;
+ break;
+ case 0x02:
+ insn_num = NDS32_INSN_SHI333;
+ fields->f_16_ra3 = ((insn & 0x0038) >> 3) & 0x7;
+ fields->f_16_rt3_7 = ((insn & 0x01c0) >> 6) & 0x7;
+ break;
+ case 0x03:
+ insn_num = NDS32_INSN_SBI333;
+ fields->f_16_ra3 = ((insn & 0x0038) >> 3) & 0x7;
+ fields->f_16_rt3_7 = ((insn & 0x01c0) >> 6) & 0x7;
+ break;
+ default:
+ insn_num = 0;
+ break;
+ }
+ break;
+ case 0x17:
+ /* 0xb800 */
+ if (insn & 0x80)
+ insn_num = NDS32_INSN_LWI37;
+ else
+ insn_num = NDS32_INSN_SWI37;
+ fields->f_16_rt3 = ((insn & 0x0700) >> 8) & 0x7;
+ break;
+ case 0x18:
+ /* 0xc000 */
+ insn_num = NDS32_INSN_BEQZ38;
+ fields->f_16_rt3 = ((insn & 0x0700) >> 8) & 0x7;
+ break;
+ case 0x19:
+ /* 0xc800 */
+ insn_num = NDS32_INSN_BNEZ38;
+ fields->f_16_rt3 = ((insn & 0x0700) >> 8) & 0x7;
+ break;
+ case 0x1a:
+ /* 0xd000 */
+ if ((insn & 0x0700) != 0x0500)
+ {
+ insn_num = NDS32_INSN_BEQS38;
+ fields->f_16_rt3 = ((insn & 0x0700) >> 8) & 0x7;
+ }
+ else
+ {
+ insn_num = NDS32_INSN_J8;
+ }
+ break;
+ case 0x1b:
+ /* 0xd800 */
+ if ((insn & 0x0700) != 0x0500)
+ {
+ insn_num = NDS32_INSN_BNES38;
+ fields->f_16_rt3 = ((insn & 0x0700) >> 8) & 0x7;
+ }
+ else if ((insn & 0x00e0) == 0x20)
+ {
+ insn_num = NDS32_INSN_JRAL5;
+ fields->f_16_rb5h = insn & 0x1f;
+ }
+ else if ((insn & 0x00e0) == 0x80)
+ {
+ insn_num = NDS32_INSN_RET5;
+ fields->f_16_rb5h = insn & 0x1f;
+ }
+ else
+ {
+ insn_num = 0;
+ }
+ break;
+ case 0x1d:
+ /* 0xe800 */
+ if ((insn & 0x0700) == 0x0)
+ {
+ insn_num = NDS32_INSN_BEQZS8;
+ }
+ else if ((insn & 0x0700) == 0x100)
+ {
+ insn_num = NDS32_INSN_BNEZS8;
+ }
+ else
+ {
+ insn_num = 0;
+ }
+ break;
+ default:
+ insn_num = 0;
+ break;
+ }
+ }
+ *oi = gas_cgen_cpu_desc->insn_table.init_entries[insn_num].opinst;
+ return insn_num;
+}
+
+typedef struct seg_fixup seg_fixup;
+struct seg_fixup
+{
+ segT seg;
+ fixS *fix;
+ seg_fixup *next;
+};
+
+static seg_fixup *sf_list = NULL;
+
+static void
+set_prev_fix (fixS *fix_p)
+{
+ seg_fixup *sf_t = sf_list;
+
+ while (sf_t)
+ {
+ if (sf_t->seg == now_seg)
+ break;
+ sf_t = sf_t->next;
+ }
+ if (!sf_t)
+ {
+ sf_t = xmalloc (sizeof (seg_fixup));
+ sf_t->next = sf_list;
+ sf_list = sf_t;
+ }
+ sf_t->seg = now_seg;
+ sf_t->fix = fix_p;
+}
+
+static fixS *
+get_prev_fix (void)
+{
+ seg_fixup *sf_t = sf_list;
+
+ while (sf_t)
+ {
+ if (sf_t->seg == now_seg)
+ break;
+ sf_t = sf_t->next;
+ }
+ if (sf_t)
+ return sf_t->fix;
+ return NULL;
+}
+
+static void
+clean_seg_fixup_list (void)
+{
+ seg_fixup *sf_t;
+
+ while (sf_list)
+ {
+ sf_t = sf_list->next;
+ free (sf_list);
+ sf_list = sf_t;
+ }
+}
+
+/* This function is used to create new fixups with LABEL relocations and append
+ them to the newest fragment(may or may not be frag_now). */
+static void
+append_label_reloc (fragS *frag, int byte_sz_p)
+{
+ /* Optimize for space and label exists. */
+ fixS *fixP;
+ expressionS exp;
+ unsigned long where_t;
+
+ if (!enable_relax_relocs)
+ return;
+
+ /* Create and attach a BFD_RELOC_NDS32_LABEL fixup
+ the size of instrcution 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 = 0;
+ if (frag != frag_now)
+ where_t = frag->fr_fix - byte_sz_p;
+ else
+ where_t = frag_now_fix () - byte_sz_p;
+
+ fixP = fix_new_exp (frag, where_t, 0, &exp, 0, BFD_RELOC_NDS32_LABEL);
+ /* Chain the fixup together. */
+ fixP->tc_fix_data = (fixS*) get_prev_fix ();
+ fixP->fx_offset = label_align;
+ label_exist = 0;
+ label_align = 0;
+ set_prev_fix (fixP);
+}
+
+static int
+is_mfusr_pc (nds32_insn *insn)
+{
+ int insn_num;
+
+ if (CGEN_INSN_BITSIZE (insn->insn) == 32)
+ {
+ insn_num = CGEN_INSN_NUM (insn->insn);
+ if (insn_num != -1 && insn_num != NDS32_INSN_MFUSR)
+ return 0;
+
+ insn_num = INSN_VALUE (insn->buffer);
+ return ((insn_num & INSN_MFUSR_PC_MASK) == INSN_MFUSR_PC);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+static int last_fr_address = 0;
+
+static int
+check_instruction_availability (nds32_insn *insn, char *str)
+{
+ int insn_num = CGEN_INSN_NUM (insn->insn);
+
+ if (is_mfusr_pc (insn) && march_cpu_opt == ARCH_V1)
+ {
+ /* V2 always support this instruction and has no this flag. */
+ nds32_flags |= E_NDS32_HAS_MFUSR_PC_INST;
+ }
+ else if ((CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_A32V2)
+ || CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_A16V2))
+ && march_cpu_opt == ARCH_V1)
+ {
+ /* V2 instruction used in V1 architecture. */
+ as_bad (_("instruction '%s' not supported in V1 architecture"),
rem__r (str));
+ return 0;
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_NOT_V3M)
+ && march_cpu_opt == ARCH_V3M)
+ {
+ as_bad (_("instruction '%s' not supported in V3M architecture"),
+ rem__r (str));
+ return 0;
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_V3)
+ && (march_cpu_opt == ARCH_V1 || march_cpu_opt == ARCH_V2))
+ {
+ /* V3 instruction used in V1/V2 architecture
+ IFRET16 uses encoding of MOV55, so MOV55 is considered as a V3 instruction.
+ To correctly assemble mov55, an exception is created for mov55. */
+ if (insn_num != NDS32_INSN_MOV55)
+ {
+ as_bad (_("V3 instruction '%s' not supported in (V1|V2) architecture"),
+ rem__r (str));
+ return 0;
+ }
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_EXT))
+ {
+ /* Make sure that it is for C/C++ performance extension. */
+ if (enable_c_extension)
+ {
+ nds32_flags |= E_NDS32_HAS_EXT_INST;
+ }
+ else
+ {
+ as_bad (_("instruction '%s' requires enabling performance extension"),
+ rem__r (str));
+ return 0;
+ }
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_EXT2))
+ {
+ /* Make sure that it is for C/C++ performance extension. */
+ if (enable_c_extension2)
+ {
+ nds32_flags |= E_NDS32_HAS_EXT2_INST;
+ }
+ else
+ {
+ as_bad (_("instruction '%s' requires enabling performance extension II"),
+ rem__r (str));
+ return 0;
+ }
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_AUDIO))
+ {
+ /* Make sure that it is for C/C++ performance extension. */
+ if (enable_audio_extension)
+ {
+ nds32_flags |= E_NDS32_HAS_AUDIO_INST;
+ }
+ else
+ {
+ as_bad (_("instruction '%s' requires enabling AUDIO extension"),
+ rem__r (str));
+ return 0;
+ }
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_STRING))
+ {
+ /* Make sure that it is for C/C++ performance extension. */
+ if (enable_string_extension)
+ {
+ nds32_flags |= E_NDS32_HAS_STRING_INST;
+ }
+ else
+ {
+ as_bad (_("instruction '%s' requires enabling STRING extension"),
+ rem__r (str));
+ return 0;
+ }
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_DIV))
+ {
+ /* Make sure that it is for DIV extension. */
+ if (enable_div_extension)
+ {
+ if (march_cpu_opt == ARCH_V1)
+ {
+ nds32_flags |= E_NDS32_HAS_DIV_INST;
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_DX_REG))
+ {
+ if (enable_dx_regs_extension)
+ {
+ nds32_flags |= E_NDS32_HAS_DIV_DX_INST;
+ }
+ else
+ {
+ as_bad (_("instruction '%s' requires enabling DX_REGS extension"),
+ rem__r (str));
+ return 0;
+ }
+ }
+ }
+ else
+ {
+ as_bad (_("instruction '%s' requires enabling DIV extension"),
+ rem__r (str));
+ return 0;
+ }
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_FPU_COM))
+ {
+ /* Make sure that it is for FPU extension. */
+ if (enable_fpu_sp_extension || enable_fpu_dp_extension)
+ {
+ if (!(nds32_flags & (E_NDS32_HAS_FPU_INST | E_NDS32_HAS_FPU_DP_INST)))
+ {
+ /* Preserve information to decide which FPU bit will be enabled
+ in elf_nds32_final_processing. */
+ nds32_fpu_com = 1;
+ }
+ }
+ else
+ {
+ as_bad (_("instruction '%s' requires enabling FPU extension"),
+ rem__r (str));
+ return 0;
+ }
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_FPU_SP))
+ {
+ /* Make sure that it is for FPU extension. */
+ if (enable_fpu_sp_extension)
+ {
+ nds32_flags |= E_NDS32_HAS_FPU_INST;
+ }
+ else
+ {
+ as_bad (_("instruction '%s' requires enabling FPU_SP extension"),
+ rem__r (str));
+ return 0;
+ }
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_FPU_SP_MAC))
+ {
+ /* Make sure that it is for FPU extension. */
+ if (enable_fpu_sp_extension && enable_fpu_mac_extension)
+ {
+ nds32_flags |= E_NDS32_HAS_FPU_MAC_INST;
+ nds32_flags |= E_NDS32_HAS_FPU_INST;
+ }
+ else
+ {
+ as_bad (_("instruction '%s' requires enabling FPU_SP and FPU_MAC
extension"),
+ rem__r (str));
+ return 0;
+ }
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_FPU_DP))
+ {
+ /* Make sure that it is for FPU extension. */
+ if (enable_fpu_dp_extension)
+ {
+ nds32_flags |= E_NDS32_HAS_FPU_DP_INST;
+ }
+ else
+ {
+ as_bad (_("instruction '%s' requires enabling FPU_DP extension"),
+ rem__r (str));
+ return 0;
+ }
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_FPU_DP_MAC))
+ {
+ /* Make sure that it is for FPU extension. */
+ if (enable_fpu_dp_extension && enable_fpu_mac_extension)
+ {
+ nds32_flags |= E_NDS32_HAS_FPU_MAC_INST;
+ nds32_flags |= E_NDS32_HAS_FPU_DP_INST;
+ }
+ else
+ {
+ as_bad (_("instruction '%s' requires enabling FPU_DP and FPU_MAC
extension"),
+ rem__r (str));
+ return 0;
+ }
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_FPU_BOTH))
+ {
+ /* Make sure that it is for FPU extension. */
+ if (enable_fpu_dp_extension && enable_fpu_sp_extension)
+ {
+ nds32_flags |= E_NDS32_HAS_FPU_DP_INST;
+ nds32_flags |= E_NDS32_HAS_FPU_INST;
+ }
+ else
+ {
+ as_bad (_("instruction '%s' requires enabling both FPU_DP and
FPU_SP extension"),
+ rem__r (str));
+ return 0;
+ }
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_MAC))
+ {
+ /* Make sure that it is for MAC extension. */
+ if (enable_mac_extension)
+ {
+ if (march_cpu_opt == ARCH_V1)
+ {
+ nds32_flags &= ~E_NDS32_HAS_NO_MAC_INST;
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_DX_REG))
+ {
+ if (enable_dx_regs_extension)
+ {
+ nds32_flags |= E_NDS32_HAS_MAC_DX_INST;
+ }
+ else
+ {
+ as_bad (_("instruction '%s' requires enabling DX_REGS extension"),
+ rem__r (str));
+ return 0;
+ }
+ }
+ }
+ else if (march_cpu_opt == ARCH_V1
+ || CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_DX_REG))
+ {
+ as_bad (_("instruction '%s' requires enabling MAC extension"),
rem__r (str));
+ return 0;
+ }
+ }
+ else if (march_cpu_opt != ARCH_V1
+ && CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_DX_REG))
+ {
+ /* For DX_REG set but not for MAC, DIV, AUDIO. */
+ /* For example, mfusr, mtusr. */
+
+ insn_num = CGEN_INSN_NUM (insn->insn);
+ if ((insn_num == NDS32_INSN_MFUSR
+ || insn_num == NDS32_INSN_MTUSR)
+ && CGEN_FIELDS_GROUPIDX (insn->fields) == 0
+ && CGEN_FIELDS_USRIDX (insn->fields) < 4
+ && CGEN_FIELDS_USRIDX (insn->fields) >= 0)
+ {
+ if (enable_dx_regs_extension)
+ nds32_flags |= E_NDS32_HAS_MAC_DX_INST | E_NDS32_HAS_DIV_DX_INST;
+ else
+ {
+ as_bad (_("instruction '%s' requires enabling DX_REGS extension"),
+ rem__r (str));
+ return 0;
+ }
+ }
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_IFCEXT))
+ {
+ nds32_flags |= E_NDS32_HAS_IFC_INST;
+ }
+ else if (CGEN_INSN_ATTR_VALUE (insn->insn, CGEN_INSN_STAT))
+ {
+ nds32_flags |= E_NDS32_HAS_SATURATION_INST;
+ }
+ return 1;
+}
+
+static int
+get_instruction_attribute (nds32_insn *insn, int insn_num,
+ relax_substateT *subtype)
+{
+ int branch = 0, ind_branch = 0;
+
+ switch (insn_num)
+ {
+ case NDS32_INSN_LMW_BI:
+ case NDS32_INSN_LMW_BIM:
+ case NDS32_INSN_LMW_BD:
+ case NDS32_INSN_LMW_BDM:
+ case NDS32_INSN_LMW_AI:
+ case NDS32_INSN_LMW_AIM:
+ case NDS32_INSN_LMW_AD:
+ case NDS32_INSN_LMW_ADM:
+ case NDS32_INSN_SMW_BI:
+ case NDS32_INSN_SMW_BIM:
+ case NDS32_INSN_SMW_BD:
+ case NDS32_INSN_SMW_BDM:
+ case NDS32_INSN_SMW_AI:
+ case NDS32_INSN_SMW_AIM:
+ case NDS32_INSN_SMW_AD:
+ case NDS32_INSN_SMW_ADM:
+ /* Make sure range register pair is in correct order. */
+ if (CGEN_FIELDS_RT5 (insn->fields) > CGEN_FIELDS_RB5 (insn->fields))
+ {
+ as_bad (_("LMW/SMW register range out of order"));
+ return 0;
+ }
+
+ if (enable_reduce_regs)
+ {
+ /* FIXME: This expression is so ugly. */
+ if ((CGEN_FIELDS_RT5 (insn->fields) > REG_R10
+ || CGEN_FIELDS_RB5 (insn->fields) > REG_R10)
+ && ((CGEN_FIELDS_RT5 (insn->fields) != REG_R15
+ || CGEN_FIELDS_RB5 (insn->fields) != REG_R15)
+ && (CGEN_FIELDS_RT5 (insn->fields) != REG_SP
+ || CGEN_FIELDS_RB5 (insn->fields) != REG_SP)))
+ {
+ as_bad (_("LMW/SMW register range invalid"));
+ return 0;
+ }
+ }
+
+ break;
+
+ case NDS32_INSN_J:
+ branch = 1;
+ subtype[0] |= RELAX_BRANCH_MASK | SET_BR_CODE (BR_UCOND_BR_S16M);
+ break;
+
+ case NDS32_INSN_JAL:
+ branch = 1;
+ subtype[0] |= RELAX_BRANCH_MASK | SET_BR_CODE (BR_UCOND_CALL_S16M);
+ break;
+
+ case NDS32_INSN_J8:
+ branch = 1;
+ subtype[0] |= RELAX_BRANCH_MASK | SET_BR_CODE (BR_UCOND_BR_S256);
+ break;
+
+ case NDS32_INSN_BEQZ:
+ case NDS32_INSN_BNEZ:
+ case NDS32_INSN_BGEZ:
+ case NDS32_INSN_BLTZ:
+ case NDS32_INSN_BGTZ:
+ case NDS32_INSN_BLEZ:
+ branch = 1;
+ subtype[0] |= (CGEN_FIELDS_RT5 (insn->fields) << 27) | RELAX_BRANCH_MASK
+ | SET_BR_CODE (BR_COND_BR_S64K);
+ break;
+
+ case NDS32_INSN_BGEZAL:
+ case NDS32_INSN_BLTZAL:
+ branch = 1;
+ subtype[0] |= (CGEN_FIELDS_RT5 (insn->fields) << 27) | RELAX_BRANCH_MASK
+ | SET_BR_CODE (BR_COND_CALL_S64K);
+ break;
+
+ case NDS32_INSN_BEQ:
+ case NDS32_INSN_BNE:
+ branch = 1;
+ subtype[0] |= (CGEN_FIELDS_RT5 (insn->fields) << 27)
+ | (CGEN_FIELDS_RA5 (insn->fields) << 22) | RELAX_BRANCH_MASK
+ | SET_BR_CODE (BR_COND_BR_S16K);
+ break;
+
+ case NDS32_INSN_BEQC:
+ case NDS32_INSN_BNEC:
+ branch = 1;
+ subtype[0] |= (CGEN_FIELDS_RT5 (insn->fields) << 27)
+ | RELAX_BRANCH_MASK | SET_BR_CODE (BR_COND_BR_S256);
+ subtype[1] |= (CGEN_FIELDS_SIMM11 (insn->fields) << 21);
+ break;
+
+ case NDS32_INSN_JRAL:
+ case NDS32_INSN_JRAL_ITON:
+ case NDS32_INSN_JRAL_TON:
+ ind_branch = 1;
+ CGEN_FIELDS_RT5 (insn->fields) = (INSN_VALUE (insn->buffer) >>
20) & 0x1f;
+ break;
+
+ case NDS32_INSN_JRAL5:
+ case NDS32_INSN_JR5:
+ case NDS32_INSN_JR:
+ case NDS32_INSN_JR_ITOFF:
+ case NDS32_INSN_JR_TOFF:
+ case NDS32_INSN_RET:
+ case NDS32_INSN_RET_ITOFF:
+ case NDS32_INSN_RET_TOFF:
+ case NDS32_INSN_IRET:
+ case NDS32_INSN_RET5:
+ case NDS32_INSN_IFRET:
+ ind_branch = 1;
+ break;
+
+ case NDS32_INSN_BEQZ38:
+ case NDS32_INSN_BNEZ38:
+ branch = 1;
+ subtype[0] |= (CGEN_FIELDS_RT3 (insn->fields) << 27)
+ | RELAX_BRANCH_MASK | SET_BR_CODE (BR_COND_BR_S256);
+ break;
+
+ case NDS32_INSN_BEQS38:
+ case NDS32_INSN_BNES38:
+ branch = 1;
+ subtype[0] |= (CGEN_FIELDS_RT3 (insn->fields) << 27)
+ | (REG_R5 << 22) | RELAX_BRANCH_MASK
+ | SET_BR_CODE (BR_COND_BR_S256);
+ break;
+
+ case NDS32_INSN_BEQZS8:
+ case NDS32_INSN_BNEZS8:
+ branch = 1;
+ subtype[0] |= (REG_TA << 27) | RELAX_BRANCH_MASK
+ | SET_BR_CODE (BR_COND_BR_S256);
+ break;
+ case NDS32_INSN_IFCALL:
+ case NDS32_INSN_IFCALL9:
+ branch = 1;
+ break;
+ }
+
+ /* Do not use 16 bit instruction in frag. */
+ if (disable_16bit)
+ subtype[0] |= RELAX_FORCE_NO_16BIT_MASK;
+
+ return (ind_branch << 1) | branch;
+}
+
+/* GAS will call this function for each input line which does not contain a
+ pseudo-op. The argument is a null terminated string. The function should
+ assemble the string as an instruction with operands. Normally md_assemble
+ will do this by calling frag_more and writing out some bytes (Frags).
+ md_assemble will call fix_new to create fixups as needed (Fixups). Targets
+ which need to do special purpose relaxation will call frag_var.
+ Depending on the optimization flag a convertible instruction my be converted
+ from 16-bit to 32-bit or vice versa.
+
+ -Os will try to convert all 32-bit instructions to 16-bit.
+ -O will try to align labels, visible or hidden, on boundary and convert as
+ many 32-bit instructions as possible to 16-bit. Occasionally, a
+ convertible 16-bit instruction may be converted to 32-bit in order to
+ align a label No conversion will happen if no optimization
flag turned on. */
+
+/* This variable is used to record whether previous instruction
+ is a branch or not. If so, the current instruction should be
+ expanded in 32-bit form in order to help branch prediction.
+
+ `b2bb_prev' is set in md_assemble () depending on the current
+ instruction. And it is reset in nds32_elf_section_change_hook (). */
+
+void
+md_assemble (char *str)
+{
+ nds32_insn insn;
+ char *errmsg;
+ relax_substateT subtype[NDS32_FRAG_FLAG_NUM];
+ int branch;
+ int ind_branch;
+ int insn_num;
+ int saved_endian;
+ int relaxable = 0;
+ fragS *fragP;
+ const CGEN_OPINST *oi = NULL;
+ nds32_insn_instant *insn_ins;
+ struct nds32_pseudo_opcode *opcode;
+
+ opcode = nds32_lookup_pseudo_opcode (str);
+ /* Note that we need to check 'compiler_generated_asm' 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 (opcode
+ && !(compiler_generated_asm && opcode->physical_op))
+ {
+ nds32_pseudo_opcode_wrapper (str, opcode);
+ return;
+ }
+
+ if (enable_reduce_regs && reg_out_of_range (str))
+ {
+ as_bad (_("instruction '%s' use invalid register not in "
+ "Reduced Register Set"), rem__r (str));
+ }
+
+ /* COLE: I think the programmer should take care of this.
+ It's not assembler's resposibility. Remove it someday.
+ Saddly, VLSI team need this now. */
+ nds32_adjust_label (1);
+
+ /* Initialize GAS's cgen interface for a new instruction. */
+ gas_cgen_init_parse ();
+
+ insn.insn = nds32_cgen_assemble_insn (gas_cgen_cpu_desc, str, &insn.fields,
+ insn.buffer, &errmsg);
+ if (!insn.insn)
+ {
+ as_bad (_("%s"), errmsg);
+ return;
+ }
+ insn_num = CGEN_INSN_NUM (insn.insn);
+ check_forbid_insn (CGEN_INSN_MNEMONIC (insn.insn), insn_num);
+ if (!check_instruction_availability (&insn, str))
+ return;
+
+ subtype[0] = subtype[1] = 0;
+ insn_num = convert_macro_insn_to_insn (&insn, &oi);
+
+ branch = get_instruction_attribute (&insn, insn_num, subtype);
+ ind_branch = (branch >> 1) & 1;
+ branch = branch & 1;
+
+ if (opt_b2bb && b2bb_prev && (branch || ind_branch))
+ subtype[0] |= RELAX_FORCE_NO_16BIT_MASK;
+ b2bb_prev = (branch || ind_branch);
+
+ {
+ finished_insnS fi;
+ uint16_t insn16 = 0;
+ int byte_sz = CGEN_INSN_BITSIZE (insn.insn) / 8;
+ int org_byte_sz = byte_sz;
+ int i;
+
+ memset (fi.fixups, 0, sizeof (fixS *) * GAS_CGEN_MAX_FIXUPS);
+ fi.num_fixups = 0;
+
+ /* Doesn't really matter what we pass for RELAX_P here.
+ This frag may have to be closed if it ends up with a branch instruction.
+ Make sure the variable part and fixed part are in same fragment. */
+ frag_grow (byte_sz * 2);
+ fragP = frag_now;
+
+ /* COLE: Emit debug line before emit instruction;
+ otherwise, relax will mass it around.
+ .loc 1 1 0
+ .loc 1 2 0
+ beqz $r0, .L0
+
+ In this case, when dwarf2_emit_line is called, by gas_cgen_finish_insn,
+ for 'beqz', the symbol for line is tag to a new frag with negative offset
+ pointed to the begining of 'beqz'. If 'beqz' is relaxed to 'beqz38',
+ the offset will point to two bytes ahead of 'beqz38' and cause negative
+ advance pc.
+
+ So why not just emit the line first? Hereby, the positive fixed offset
+ can be used. */
+
+ dwarf2_emit_insn (0);
+ if (byte_sz == 4)
+ gas_cgen_finish_insn (insn.insn, insn.buffer,
+ CGEN_FIELDS_BITSIZE (&insn.fields), 1, &fi);
+ else
+ {
+ insn.orig_insn = insn.insn;
+ gas_cgen_finish_insn (insn.orig_insn, insn.buffer,
+ CGEN_FIELDS_BITSIZE (&insn.fields),
+ 1 /* relax_p */, &fi);
+ }
+
+ /* Comment for 16-bit: A 16-bit instruction may have to be expanded to
+ 32-bit and then contract to 16-bit again, so force to close this frag
+ with 2 extra bytes to expand; remember this frag, so we can use it
+ when we encounter a label later.
+
+ If this is a branch, don't use it because it may never be contracted
+ to 16-bit again once it expands. */
+ if (fragP != frag_now)
+ {
+ /* Comment for 16-bit:
+ This is a branch, it may expand to reach the target, so we can't
+ treat it as normal 16-bit instruction. */
+ if (branch)
+ {
+ /* Set tc_frag_data.fixup to NULL or address of LABEL relocation
fixup. */
+ while (fragP->fr_next != frag_now)
+ {
+ /* Extra frag is created. */
+ fragP->tc_frag_data.fixup = NULL;
+ fragP = fragP->fr_next;
+ if ((prev_fixP = get_prev_fix ()))
+ prev_fixP->fx_where = 0;
+ }
+
+ if (optimize && label_exist)
+ append_label_reloc (fi.frag, byte_sz);
+
+ fragP->tc_frag_data.fixup = get_prev_fix ();
+ set_prev_fix (NULL);
+ RELAX_SET_FRAG_TC_FLAGS (fragP, subtype);
+ RELAX_SET_FRAG_TC_OPCNUM (fragP, insn_num);
+ for (i = 0; i < fi.num_fixups; i++)
+ if (fi.fixups[i] == NULL)
+ {
+ relaxable = 1;
+ break;
+ }
+ }
+ else
+ {
+ /* Frag is closed due to no more space to expand. */
+ fragP = frag_now;
+ }
+ }
+ else if (branch)
+ {
+ if (byte_sz == 4)
+ {
+ /* Not all branches has RELAXABLE attribute, this area is
+ used to process this kind of branches.
+ Ex. J, Jal; */
+ if (fi.num_fixups)
+ {
+ if ((fi.fixups[0]->fx_r_type - BFD_RELOC_UNUSED) == NDS32_OPERAND_CONCAT24)
+ fi.fixups[0]->fx_r_type = BFD_RELOC_NDS32_25_ABS;
+
+ else if ((fi.fixups[0]->fx_r_type - BFD_RELOC_UNUSED) ==
NDS32_OPERAND_DISP24)
+ {
+ fi.fixups[0]->fx_r_type = BFD_RELOC_NDS32_25_PCREL;
+ fi.fixups[0]->fx_pcrel = 0;
+
+ if (!disable_16bit
+ && (optimize || optimize_for_space || optimize_for_space_no_align)
+ && (subtype[0] & RELAX_FORCE_NO_16BIT_MASK) == 0
+ && insn_num == NDS32_INSN_J
+ && fi.fixups[0]->fx_cgen.opinfo != BFD_RELOC_NDS32_25_ABS)
+ {
+ expressionS exp_r;
+ exp_r.X_op = O_symbol;
+ exp_r.X_add_symbol = abs_section_sym;
+ exp_r.X_add_number = 0;
+ fix_new_exp (fi.fixups[0]->fx_frag,
+ fi.fixups[0]->fx_where, 4, &exp_r, 0,
+ BFD_RELOC_NDS32_INSN16);
+ }
+ }
+ else if ((fi.fixups[0]->fx_r_type - BFD_RELOC_UNUSED) ==
NDS32_OPERAND_DISP16)
+ {
+ if (org_byte_sz == 4 && insn_num == NDS32_INSN_IFCALL)
+ {
+ fi.fixups[0]->fx_r_type = BFD_RELOC_NDS32_17IFC_PCREL;
+ fi.fixups[0]->fx_pcrel = 0;
+ insn_ins = xmalloc (sizeof (nds32_insn_instant));
+ assign_instant (insn_ins, insn_num, &fi, org_byte_sz, byte_sz, &insn,
+ relaxable, oi);
+ insert_relax_type (insn_ins, BFD_RELOC_NDS32_INSN16,
+ 4, 0, NULL);
+ }
+ else
+ {
+ fi.fixups[0]->fx_r_type = BFD_RELOC_NDS32_17_PCREL;
+ fi.fixups[0]->fx_pcrel = 0;
+ }
+ }
+ else if ((fi.fixups[0]->fx_r_type - BFD_RELOC_UNUSED) ==
NDS32_OPERAND_DISP14)
+ {
+ fi.fixups[0]->fx_r_type = BFD_RELOC_NDS32_15_PCREL;
+ fi.fixups[0]->fx_pcrel = 0;
+ }
+ else if ((fi.fixups[0]->fx_r_type - BFD_RELOC_UNUSED) == NDS32_OPERAND_DISP8)
+ {
+ fi.fixups[0]->fx_r_type = BFD_RELOC_NDS32_WORD_9_PCREL;
+ fi.fixups[0]->fx_pcrel = 0;
+ }
+ }
+ else
+ as_warn (_("Branch instruction '%s' with absolute offset is
dangerous."),
+ rem__r (str));
+ }
+ else /* byte_sz == 2 */
+ {
+
+ /* Not all branches has RELAXABLE attribute, this area is
+ used to process this kind of branches.
+ Ex. J, Jal; */
+
+ /* Turn off fx_pcrel to prevent r_addend set as pc-relative offset
+ but set as a section-relative offset
+ Set fx_no_overflow to bypass the check of large symbol value of
+ short instruction. (e.g., 0x10000 for 16-bit instruction.) */
+ if (fi.num_fixups)
+ {
+ if ((fi.fixups[0]->fx_r_type - BFD_RELOC_UNUSED) == NDS32_OPERAND_HSDISP8)
+ {
+ fi.fixups[0]->fx_r_type = BFD_RELOC_NDS32_9_PCREL;
+ fi.fixups[0]->fx_pcrel = 0;
+ fi.fixups[0]->fx_no_overflow = 1;
+ }
+ else if ((fi.fixups[0]->fx_r_type - BFD_RELOC_UNUSED) == NDS32_OPERAND_DISP9)
+ {
+ fi.fixups[0]->fx_r_type = BFD_RELOC_NDS32_10_UPCREL;
+ fi.fixups[0]->fx_pcrel = 0;
+ fi.fixups[0]->fx_no_overflow = 1;
+ }
+ }
+ else
+ as_warn (_("Branch instruction '%s' with absolute offset is
dangerous."),
+ rem__r (str));
+
+ /* br && no RELAXABLE attribute.
+ Complete frag for this instruction may expand to 4-byte
+ maybe this aprt can be commented out? */
+ frag_var (rs_machine_dependent, 0, 0,
+ 0 /*subtype */, NULL, 0, NULL);
+ if (optimize && label_exist)
+ append_label_reloc (fi.frag, 2);
+ fragP->tc_frag_data.fixup = get_prev_fix ();
+ set_prev_fix (NULL);
+ }
+ }
+ else
+ {
+ if (byte_sz == 2)
+ {
+ if (insn_num == NDS32_INSN_LWI45_FE)
+ {
+ for (i = 0; i < fi.num_fixups; i++)
+ {
+ if (fi.fixups[i] == NULL)
+ break;
+ else if (fi.fixups[i]->fx_cgen.opinfo == BFD_RELOC_NDS32_MINUEND)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* The fragP->last_fr_address will be used when estimating the size of
+ fragments. It is just used to stored the index of the
fragment here. */
+ if (fragP->last_fr_address == 0)
+ {
+ fragP->last_fr_address = last_fr_address;
+ last_fr_address++;
+ }
+
+ if (byte_sz == 4)
+ {
+ if (!fi.num_fixups && !disable_16bit
+ && (optimize || optimize_for_space || optimize_for_space_no_align)
+ && convert_32_to_16 (fragP, &insn16, &subtype[0], &insn_num))
+ {
+ uint32_t old_insn =
+ bfd_getb32 (fragP->fr_literal + frag_now_fix_octets () - 4);
+
+ /* We have no way to know whether a 32-bit insn is convertible
+ or not if it has fixup (ie symbol)
+ optimize is on, so make a frag to contracts convertible insn. */
+
+ frag_var (rs_machine_dependent, 0, 0, 0 /* subtype[0] */,
+ NULL, 0, NULL);
+ RELAX_SET_FRAG_TC_FLAGS (fragP, subtype);
+ RELAX_SET_FRAG_TC_OPCNUM (fragP, insn_num);
+
+ /* Modify to 16-bit, instruction is always big endian. */
+ saved_endian = target_big_endian;
+ target_big_endian = 1;
+ md_number_to_chars (fragP->fr_literal + fragP->fr_fix - 4, insn16, 2);
+ target_big_endian = saved_endian;
+ fragP->fr_fix -= 2;
+
+ /* Always mark it as a 16-bit instruction. */
+ /* No INSN16 relocation is needed. */
+ if (optimize && !branch)
+ {
+ /* For 32-to-16 bit branches,
+ the label relocs have already stored. */
+ if (optimize && label_exist)
+ append_label_reloc (fi.frag, 2);
+
+ fragP->tc_frag_data.fixup = get_prev_fix ();
+ set_prev_fix (NULL);
+
+ /* Mark as relaxable in order to align label. */
+ RELAX_SET_RELAXABLE (fragP);
+ RELAX_SET_INSN (fragP, old_insn);
+ }
+ }
+ }
+ else /* byte_sz == 2 */
+ {
+ if (!branch && optimize)
+ {
+ uint32_t insn32;
+ uint16_t tmp16;
+
+ tmp16 = bfd_getb16 (frag_now->fr_literal + frag_now_fix () - 2);
+ if (nds32_convert_16_to_32 (stdoutput, tmp16, &insn32))
+ {
+ /* Not a br. This frag needs to be expanded. */
+ frag_grow (2);
+ frag_var (rs_machine_dependent, 2, 0,
+ 0 /* subtype[0] */, NULL, 0, NULL);
+ RELAX_SET_FRAG_TC_FLAGS (fragP, subtype);
+ RELAX_SET_FRAG_TC_OPCNUM (fragP, insn_num);
+
+ /* Mark as relaxable in order to align label. */
+ RELAX_SET_RELAXABLE (fragP);
+ }
+
+ if (optimize && label_exist)
+ append_label_reloc (fi.frag, 2);
+
+ fragP->tc_frag_data.fixup = get_prev_fix ();
+ set_prev_fix (NULL);
+ }
+ }
+
+ /* Create an instruction node and insert it into a proper basic block. */
+ insn_ins = xmalloc (sizeof (nds32_insn_instant));
+ if (insn16 != 0)
+ byte_sz = 2;
+ assign_instant (insn_ins, insn_num, &fi, org_byte_sz, byte_sz, &insn,
+ relaxable, oi);
+ if (bb_now->insn_head == NULL)
+ bb_now->insn_head = insn_ins;
+ bb_now->insn_tail = insn_ins;
+
+ /* Build a list to connect all sethi instructions. */
+ if (org_byte_sz == 4 && insn_num == NDS32_INSN_SETHI)
+ {
+ for (i = 0; i < insn_ins->num_fixups; i++)
+ {
+ if (insn_ins->fixups[i]->fx_cgen.opinfo == BFD_RELOC_NDS32_HI20
+ || insn_ins->fixups[i]->fx_cgen.opinfo == BFD_RELOC_NDS32_GOT_HI20
+ || insn_ins->fixups[i]->fx_cgen.opinfo == BFD_RELOC_NDS32_GOTOFF_HI20
+ || insn_ins->fixups[i]->fx_cgen.opinfo == BFD_RELOC_NDS32_PLTREL_HI20
+ || insn_ins->fixups[i]->fx_cgen.opinfo == BFD_RELOC_NDS32_PLT_GOTREL_HI20)
+ {
+ push_insn_to_list (&nds32_hi_insn_list, insn_ins);
+ break;
+ }
+ }
+ }
+
+ /* Create a new basic block if current instruction is a branch. */
+ if (branch || ind_branch)
+ create_new_basic_block ();
+
+ if (optimize && label_exist)
+ append_label_reloc (fi.frag, byte_sz);
+
+ }
+
+ /* If current instruction is JAL, JRAL, BGEZAL, BLTZAL or JRAL5
+ the next instruction must be treated as having a label when optimize for
+ speed. A label relocation must be added and alignment must be
enforced. */
+ if (optimize)
+ {
+ switch (insn_num)
+ {
+ case NDS32_INSN_JAL:
+ case NDS32_INSN_JRAL:
+ case NDS32_INSN_BGEZAL:
+ case NDS32_INSN_BLTZAL:
+ case NDS32_INSN_JRAL5:
+ label_exist = 1;
+ break;
+ }
+ }
+}
+
+/* md_macro_start */
+
+void
+nds32_macro_start (void)
+{
+ /* Maximum number of levels. */
+ if (macro_level >= MAX_MACRO_LEVEL - 1)
+ as_fatal (_("macros nested too deeply, only allows 32 nesting."));
+
+ ++macro_level;
+ builtin_hash[macro_level] = hash_new ();
+ local_label_hash[macro_level] = hash_new ();
+}
+
+/* md_macro_info */
+
+void
+nds32_macro_info (void *info)
+{
+ formal_entry *entry;
+ macro_entry *macro = (macro_entry *) info;
+
+ /* Put the formal arguments into the substitution symbol table. */
+ for (entry = macro->formals; entry; entry = entry->next)
+ {
+ char *name = strncpy (xmalloc (entry->name.len + 1),
+ entry->name.ptr, entry->name.len);
+ char *value = strncpy (xmalloc (entry->actual.len + 1),
+ entry->actual.ptr, entry->actual.len);
+
+ name[entry->name.len] = '\0';
+ value[entry->actual.len] = '\0';
+ hash_insert (builtin_hash[macro_level], name, value);
+ }
+}
+
+/* md_macro_end */
+
+void
+nds32_macro_end (void)
+{
+ hash_die (builtin_hash[macro_level]);
+ builtin_hash[macro_level] = NULL;
+ hash_die (local_label_hash[macro_level]);
+ local_label_hash[macro_level] = NULL;
+ --macro_level;
+}
+
+/* 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_get_section_alignment (stdoutput, segment);
+
+ return ((size + (1 << align) - 1) & (-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;
+}
+
+/* Can a branch instruction be converted to 16-bit? */
+
+static int
+is_convertible (fragS *fragP)
+{
+ switch (RELAX_OPC_NUM (fragP))
+ {
+ case NDS32_INSN_JRAL:
+ case NDS32_INSN_JRAL5:
+ case NDS32_INSN_J8:
+ case NDS32_INSN_JR:
+ case NDS32_INSN_JR5:
+ case NDS32_INSN_RET:
+ case NDS32_INSN_RET5:
+ case NDS32_INSN_BEQS38:
+ case NDS32_INSN_BNES38:
+ case NDS32_INSN_BEQZ38:
+ case NDS32_INSN_BNEZ38:
+ case NDS32_INSN_BEQZS8:
+ case NDS32_INSN_BNEZS8:
+ return 1;
+ break;
+
+ case NDS32_INSN_BEQ:
+ case NDS32_INSN_BNE:
+ if ((RELAX_RT (fragP) < REG_R8 && RELAX_RA (fragP) == REG_R5
+ && RELAX_RT (fragP) != REG_R5)
+ || (RELAX_RA (fragP) < REG_R8
+ && RELAX_RT (fragP) == REG_R5
+ && RELAX_RA (fragP) != REG_R5))
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ break;
+
+ case NDS32_INSN_BEQZ:
+ case NDS32_INSN_BNEZ:
+ if (RELAX_RT (fragP) < REG_R8 || RELAX_RT (fragP) == REG_R15)
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+}
+
+/* Adjust frag address due to backward relax; after this adjustment all frags
+ from the relaxed frag to current or the last frag are at correct
address. */
+
+static void
+adj_frag_addr (fragS *startP, fragS *endP, int adj)
+{
+ while (startP)
+ {
+ startP = startP->fr_next;
+ if (startP)
+ {
+ startP->fr_address += adj;
+ if (startP == endP)
+ {
+ break;
+ }
+ }
+ }
+}
+
+static void
+add_label_reloc (fragS *fragP)
+{
+ fixS *fixP;
+
+ if (!enable_relax_relocs)
+ return;
+
+ if (fragP->fr_next)
+ {
+ expressionS exp;
+
+ /* More frags follow this frag. */
+ if (!fragP->fr_next->tc_frag_data.fixup)
+ {
+ /* Next frag has no LABEL reloc; add LABEL reloc to next frag. */
+ /* No existing LABEL reloc, OK to add one. */
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = abs_section_sym;
+ exp.X_add_number = 0;
+ fixP = fix_new_exp (fragP->fr_next, 0,
+ 0, &exp, 0, BFD_RELOC_NDS32_LABEL);
+ fragP->fr_next->tc_frag_data.fixup = fixP;
+ }
+ else
+ {
+ /* LABEL reloc existed; check to see if it is at the beginning
+ of next frag; if so just do nothing, else chain it together. */
+ fixP = (fixS *) fragP->fr_next->tc_frag_data.fixup;
+ while (fixP)
+ {
+ if (fixP->fx_where == 0)
+ {
+ /* LABEL reloc at the beginning. */
+ return;
+ }
+ /* If it not null, it means there is label fix. */
+ fixP = (fixS *) fixP->tc_fix_data;
+ }
+
+ /* No LABEL reloc at the beginning. */
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = abs_section_sym;
+ exp.X_add_number = 0;
+ fixP = fix_new_exp (fragP->fr_next, 0,
+ 0, &exp, 0, BFD_RELOC_NDS32_LABEL);
+
+ fixP->tc_fix_data = (fixS*) fragP->fr_next->tc_frag_data.fixup;
+ fragP->fr_next->tc_frag_data.fixup = fixP;
+ }
+
+ }
+}
+
+
+/* Estimate the size of branch instruction sequence.
+
+ fragP: ptr to the target frag
+ segment: segment the frag is in
+ first_time: set to 1 if estimation for the first time else set to 0
+ return value: size difference after estimation. */
+
+static int
+calc_branch_seq_size (fragS *fragP,
+ segT segment, int first_time, long stretch)
+{
+ long offset;
+ long size;
+ long insn_size;
+ int org_size;
+ int off;
+ BR_RANGE_TYPE insn_range = 0;
+ BR_RANGE_TYPE third_higher = 0;
+ BR_RANGE_TYPE min_range = 0;
+ BR_RANGE_TYPE sym_range = 0;
+ BR_SEQ_TYPE code_seq;
+ int adj = 0;
+
+ adj = adj_label_pair (fragP);
+
+ /* A branch without symbol is not relaxable, do nothing. */
+ if (fragP->fr_symbol == NULL)
+ {
+ if (RELAX_RELAXABLE (fragP))
+ {
+ prev_frag = fragP;
+ }
+ return adj;
+ }
+
+ off = 0;
+ switch (RELAX_OPC_NUM (fragP))
+ {
+ case NDS32_INSN_J:
+ insn_range = BR_RANGE_S16M;
+ insn_size = 4;
+ org_size = 4;
+ off = 4;
+ code_seq = BR_UCOND_BR_S16M;
+ break;
+
+ case NDS32_INSN_JAL:
+ insn_range = BR_RANGE_S16M;
+ insn_size = 4;
+ org_size = 4;
+ off = 4;
+ code_seq = BR_UCOND_CALL_S16M;
+ break;
+
+ case NDS32_INSN_BGEZAL:
+ case NDS32_INSN_BLTZAL:
+ insn_range = BR_RANGE_S64K;
+ insn_size = 4;
+ org_size = 4;
+ off = 4;
+ code_seq = BR_COND_CALL_S64K;
+ break;
+
+ case NDS32_INSN_BEQZ:
+ case NDS32_INSN_BNEZ:
+ case NDS32_INSN_BGEZ:
+ case NDS32_INSN_BLTZ:
+ case NDS32_INSN_BGTZ:
+ case NDS32_INSN_BLEZ:
+ third_higher = BR_RANGE_S64K;
+ insn_range = BR_RANGE_S64K;
+ insn_size = 4;
+ org_size = 4;
+ off = 4;
+ code_seq = BR_COND_BR_S64K;
+ break;
+
+ case NDS32_INSN_BEQ:
+ case NDS32_INSN_BNE:
+ third_higher = BR_RANGE_S16K;
+ insn_range = BR_RANGE_S16K;
+ insn_size = 4;
+ org_size = 4;
+ off = 4;
+ code_seq = BR_COND_BR_S16K;
+ break;
+
+ case NDS32_INSN_BEQC:
+ case NDS32_INSN_BNEC:
+ third_higher = BR_RANGE_S256;
+ insn_range = BR_RANGE_S256;
+ insn_size = 4;
+ org_size = 4;
+ off = 4;
+ code_seq = BR_COND_BR_S256;
+ break;
+
+ case NDS32_INSN_JR:
+ case NDS32_INSN_RET:
+ insn_range = BR_RANGE_U4G;
+ insn_size = 4;
+ org_size = 4;
+ off = 4;
+ code_seq = BR_UCOND_BR_U4G;
+ break;
+
+ case NDS32_INSN_JRAL:
+ insn_range = BR_RANGE_U4G;
+ insn_size = 4;
+ org_size = 4;
+ off = 4;
+ code_seq = BR_UCOND_CALL_U4G;
+ break;
+
+ case NDS32_INSN_J8:
+ insn_range = BR_RANGE_S256;
+ insn_size = 2;
+ org_size = 2;
+ off = 2;
+ code_seq = BR_UCOND_BR_S256;
+ break;
+
+ case NDS32_INSN_BEQS38:
+ case NDS32_INSN_BNES38:
+ third_higher = BR_RANGE_S16K;
+ insn_range = BR_RANGE_S256;
+ insn_size = 2;
+ org_size = 2;
+ off = 2;
+ code_seq = BR_COND_BR_S256;
+ break;
+
+ case NDS32_INSN_BEQZ38:
+ case NDS32_INSN_BNEZ38:
+ case NDS32_INSN_BEQZS8:
+ case NDS32_INSN_BNEZS8:
+ third_higher = BR_RANGE_S64K;
+ insn_range = BR_RANGE_S256;
+ insn_size = 2;
+ org_size = 2;
+ off = 2;
+ code_seq = BR_COND_BR_S256;
+ break;
+
+ case NDS32_INSN_JR5:
+ case NDS32_INSN_RET5:
+ insn_range = BR_RANGE_U4G;
+ insn_size = 2;
+ org_size = 2;
+ off = 2;
+ code_seq = BR_UCOND_BR_U4G;
+ break;
+
+ case NDS32_INSN_JRAL5:
+ insn_range = BR_RANGE_U4G;
+ insn_size = 2;
+ org_size = 2;
+ off = 2;
+ code_seq = BR_UCOND_CALL_U4G;
+ break;
+
+ default:
+ insn_size = 0;
+ org_size = 0;
+ off = 0;
+ code_seq = BR_SEQ_NONE;
+ break;
+ }
+
+ if (!optimize && !optimize_for_space && !optimize_for_space_no_align)
+ {
+ /* Remember range of original instruction. */
+ min_range = insn_range;
+ }
+
+ if (!first_time)
+ {
+ code_seq = RELAX_SEQ_TYPE (fragP);
+ /* Get previous code sequence. */
+ switch (code_seq)
+ {
+ case BR_UCOND_BR_S256:
+ /* PC relative -256 to 254 unconditional branch. */
+ insn_range = BR_RANGE_S256;
+ insn_size = RELAX_RELAXED (fragP) ? 4 : 2;
+ off = insn_size;
+ break;
+
+ case BR_COND_BR_S256:
+ /* PC relative -256 to 254 conditional branch. */
+ insn_range = BR_RANGE_S256;
+ insn_size = RELAX_RELAXED (fragP) ? 4 : 2;
+ if (RELAX_OPC_NUM (fragP) == NDS32_INSN_BEQS38
+ || RELAX_OPC_NUM (fragP) == NDS32_INSN_BNES38)
+ {
+ third_higher = BR_RANGE_S16K;
+ }
+ else if (RELAX_OPC_NUM (fragP) == NDS32_INSN_BEQC
+ || RELAX_OPC_NUM (fragP) == NDS32_INSN_BNEC)
+ {
+ insn_size = 4;
+ }
+ else
+ {
+ third_higher = BR_RANGE_S64K;
+ }
+ off = insn_size;
+ break;
+
+ case BR_COND_BR_S16K:
+ /* PC relative -16K to 16K - 2 conditional branch. */
+ insn_range = BR_RANGE_S16K;
+ if (RELAX_OPC_NUM (fragP) == NDS32_INSN_BEQC
+ || RELAX_OPC_NUM (fragP) == NDS32_INSN_BNEC)
+ insn_size = 6;
+ else
+ {
+ third_higher = BR_RANGE_S16K;
+ insn_size = 4;
+ }
+ off = 4;
+ break;
+
+ case BR_COND_CALL_S64K:
+ /* PC relative -64K to 64K -2 conditional call. */
+ insn_range = BR_RANGE_S64K;
+ insn_size = 4;
+ off = insn_size;
+ break;
+
+ case BR_COND_BR_S64K:
+ /* PC relative -64K to 64K - 2 conditional branch. */
+ insn_range = BR_RANGE_S64K;
+ third_higher = BR_RANGE_S64K;
+ insn_size = 4;
+ off = insn_size;
+ break;
+
+ case BR_UCOND_CALL_S16M:
+ /* PC relative -16M to 16M -2 unconditional call. */
+ insn_range = BR_RANGE_S16M;
+ insn_size = 4;
+ off = insn_size;
+ break;
+
+ case BR_COND_CALL_S16M:
+ /* PC relative -16M to 16M -2 conditional call. */
+ insn_range = BR_RANGE_S16M;
+ insn_size = 8;
+ off = 4;
+ break;
+
+ case BR_UCOND_BR_S16M:
+ /* PC relative -16M to 16M -2 unconditional branch. */
+ insn_range = BR_RANGE_S16M;
+ insn_size = 4;
+ off = insn_size;
+ break;
+
+ case BR_COND_BR_S16M:
+ /* PC relative -16M to 16M - 2 conditional branch. */
+ insn_range = BR_RANGE_S16M;
+ insn_size = (RELAX_USE_32BIT (fragP) || optimize
+ || !is_convertible (fragP))
+ ? 8 : 6;
+ off = 4;
+ break;
+
+ case BR_UCOND_CALL_U4G:
+ /* Absolute 0 to 4G - 2 unconditional call. */
+ insn_range = BR_RANGE_U4G;
+ if (RELAX_USE_32BIT (fragP) || optimize)
+ {
+ insn_size = 12;
+ off = 4;
+ }
+ else
+ {
+ insn_size = 10;
+ off = 2;
+ }
+ break;
+
+ case BR_COND_CALL_U4G:
+ /* Absolute 0 to 4G - 2 conditional call. */
+ insn_range = BR_RANGE_U4G;
+ insn_size = (RELAX_USE_32BIT (fragP) || optimize) ? 16 : 14;
+ off = (RELAX_USE_32BIT (fragP) || optimize) ? 4 : 2;
+ break;
+
+ case BR_UCOND_BR_U4G:
+ /* Absolute 0 to 4G - 2 unconditional branch. */
+ insn_range = BR_RANGE_U4G;
+ insn_size = (RELAX_USE_32BIT (fragP) || optimize) ? 12 : 10;
+ off = (RELAX_USE_32BIT (fragP) || optimize) ? 4 : 2;
+ break;
+
+ case BR_COND_BR_U4G:
+ /* Absolute 0 to 4G - 2 conditional branch. */
+ insn_range = BR_RANGE_U4G;
+ if (RELAX_USE_32BIT (fragP)
+ || (!is_convertible (fragP) && optimize))
+ {
+ insn_size = 16;
+ off = 4;
+ }
+ else if (is_convertible (fragP))
+ {
+ insn_size = 12;
+ off = 2;
+ }
+ else
+ {
+ insn_size = 14;
+ off = 2;
+ }
+ break;
+
+ default:
+ return 0;
+ break;
+ }
+ }
+
+ if (S_GET_SEGMENT (fragP->fr_symbol) != segment
+ || S_IS_WEAK (fragP->fr_symbol))
+ {
+ /* The symbol is undefined in this segment.
+ Change the relaxation subtype to the max allowable and leave
+ all further handling to md_convert_frag. */
+
+ /* This symbol could be far far away, use longest instruction sequence,
+ and let linker relaxes it. */
+
+ offset = 0x80000000;
+ }
+ else
+ {
+ /* This is local symbol. */
+ addressT addr;
+ offsetT val;
+
+ /* Calculate symbol address and instruction address. */
+ if (S_GET_VALUE (fragP->fr_symbol) > fragP->fr_address)
+ {
+ val = (S_GET_VALUE (fragP->fr_symbol) + stretch) + fragP->fr_offset;
+ addr = fragP->fr_address + fragP->fr_fix - org_size;
+ }
+ else
+ {
+ val = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset;
+ addr = fragP->fr_address + fragP->fr_fix - org_size + insn_size - off;
+ }
+
+ /* Calculate pc relative offset. */
+ offset = val - addr;
+ }
+
+ if (offset >= -(0x100) && offset < 0x100)
+ {
+ /* +- 256 bytes, 16-bit instruction is possible. */
+ sym_range = BR_RANGE_S256;
+ }
+ else if (offset >= -(0x4000) && offset < 0x4000)
+ {
+ /* +- 16K, 32-bit conditional branch. */
+ sym_range = BR_RANGE_S16K;
+ }
+ else if (offset >= -(0x10000) && offset < 0x10000)
+ {
+ /* +- 64K, 32-bit conditional branch. */
+ sym_range = BR_RANGE_S64K;
+ }
+ else if (offset >= -(0x1000000) && offset < 0x1000000)
+ {
+ /* +- 16M, 32-bit unconditional branch. */
+ sym_range = BR_RANGE_S16M;
+ }
+ else
+ {
+ /* cases above this one are all pc relative
+ 4G, direct or indirect branch. */
+ sym_range = BR_RANGE_U4G;
+ }
+
+ if (!optimize && !optimize_for_space && !optimize_for_space_no_align)
+ {
+ if (sym_range < min_range)
+ sym_range = min_range;
+ }
+
+ /* 16-bit branch is a special case; we want to expand it if it is
in range and
+ optimize for speed is on. */
+ if (sym_range == insn_range)
+ {
+ /* Range no change. */
+ if ((code_seq == BR_COND_BR_S256 || code_seq == BR_UCOND_BR_S256)
+ && is_convertible (fragP))
+ {
+ if (optimize)
+ {
+ /* Mark as relaxable. */
+ RELAX_SET_RELAXABLE (fragP);
+ }
+ else if (optimize_for_space || optimize_for_space_no_align)
+ {
+ /* Mark as relaxed. */
+ RELAX_SET_RELAXABLE (fragP);
+ RELAX_CLEAR_RELAXED (fragP);
+ }
+ }
+
+ if (RELAX_RELAXABLE (fragP))
+ {
+ prev_frag = fragP;
+ }
+
+ RELAX_SET_FRAG_TC_FLAG (0, fragP, ((RELAX_GET_FRAG_TC_FLAG (0, fragP)
+ & (~RELAX_SEQ_TYPE_MASK))
+ | code_seq << 10));
+
+ return (first_time && RELAX_RELAXED (fragP)) ? 2 + adj : adj;
+ }
+
+ /* Reset flags. */
+ RELAX_CLEAR_RELAXABLE (fragP);
+ RELAX_CLEAR_RELAXED (fragP);
+
+ switch (code_seq)
+ {
+ case BR_UCOND_CALL_S16M:
+ case BR_UCOND_CALL_U4G:
+ if (sym_range > BR_RANGE_S16M)
+ {
+ /* sethi $ta, hi20(lable)
+ ori $ta, $ta, lo12(label)
+ jral(5) $ta
+ Size is 10 or 12 bytes. */
+ code_seq = BR_UCOND_CALL_U4G;
+
+ if (pic_code && (S_GET_SEGMENT (fragP->fr_symbol) != segment
+ || S_IS_WEAK (fragP->fr_symbol)))
+ size = 16;
+ else
+ size = (RELAX_USE_32BIT (fragP) || optimize) ? 12 : 10;
+ }
+ else
+ {
+ /* jal label */
+ code_seq = BR_UCOND_CALL_S16M;
+ size = 4;
+ }
+ break;
+
+ case BR_COND_CALL_S64K:
+ case BR_COND_CALL_S16M:
+ case BR_COND_CALL_U4G:
+ if (sym_range > BR_RANGE_S16M)
+ {
+ /* bltz rt, $1
+ sethi $ta, hi20(label)
+ ori $ta, $ta, lo12(label)
+ jral(5) $ta
+ $1:
+ Size is 14 or 16 bytes. */
+ code_seq = BR_COND_CALL_U4G;
+ if (pic_code && (S_GET_SEGMENT (fragP->fr_symbol) != segment
+ || S_IS_WEAK (fragP->fr_symbol)))
+ size = 16;
+ else
+ size = (RELAX_USE_32BIT (fragP) || optimize) ? 16 : 14;
+ }
+ else if (sym_range > BR_RANGE_S64K)
+ {
+ /* bltz rt, $1
+ jal label
+ $1:
+ Size is 8 bytes. */
+ code_seq = BR_COND_CALL_S16M;
+ size = 8;
+ }
+ else
+ {
+ /* bgezal label
+ Size is 4 bytes. */
+ code_seq = BR_COND_CALL_S64K;
+ size = 4;
+ }
+
+ break;
+
+ case BR_UCOND_BR_S256:
+ case BR_UCOND_BR_S16M:
+ case BR_UCOND_BR_U4G:
+ if (sym_range > BR_RANGE_S16M)
+ {
+ /* sethi $ta, hi20(label)
+ ori $ta, $ta, lo12(label)
+ jral(5) $ta
+ Size is 10 or 12 bytes. */
+ code_seq = BR_UCOND_BR_U4G;
+ size = (RELAX_USE_32BIT (fragP) || optimize) ? 12 : 10;
+ }
+ else if (sym_range > BR_RANGE_S256 || (RELAX_USE_32BIT (fragP)))
+ {
+ /* j label
+ Size is 4 bytes. */
+ code_seq = BR_UCOND_BR_S16M;
+ size = 4;
+ }
+ else
+ {
+ /* 16-bit branch. */
+ /* Mark this instruction. */
+ code_seq = BR_UCOND_BR_S256;
+ size = 2;
+ if (optimize)
+ {
+ /* Mark as relaxable. */
+ RELAX_SET_RELAXABLE (fragP);
+ }
+ else if (optimize_for_space || optimize_for_space_no_align)
+ {
+ /* Mark as relaxed. */
+ RELAX_SET_RELAXABLE (fragP);
+ RELAX_CLEAR_RELAXED (fragP);
+ }
+ }
+
+ break;
+
+ case BR_COND_BR_S256:
+ case BR_COND_BR_S16K:
+ case BR_COND_BR_S64K:
+ case BR_COND_BR_S16M:
+ case BR_COND_BR_U4G:
+ if (sym_range > BR_RANGE_S16M)
+ {
+ add_label_reloc (fragP);
+ code_seq = BR_COND_BR_U4G;
+ if (RELAX_USE_32BIT (fragP)
+ || (optimize && !is_convertible (fragP)))
+ {
+ /* bne rt, $lp, $1 or bnez rt, $1
+ sethi $ta, hi20(label)
+ ori $ta, $ta, lo12(label)
+ jr $ta
+ $1: */
+ size = 16;
+ }
+ else
+ {
+ if (is_convertible (fragP))
+ {
+ /* Original instruction can covert to 16-bit.
+ bnes38 rt3, $1 or bnez38 rt3, $1
+ sethi $ta, hi20(label)
+ ori $ta, $ta, lo12(label)
+ jr5 $ta
+ $1: */
+ size = 12;
+ }
+ else
+ {
+ /* bne rt, $lp, $1 or bnez rt, $1
+ sethi $ta, hi20(label)
+ ori $ta, $ta, lo12(label)
+ jr5 $ta
+ $1: */
+ size = 14;
+ }
+ }
+ }
+ else if (sym_range > third_higher)
+ {
+ add_label_reloc (fragP);
+ /* beqc rt, imm11s, imm8s */
+ if (RELAX_OPC_NUM (fragP) == NDS32_INSN_BEQC
+ || RELAX_OPC_NUM (fragP) == NDS32_INSN_BNEC)
+ {
+ if (!RELAX_USE_32BIT (fragP) && sym_range <= BR_RANGE_S16K
+ && RELAX_SIMM11 (fragP) < 16 && RELAX_SIMM11 (fragP) >= -16)
+ {
+ /* movi55 $ta, imm11s (-16 <= imm11s < 16)
+ beq rt, $ta, imm8s */
+ code_seq = BR_COND_BR_S16K;
+ size = 6;
+ }
+ else
+ {
+ /* movi $ta, imm11s
+ beq rt, $ta, imm8s */
+ code_seq = BR_COND_BR_S16M;
+ size = 8;
+ }
+ }
+ /* range > +/-16K or +/-64K */
+ /* Create LABEL reloc for next frag and attach to it. */
+ else if (RELAX_USE_32BIT (fragP) || optimize
+ || !is_convertible (fragP))
+ {
+ /* bnez rt, $1 ; this insn could be 16-bit
+ j label
+ $1:
+ Size is 8 bytes. */
+ code_seq = BR_COND_BR_S16M;
+ size = 8;
+ }
+ else
+ {
+ /* bnez38 rt3, $1
+ j label
+ $1:
+ Size is 6 bytes. */
+ code_seq = BR_COND_BR_S16M;
+ size = 6;
+ }
+ }
+ else if (sym_range <= BR_RANGE_S256 && is_convertible (fragP)
+ && !(RELAX_USE_32BIT (fragP)))
+ {
+ /* Size is 2 bytes. */
+ /* Mark this instruction. */
+ code_seq = BR_COND_BR_S256;
+ size = 2;
+ if (optimize)
+ {
+ /* Mark as relaxable. */
+ RELAX_SET_RELAXABLE (fragP);
+ }
+ else if (optimize_for_space || optimize_for_space_no_align)
+ {
+ /* Mark as relaxed. */
+ RELAX_SET_RELAXABLE (fragP);
+ RELAX_CLEAR_RELAXED (fragP);
+ }
+ }
+ else
+ {
+ /* 4-byte size. */
+ size = 4;
+ if (third_higher == BR_RANGE_S64K)
+ {
+ /* beqz rt5, label or other branches. */
+ code_seq = BR_COND_BR_S64K;
+ }
+ else if (third_higher == BR_RANGE_S16K)
+ {
+ /* beq rt, $lp, label or bne rt, $lp, label */
+ code_seq = BR_COND_BR_S16K;
+ }
+ else if (third_higher == BR_RANGE_S256)
+ {
+ /* beqc rt, simm11, label or bne rt, simm11, label */
+ code_seq = BR_COND_BR_S256;
+ }
+ }
+
+ break;
+
+ case BR_SEQ_NONE:
+ default:
+ return 0;
+ break;
+ }
+
+ if (optimize && RELAX_RELAXABLE (fragP))
+ {
+ /* This frag is marked as relaxable, save it for later use. */
+ prev_frag = fragP;
+ }
+
+ RELAX_SET_FRAG_TC_FLAG (0, fragP, ((RELAX_GET_FRAG_TC_FLAG (0, fragP)
+ & (~RELAX_SEQ_TYPE_MASK))
+ | code_seq << 10));
+
+ /* Return the size difference. */
+ return size - insn_size + adj;
+}
+
+
+
+/* Estimate the size of load/store instruction sequence.
+ fragP: ptr to the target frag.
+ segment: segment the frag is in.
+ first_time: set to 1 if estimation for the first time else set to 0.
+ return value: size difference after estimation. */
+
+static int
+calc_32_to_16_size (fragS *fragP,
+ segT segment ATTRIBUTE_UNUSED, int first_time)
+{
+ int adj = 0;
+ int insn_size;
+ int org_size;
+
+ adj = adj_label_pair (fragP);
+
+ if (first_time)
+ {
+ org_size = 2;
+ }
+ else
+ {
+ org_size = RELAX_RELAXED (fragP) ? 4 : 2;
+ }
+
+ insn_size = RELAX_RELAXED (fragP) ? 4 : 2;
+
+ return insn_size - org_size + adj;
+}
+
+/* Match couple relaxed insn and clear relax. */
+
+static int
+adj_insn16_pair (fragS *fragP)
+{
+ int adj = 0;
+ fixS *fixP;
+
+ /* Try to contract relaxed instruction pair. */
+ if ((fixP = (fixS *) (fragP->tc_frag_data.fixup)))
+ {
+ /* This frag has label. */
+ while (fixP->tc_fix_data)
+ {
+ fixP = (fixS *) fixP->tc_fix_data;
+ }
+
+ /* FIXME: Please review this || && expression and add proper
parenthesis. */
+ if (fixP && fixP->fx_r_type == BFD_RELOC_NDS32_LABEL
+ && ((fixP->fx_frag->fr_fix > (fixP->fx_where + 1)
+ && 4 == get_frag_insn_size (fixP->fx_frag, fixP->fx_where))
+ || fixP->fx_offset > 1))
+ {
+ /* This frag has label; clear previous INSN16. */
+ prev_insn16 = NULL;
+ }
+ }
+
+ if (RELAX_RELAXED (fragP))
+ {
+ /* This frag is relaxed. */
+ if (prev_insn16)
+ {
+ RELAX_CLEAR_RELAXED (prev_insn16);
+ RELAX_CLEAR_RELAXED (fragP);
+
+ /* Now adjust frag address. */
+ adj_frag_addr (prev_insn16, fragP, -2);
+ adj_frag_addr (fragP, NULL, -4);
+ prev_insn16 = NULL;
+ }
+ else
+ {
+ /* No prev relaxed insn; save it for later used. */
+ prev_insn16 = fragP;
+ }
+ }
+
+ return adj;
+}
+
+/* Save the relaxable frag in order to align the label. */
+
+static int
+adj_label_pair (fragS *fragP)
+{
+ fixS *fixP;
+ int adj = 0;
+
+ /* Looking for first label in frag. */
+ if (!optimize)
+ return 0;
+
+ if ((fixP = (fixS *) (fragP->tc_frag_data.fixup)))
+ {
+ while (fixP->tc_fix_data)
+ {
+ fixP = (fixS *) fixP->tc_fix_data;
+ }
+ if (fixP && fixP->fx_r_type == BFD_RELOC_NDS32_LABEL
+ && ((fixP->fx_frag->fr_fix > (fixP->fx_where + 1)
+ && 4 == get_frag_insn_size (fixP->fx_frag, fixP->fx_where))
+ || fixP->fx_offset > 1)
+ && prev_frag)
+ {
+ if ((fixP->fx_frag->fr_address + fixP->fx_where) & 3)
+ {
+ /* A label is not aligned on boundary and this label has
+ 4 byte instruction mark/unmark prev frag, so it will
+ be expanded or contract in next iteration. */
+ if (RELAX_RELAXED (prev_frag))
+ {
+ /* Frag has been expanded; contract it. */
+ RELAX_CLEAR_RELAXED (prev_frag);
+ adj = -2;
+ }
+ else
+ {
+ /* Frag has not been expanded; expand it. */
+ RELAX_SET_RELAXED (prev_frag);
+ adj = 2;
+ }
+
+ /* Now adjust frag address. */
+ adj_frag_addr (prev_frag, fragP, adj);
+ }
+ prev_frag = NULL;
+ }
+ }
+
+ /* This frag is relaxable, save it for later use. */
+ if (RELAX_RELAXABLE (fragP))
+ prev_frag = fragP;
+
+ return adj;
+}
+
+/* Determine prev_frag is valid. If there is any label between
+ current frag and prev_frag, set the prev_frag is invalid. */
+
+static void
+invalid_prev_frag (fragS *fragP)
+{
+ fragS *frag_t;
+
+ if (!prev_frag)
+ return;
+
+ if (prev_frag->last_fr_address >= fragP->last_fr_address)
+ {
+ prev_frag = NULL;
+ return;
+ }
+
+ 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)
+ {
+ /* If there is any alingment between this frag and prev_frag,
+ clear prev_frag, and relax the prev_frag in oder to insert
+ an optional nop if the alignment frag is not aligned. */
+ if (optimize
+ && (frag_t->fr_address & ((1 << frag_t->fr_offset) - 1))
+ && !RELAX_RELAXED (prev_frag))
+ {
+ RELAX_SET_RELAXED (prev_frag);
+ adj_frag_addr (prev_frag, frag_t, 2);
+ }
+ prev_insn16 = NULL;
+ prev_frag = NULL;
+ return;
+ }
+ frag_t = frag_t->fr_next;
+ }
+}
+
+/* md_relax_frag */
+
+long
+nds32_relax_frag (segT segment, fragS *fragP, long stretch ATTRIBUTE_UNUSED)
+{
+ int adj = 0;
+
+ invalid_prev_frag (fragP);
+ adj = adj_insn16_pair (fragP);
+
+ if (RELAX_BRANCH (fragP))
+ {
+ /* Calculate the final length difference. */
+ return calc_branch_seq_size (fragP, segment, 0, stretch) + adj;
+ }
+ else if (RELAX_RELAXABLE (fragP))
+ {
+ /* This 32-bit instruction can be converted to 16-bit. */
+ return calc_32_to_16_size (fragP, segment, 0) + adj;
+ }
+ else
+ {
+ return adj_label_pair (fragP) + adj;
+ }
+
+ return adj;
+}
+
+/* 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)
+{
+ /* The only thing we have to handle here are symbols outside of the
+ current segment. They may be undefined or in a different segment in
+ which case linker scripts may place them anywhere.
+ However, we can't finish the fragment here and emit the relocation
+ as instruction alignment requirements may move the instruction about. */
+ int adj = 0;
+
+ invalid_prev_frag (fragP);
+ adj = adj_insn16_pair (fragP);
+
+ if (RELAX_BRANCH (fragP))
+ {
+ /* Calculate relax length difference for the first time. */
+ return calc_branch_seq_size (fragP, segment, 1, 0) + adj;
+ }
+ else if (RELAX_RELAXABLE (fragP))
+ {
+ /* This 32-bit instruction can be converted to 16-bit. */
+ return calc_32_to_16_size (fragP, segment, 1) + adj;
+ }
+ else
+ {
+ return adj_label_pair (fragP) + adj;
+ }
+
+ return adj;
+}
+
+/* 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)
+{
+ int target_address;
+ int opcode_address;
+ int addend;
+ uint32_t insn = 0;
+ uint16_t insn16 = 0;
+ uint32_t comp_insn = 0;
+ uint16_t comp_insn16 = 0;
+ unsigned long reloc = 0;
+ int org_size = 0;
+ int size = 0;
+ unsigned long where;
+ char *buf;
+ expressionS exp, exp_relax, exp_cond;
+ fixS *fixP;
+ int saved_endian;
+ int insn_num;
+
+ if (fragP->fr_symbol == NULL && !RELAX_RELAXABLE (fragP))
+ return;
+
+ /* Text is always big endian. */
+ saved_endian = target_big_endian;
+ target_big_endian = 1;
+
+ insn_num = RELAX_OPC_NUM (fragP);
+ switch (RELAX_OPC_NUM (fragP))
+ {
+ case NDS32_INSN_MOVI:
+ case NDS32_INSN_SETHI:
+ case NDS32_INSN_ADDI:
+ case NDS32_INSN_ORI:
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_J:
+ insn = INSN_J;
+ insn16 = INSN_J8;
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_JAL:
+ insn = INSN_JAL;
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_JR:
+ insn = INSN_JR;
+ insn16 = INSN_JR5;
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_RET:
+ insn = INSN_RET;
+ insn16 = INSN_RET5;
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_JRAL:
+ insn = INSN_JRAL;
+ insn16 = INSN_JRAL5;
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_BEQZ:
+ reloc = BFD_RELOC_NDS32_17_PCREL;
+ insn = INSN_BEQZ | (RELAX_RT (fragP) << 20);
+ comp_insn = INSN_BNEZ | (RELAX_RT (fragP) << 20);
+ if (RELAX_RT (fragP) < REG_R8)
+ {
+ insn16 = INSN_BEQZ38 | ((RELAX_RT (fragP) & 0x7) << 8);
+ comp_insn16 = INSN_BNEZ38 | ((RELAX_RT (fragP) & 0x7) << 8);
+ }
+ else if (RELAX_RT (fragP) == REG_TA)
+ {
+ insn16 = INSN_BEQZS8;
+ comp_insn16 = INSN_BNEZS8;
+ }
+
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_BNEZ:
+ reloc = BFD_RELOC_NDS32_17_PCREL;
+ insn = INSN_BNEZ | (RELAX_RT (fragP) << 20);
+ comp_insn = INSN_BEQZ | (RELAX_RT (fragP) << 20);
+ if (RELAX_RT (fragP) < REG_R8)
+ {
+ insn16 = INSN_BNEZ38 | ((RELAX_RT (fragP) & 0x7) << 8);
+ comp_insn16 = INSN_BEQZ38 | ((RELAX_RT (fragP) & 0x7) << 8);
+ }
+ else if (RELAX_RT (fragP) == REG_TA)
+ {
+ insn16 = INSN_BNEZS8;
+ comp_insn16 = INSN_BEQZS8;
+ }
+
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_BGEZ:
+ insn = INSN_BGEZ | (RELAX_RT (fragP) << 20);
+ comp_insn = INSN_BLTZ | (RELAX_RT (fragP) << 20);
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_BLTZ:
+ insn = INSN_BLTZ | (RELAX_RT (fragP) << 20);
+ comp_insn = INSN_BGEZ | (RELAX_RT (fragP) << 20);
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_BGTZ:
+ insn = INSN_BGTZ | (RELAX_RT (fragP) << 20);
+ comp_insn = INSN_BLEZ | (RELAX_RT (fragP) << 20);
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_BLEZ:
+ insn = INSN_BLEZ | (RELAX_RT (fragP) << 20);
+ comp_insn = INSN_BGTZ | (RELAX_RT (fragP) << 20);
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_BGEZAL:
+ insn = INSN_BGEZAL | (RELAX_RT (fragP) << 20);
+ comp_insn = INSN_BLTZ | (RELAX_RT (fragP) << 20);
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_BLTZAL:
+ insn = INSN_BLTZAL | (RELAX_RT (fragP) << 20);
+ comp_insn = INSN_BGEZ | (RELAX_RT (fragP) << 20);
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_BEQ:
+ reloc = BFD_RELOC_NDS32_15_PCREL;
+ insn = INSN_BEQ | (RELAX_RT (fragP) << 20) | (RELAX_RA (fragP) << 15);
+ comp_insn = INSN_BNE | (RELAX_RT (fragP) << 20) | (RELAX_RA
(fragP) << 15);
+ if (RELAX_RT (fragP) < REG_R8 && RELAX_RA (fragP) == REG_R5
+ && RELAX_RT (fragP) != RELAX_RA (fragP))
+ {
+ insn16 = INSN_BEQS38 | ((RELAX_RT (fragP) & 0x7) << 8);
+ comp_insn16 = INSN_BNES38 | ((RELAX_RT (fragP) & 0x7) << 8);
+ }
+ else if (RELAX_RA (fragP) < REG_R8 && RELAX_RT (fragP) == REG_R5
+ && RELAX_RT (fragP) != RELAX_RA (fragP))
+ {
+ insn16 = INSN_BEQS38 | ((RELAX_RA (fragP) & 0x7) << 8);
+ comp_insn16 = INSN_BNES38 | ((RELAX_RA (fragP) & 0x7) << 8);
+ }
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_BNE:
+ reloc = BFD_RELOC_NDS32_15_PCREL;
+ insn = INSN_BNE | (RELAX_RT (fragP) << 20) | (RELAX_RA (fragP) << 15);
+ comp_insn = INSN_BEQ | (RELAX_RT (fragP) << 20)
+ | (RELAX_RA (fragP) << 15);
+ if (RELAX_RT (fragP) < REG_R8 && RELAX_RA (fragP) == REG_R5
+ && RELAX_RT (fragP) != RELAX_RA (fragP))
+ {
+ insn16 = INSN_BNES38 | ((RELAX_RT (fragP) & 0x7) << 8);
+ comp_insn16 = INSN_BEQS38 | ((RELAX_RT (fragP) & 0x7) << 8);
+ }
+ else if (RELAX_RA (fragP) < REG_R8 && RELAX_RT (fragP) == REG_R5
+ && RELAX_RT (fragP) != RELAX_RA (fragP))
+ {
+ insn16 = INSN_BNES38 | ((RELAX_RA (fragP) & 0x7) << 8);
+ comp_insn16 = INSN_BEQS38 | ((RELAX_RA (fragP) & 0x7) << 8);
+ }
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_BEQC:
+ reloc = BFD_RELOC_NDS32_WORD_9_PCREL;
+ insn = N32_BR3 (BEQC, RELAX_RT (fragP), RELAX_SIMM11 (fragP), 0);
+ if (RELAX_SEQ_TYPE (fragP) == BR_COND_BR_S16K
+ && RELAX_SIMM11 (fragP) < 16 && RELAX_SIMM11 (fragP) >= -16)
+ {
+ comp_insn16 = N16_TYPE55 (MOVI55, REG_R15, RELAX_SIMM11 (fragP));
+ comp_insn = N32_BR1 (BEQ, RELAX_RT (fragP), REG_R15, 0);
+ }
+ else
+ comp_insn = N32_BR3 (BNEC, RELAX_RT (fragP), RELAX_SIMM11 (fragP), 0);
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_BNEC:
+ reloc = BFD_RELOC_NDS32_WORD_9_PCREL;
+ insn = N32_BR3 (BNEC, RELAX_RT (fragP), RELAX_SIMM11 (fragP), 0);
+ if (RELAX_SEQ_TYPE (fragP) == BR_COND_BR_S16K
+ && RELAX_SIMM11 (fragP) < 16 && RELAX_SIMM11 (fragP) >= -16)
+ {
+ comp_insn16 = N16_TYPE55 (MOVI55, REG_R15, RELAX_SIMM11 (fragP));
+ comp_insn = N32_BR1 (BNE, RELAX_RT (fragP), REG_R15, 0);
+ }
+ else
+ comp_insn = N32_BR3 (BEQC, RELAX_RT (fragP), RELAX_SIMM11 (fragP), 0);
+ org_size = 4;
+ break;
+
+ case NDS32_INSN_J8:
+ insn16 = INSN_J8;
+ insn = INSN_J;
+ org_size = 2;
+ break;
+
+ case NDS32_INSN_JR5:
+ insn16 = INSN_JR5;
+ insn = INSN_JR;
+ org_size = 2;
+ break;
+
+ case NDS32_INSN_RET5:
+ insn16 = INSN_RET5;
+ insn = INSN_RET;
+ org_size = 2;
+ break;
+
+ case NDS32_INSN_JRAL5:
+ insn16 = INSN_JRAL5;
+ insn = INSN_JRAL;
+ org_size = 2;
+ break;
+
+ case NDS32_INSN_BEQZ38:
+ reloc = BFD_RELOC_NDS32_17_PCREL;
+ comp_insn16 = INSN_BNEZ38 | ((RELAX_RT (fragP) & 0x7) << 8);
+ insn16 = INSN_BEQZ38 | ((RELAX_RT (fragP) & 0x7) << 8);
+ comp_insn = INSN_BNEZ | (RELAX_RT (fragP) << 20);
+ insn = INSN_BEQZ | (RELAX_RT (fragP) << 20);
+ org_size = 2;
+ break;
+
+ case NDS32_INSN_BNEZ38:
+ reloc = BFD_RELOC_NDS32_17_PCREL;
+ comp_insn16 = INSN_BEQZ38 | ((RELAX_RT (fragP) & 0x7) << 8);
+ insn16 = INSN_BNEZ38 | ((RELAX_RT (fragP) & 0x7) << 8);
+ comp_insn = INSN_BEQZ | (RELAX_RT (fragP) << 20);
+ insn = INSN_BNEZ | (RELAX_RT (fragP) << 20);
+ org_size = 2;
+ break;
+
+ case NDS32_INSN_BEQS38:
+ reloc = BFD_RELOC_NDS32_15_PCREL;
+ comp_insn16 = INSN_BNES38 | ((RELAX_RT (fragP) & 0x7) << 8);
+ insn16 = INSN_BEQS38 | ((RELAX_RT (fragP) & 0x7) << 8);
+ comp_insn = INSN_BNE | (RELAX_RT (fragP) << 20) | (REG_R5 << 15);
+ insn = INSN_BEQ | (RELAX_RT (fragP) << 20) | (REG_R5 << 15);
+ org_size = 2;
+ break;
+
+ case NDS32_INSN_BNES38:
+ reloc = BFD_RELOC_NDS32_15_PCREL;
+ comp_insn16 = INSN_BEQS38 | ((RELAX_RT (fragP) & 0x7) << 8);
+ insn16 = INSN_BNES38 | ((RELAX_RT (fragP) & 0x7) << 8);
+ comp_insn = INSN_BEQ | (RELAX_RT (fragP) << 20) | (REG_R5 << 15);
+ insn = INSN_BNE | (RELAX_RT (fragP) << 20) | (REG_R5 << 15);
+ org_size = 2;
+ break;
+
+ case NDS32_INSN_BEQZS8:
+ reloc = BFD_RELOC_NDS32_17_PCREL;
+ comp_insn16 = INSN_BNEZS8;
+ insn16 = INSN_BEQZS8;
+ comp_insn = INSN_BNEZ | (REG_R15 << 20);
+ insn = INSN_BEQZ | (REG_R15 << 20);
+ org_size = 2;
+ break;
+
+ case NDS32_INSN_BNEZS8:
+ reloc = BFD_RELOC_NDS32_17_PCREL;
+ comp_insn16 = INSN_BEQZS8;
+ insn16 = INSN_BNEZS8;
+ comp_insn = INSN_BEQZ | (REG_R15 << 20);
+ insn = INSN_BNEZ | (REG_R15 << 20);
+ org_size = 2;
+ break;
+
+ case NDS32_INSN_IFRET:
+ insn16 = ((*(fragP->fr_literal + fragP->fr_fix - 2) & 0xff) << 8)
+ | (*(fragP->fr_literal + fragP->fr_fix - 1) & 0xff);
+
+ /* Check if current instruction is IFRET16. */
+ if (insn16 == INSN_IFRET16)
+ /* IFRET16 */
+ org_size = 2;
+ else
+ /* IFRET */
+ org_size = 4;
+ /* insn = INSN_IFRET; */
+ /* insn16 = INSN_IFRET16; */
+ break;
+
+ case NDS32_INSN_BMSKI33:
+ case NDS32_INSN_FEXTI33:
+ case NDS32_INSN_AND33:
+ case NDS32_INSN_OR33:
+ case NDS32_INSN_XOR33:
+ case NDS32_INSN_MUL33:
+ case NDS32_INSN_NOT33:
+ case NDS32_INSN_NEG33:
+ case NDS32_INSN_ADDRI36_SP:
+ case NDS32_INSN_MOVPI45:
+
+ case NDS32_INSN_MOV55:
+ case NDS32_INSN_MOVI55:
+ case NDS32_INSN_ADDI45:
+ case NDS32_INSN_ADD45:
+ case NDS32_INSN_SUBI45:
+ case NDS32_INSN_SUB45:
+ case NDS32_INSN_SRAI45:
+ case NDS32_INSN_SRLI45:
+ case NDS32_INSN_SLLI333:
+ case NDS32_INSN_SEB33:
+ case NDS32_INSN_SEH33:
+ case NDS32_INSN_ZEB33:
+ case NDS32_INSN_ZEH33:
+ case NDS32_INSN_XLSB33:
+ case NDS32_INSN_X11B33:
+ case NDS32_INSN_ADDI333:
+ case NDS32_INSN_ADD333:
+ case NDS32_INSN_SUBI333:
+ case NDS32_INSN_SUB333:
+ case NDS32_INSN_LWI333:
+ case NDS32_INSN_LWI333_BI:
+ case NDS32_INSN_LHI333:
+ case NDS32_INSN_LBI333:
+ case NDS32_INSN_SWI333:
+ case NDS32_INSN_SWI333_BI:
+ case NDS32_INSN_SHI333:
+ case NDS32_INSN_SBI333:
+ case NDS32_INSN_LWI450:
+ case NDS32_INSN_SWI450:
+ case NDS32_INSN_LWI37:
+ case NDS32_INSN_SWI37:
+ case NDS32_INSN_SLTI45:
+ case NDS32_INSN_SLTSI45:
+ case NDS32_INSN_SLT45:
+ case NDS32_INSN_SLTS45:
+ case NDS32_INSN_BREAK16:
+ case NDS32_INSN_ADDI10_SP:
+ case NDS32_INSN_LWI37_SP:
+ case NDS32_INSN_SWI37_SP:
+ org_size = 2;
+ break;
+
+ default:
+ org_size = 4;
+ break;
+ }
+
+ buf = fragP->fr_literal + fragP->fr_fix - org_size;
+ where = fragP->fr_fix - org_size;
+
+ /* We have final code sequence for relaxable object, expand it now. */
+ if (RELAX_BRANCH (fragP))
+ {
+
+ if (S_GET_SEGMENT (fragP->fr_symbol) != sec
+ || S_IS_WEAK (fragP->fr_symbol))
+ {
+ /* Symbol must be resolved by linker. */
+ if (fragP->fr_offset & 3)
+ as_warn (_("Addend to unresolved symbol not on word boundary."));
+ addend = 0;
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = fragP->fr_symbol;
+ exp.X_add_number = fragP->fr_offset;
+ }
+ else
+ {
+ /* Address we want to reach in file space. */
+ opcode_address = fragP->fr_address + fragP->fr_fix - org_size;
+ target_address = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset;
+ addend = (target_address - (opcode_address & -2)) >> 1;
+
+ /* Symbol_get_value_expression ?? */
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = fragP->fr_symbol;
+ exp.X_add_number = fragP->fr_offset;
+ }
+
+ switch (RELAX_SEQ_TYPE (fragP))
+ {
+ case BR_UCOND_CALL_S16M:
+ /* jal label ; 25_PCREL
+ Instruction does not change.
+ Just fill in the addend.
+ Symbol has been resolved. */
+
+ /* ET - TBI: assume code is within +/-16M, we should handle. */
+ /* Enlarger the range later. */
+ insn = INSN_JAL;
+ md_number_to_chars (buf, insn, 4);
+ if (pic_code && (S_GET_SEGMENT (fragP->fr_symbol) != sec
+ || S_IS_WEAK (fragP->fr_symbol)))
+ fixP = fix_new_exp (fragP, where, 4, &exp, 0,
+ BFD_RELOC_NDS32_25_PLTREL);
+ else
+ fixP = fix_new_exp (fragP, where, 4, &exp, 0,
+ BFD_RELOC_NDS32_25_PCREL);
+ fixP->fx_addnumber = fixP->fx_offset;
+ size = 4;
+ break;
+
+ case BR_UCOND_CALL_U4G:
+ if (pic_code && (S_GET_SEGMENT (fragP->fr_symbol) != sec
+ || S_IS_WEAK (fragP->fr_symbol)))
+ {
+ /* sethi $ta, hi20(label) ; PLT_HI20
+ ori $ta, $ta, lo20(label) ; PLT_LO12/PTR/PTRCOUNT
+ add $ta, $ta, $gp ; PTR/PTRCOUT
+ jral $ta or jral5 $ta ; PLT_GOT_SUFF (INSN16) */
+
+ /* Create sethi. */
+ insn = INSN_SETHI_TA;
+ md_number_to_chars (buf, insn, 4);
+ fixP = fix_new_exp (fragP, where, 4, &exp, 0,
+ BFD_RELOC_NDS32_PLT_GOTREL_HI20);
+ fixP->fx_addnumber = fixP->fx_offset;
+
+ /* Create ori. */
+ insn = INSN_ORI_TA;
+ md_number_to_chars (buf + 4, insn, 4);
+ fixP = fix_new_exp (fragP, where + 4, 4, &exp, 0,
+ BFD_RELOC_NDS32_PLT_GOTREL_LO12);
+ fixP->fx_addnumber = fixP->fx_offset;
+
+ insn = INSN_ADD_TA | (REG_TA << 15 | REG_GP << 10);
+ md_number_to_chars (buf + 8, insn, 4);
+
+ insn = INSN_JRAL_TA;
+ md_number_to_chars (buf + 12, insn, 4);
+ size = 16;
+ if (!RELAX_USE_32BIT (fragP) && optimize && enable_relax_relocs)
+ {
+ /* Optimize for speed, use jral because next instruction is
+ return address which has a implicit label
+ Insert INSN16 reloc, so we can contract it to align next
+ label if necessary. */
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = 0;
+ fix_new_exp (fragP, where + 8, 4, &exp_relax, 0,
+ BFD_RELOC_NDS32_INSN16);
+ fix_new_exp (fragP, where + 12, 4, &exp_relax, 0,
+ BFD_RELOC_NDS32_INSN16);
+ }
+
+ if (enable_relax_relocs)
+ {
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = SET_ADDEND (size, is_convertible (fragP),
+ optimize, !RELAX_USE_32BIT (fragP));
+ fix_new_exp (fragP, where, size, &exp_relax, 0,
+ BFD_RELOC_NDS32_PLTBLOCK);
+ }
+ }
+ else
+ {
+ /* sethi $ta, hi20(label) ; LONGCALL1/HI20
+ ori $ta, $ta, lo20(label) ; LO12S0
+ jral $ta or jral5 $ta ; (INSN16) */
+
+ /* gas_assert (RELAX_OPC_NUM(fragP->fr_subtype) == NDS32_INSN_JAL ); */
+
+ /* Create sethi. */
+ insn = INSN_SETHI_TA;
+ md_number_to_chars (buf, insn, 4);
+ fixP = fix_new_exp (fragP, where, 4, &exp, 0, BFD_RELOC_NDS32_HI20);
+ fixP->fx_addnumber = fixP->fx_offset;
+
+ /* Create ori. */
+ insn = INSN_ORI_TA;
+ md_number_to_chars (buf + 4, insn, 4);
+ if (enable_relax_relocs)
+ fixP = fix_new_exp (fragP, where + 4, 4, &exp, 0,
+ BFD_RELOC_NDS32_LO12S0_ORI);
+ else
+ fixP = fix_new_exp (fragP, where + 4, 4, &exp, 0,
+ BFD_RELOC_NDS32_LO12S0);
+ fixP->fx_addnumber = fixP->fx_offset;
+
+ if (RELAX_USE_32BIT (fragP) || optimize)
+ {
+ insn = INSN_JRAL_TA;
+ md_number_to_chars (buf + 8, insn, 4);
+ size = 12;
+ if (!RELAX_USE_32BIT (fragP) && optimize
+ && enable_relax_relocs)
+ {
+ /* Optimize for speed, use jral because next instruction
+ is return address which has a implicit label
+ Insert INSN16 reloc, so we can contract it to align next
+ label if necessary. */
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = 0;
+ fix_new_exp (fragP, where + 8, 4, &exp_relax, 0,
+ BFD_RELOC_NDS32_INSN16);
+ }
+ }
+ else
+ {
+ /* Use jral5 for smaller code size. */
+ insn16 = INSN_JRAL5_TA;
+ md_number_to_chars (buf + 8, insn16, 2);
+ size = 10;
+ }
+
+ /* Create a reloc for this sequence, so linker can contract
+ it to smaller size. The register $ta may be used by callee
+ to to calculate $gp this optimization needs further investigation. */
+ if (enable_relax_relocs)
+ {
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = SET_ADDEND (size, is_convertible (fragP),
+ optimize, !RELAX_USE_32BIT (fragP));
+ fix_new_exp (fragP, where, size, &exp_relax, 0,
+ BFD_RELOC_NDS32_LONGCALL1);
+ }
+ }
+ break;
+
+ case BR_COND_CALL_S64K:
+ /* `bgezal rt, label' or `bltzal rt, label' 17_PCREL
+ 0x0e0c0000 | (reg << 20)
+ 0x0e0d0000 | (reg << 20)
+ Instruction does not change.
+ Symbol has been resolved, no reloc is needed.
+ Just fill in the addend. */
+
+ insn |= (RELAX_RT (fragP) << 20);
+ md_number_to_chars (buf, insn, 4);
+
+ fixP = fix_new_exp (fragP, where, 4, &exp, 0, BFD_RELOC_NDS32_17_PCREL);
+ fixP->fx_addnumber = fixP->fx_offset;
+ size = 4;
+ break;
+
+ case BR_COND_CALL_S16M:
+ /* bltz rt, $1 or bgez rt, $1 ; LONGCALL2
+ jal label ; 25_PCREL
+ $1: */
+
+ comp_insn |= (RELAX_RT (fragP) << 20);
+ md_number_to_chars (buf, comp_insn, 4);
+ exp_cond.X_op = O_symbol;
+ exp_cond.X_add_symbol = symbol_temp_new (sec, 0, fragP->fr_next);
+ exp_cond.X_add_number = 0;
+ fix_new_exp (fragP, where, 4, &exp_cond, 0,
+ BFD_RELOC_NDS32_17_PCREL);
+
+ /* Create jal. */
+ /* ET - TBI: assume code is within +/-16M, we should handle. */
+ /* Larger range later. */
+ insn = INSN_JAL;
+ md_number_to_chars (buf + 4, insn, 4);
+ if (pic_code && (S_GET_SEGMENT (fragP->fr_symbol) != sec
+ || S_IS_WEAK (fragP->fr_symbol)))
+ fixP = fix_new_exp (fragP, where + 4, 4, &exp, 0,
+ BFD_RELOC_NDS32_25_PLTREL);
+ else
+ fixP = fix_new_exp (fragP, where + 4, 4, &exp, 0,
+ BFD_RELOC_NDS32_25_PCREL);
+ fixP->fx_addnumber = fixP->fx_offset;
+ size = 8;
+
+ if (enable_relax_relocs)
+ {
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = SET_ADDEND (8, is_convertible (fragP),
+ optimize, !RELAX_USE_32BIT (fragP));
+ fix_new_exp (fragP, where, 8, &exp_relax, 0,
+ BFD_RELOC_NDS32_LONGCALL2);
+ }
+
+ break;
+
+ case BR_COND_CALL_U4G:
+ /* bltz rt, $1 or bgez rt, $1 ; LONGCALL3
+ sethi $ta, hi20(label) ; HI20
+ ori $ta, $ta, lo12(label) ; LO12S0
+ jral $ta or jral5 $ta ; (INSN16)
+ $1: */
+
+ comp_insn |= (RELAX_RT (fragP) << 20);
+ md_number_to_chars (buf, comp_insn, 4);
+ exp_cond.X_op = O_symbol;
+ exp_cond.X_add_symbol = symbol_temp_new (sec, 0, fragP->fr_next);
+ exp_cond.X_add_number = 0;
+ fix_new_exp (fragP, where, 4, &exp_cond, 0,
+ BFD_RELOC_NDS32_17_PCREL);
+
+ if (pic_code && (S_GET_SEGMENT (fragP->fr_symbol) != sec
+ || S_IS_WEAK (fragP->fr_symbol)))
+ {
+ /* Create sethi. */
+ insn = INSN_SETHI_TA;
+ md_number_to_chars (buf + 4, insn, 4);
+ fixP = fix_new_exp (fragP, where + 4, 4, &exp, 0,
+ BFD_RELOC_NDS32_PLT_GOTREL_HI20);
+ fixP->fx_addnumber = fixP->fx_offset;
+
+ /* Create ori. */
+ insn = INSN_ORI_TA;
+ md_number_to_chars (buf + 8, insn, 4);
+ fixP = fix_new_exp (fragP, where + 8, 4, &exp, 0,
+ BFD_RELOC_NDS32_PLT_GOTREL_LO12);
+ fixP->fx_addnumber = fixP->fx_offset;
+
+ insn = INSN_ADD_TA | (REG_TA << 15 | REG_GP << 10);
+ md_number_to_chars (buf + 12, insn, 4);
+
+ insn = INSN_JRAL_TA;
+ md_number_to_chars (buf + 16, insn, 4);
+ size = 16;
+ if (!RELAX_USE_32BIT (fragP) && optimize && enable_relax_relocs)
+ {
+ /* Optimize for speed, use jral because next instruction is
+ return address which has a implicit label.
+ Insert 16-bit instruction fixup, so we can contract it to
+ align next label if necessary. */
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = 0;
+ fix_new_exp (fragP, where + 12, 4, &exp_relax, 0,
+ BFD_RELOC_NDS32_INSN16);
+ fix_new_exp (fragP, where + 16, 4, &exp_relax, 0,
+ BFD_RELOC_NDS32_INSN16);
+ }
+
+ /* Create a reloc for this sequence, so linker can contract
+ it to smaller size. */
+ if (enable_relax_relocs)
+ {
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = SET_ADDEND (size, is_convertible (fragP),
+ optimize, !RELAX_USE_32BIT (fragP));
+ fix_new_exp (fragP, where + 4, size, &exp_relax, 0,
+ BFD_RELOC_NDS32_PLTBLOCK);
+ }
+ }
+ else
+ {
+ /* Create sethi. */
+ insn = INSN_SETHI_TA;
+ md_number_to_chars (buf + 4, insn, 4);
+
+ fixP = fix_new_exp (fragP, where + 4, 4, &exp, 0,
+ BFD_RELOC_NDS32_HI20);
+ fixP->fx_addnumber = fixP->fx_offset;
+
+ /* Create ori. */
+ insn = INSN_ORI_TA;
+ md_number_to_chars (buf + 8, insn, 4);
+
+ if (enable_relax_relocs)
+ fixP = fix_new_exp (fragP, where + 8, 4, &exp, 0,
+ BFD_RELOC_NDS32_LO12S0_ORI);
+ else
+ fixP = fix_new_exp (fragP, where + 8, 4, &exp, 0,
+ BFD_RELOC_NDS32_LO12S0);
+ fixP->fx_addnumber = fixP->fx_offset;
+
+ /* Create jral(5). */
+ if (RELAX_USE_32BIT (fragP) || optimize)
+ {
+ insn = INSN_JRAL_TA;
+ md_number_to_chars (buf + 12, insn, 4);
+ size = 16;
+ if (!RELAX_USE_32BIT (fragP) && optimize
+ && enable_relax_relocs)
+ {
+ /* Optimize for speed, use jral because next instruction
+ is return address which has a implicit label.
+ Insert 16-bit instruction fixup, so we can contract it
+ to align next label if necessary. */
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = 0;
+ fix_new_exp (fragP, where + 12, 4, &exp_relax, 0,
+ BFD_RELOC_NDS32_INSN16);
+ }
+ }
+ else
+ {
+ /* Use jral5 for smaller code size. */
+ insn16 = INSN_JRAL5_TA;
+ md_number_to_chars (buf + 12, insn16, 2);
+ size = 14;
+ }
+
+ /* Create a reloc for this sequence, so linker can contract
+ it to smaller size. */
+ if (enable_relax_relocs)
+ {
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = SET_ADDEND (size, is_convertible (fragP),
+ optimize, !RELAX_USE_32BIT (fragP));
+ fix_new_exp (fragP, where, size, &exp_relax, 0,
+ BFD_RELOC_NDS32_LONGCALL3);
+ }
+ }
+ break;
+
+ case BR_UCOND_BR_S256:
+ /* j8 label */
+
+ if (optimize && RELAX_RELAXED (fragP))
+ {
+ if (!RELAX_USE_32BIT (fragP)
+ && optimize && addend >= -0x80 && addend < 0x80
+ && enable_relax_relocs)
+ {
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = 0;
+ fix_new_exp (fragP, where, 4, &exp_relax, 0,
+ BFD_RELOC_NDS32_INSN16);
+ }
+
+ insn = INSN_J;
+ md_number_to_chars (buf, insn, 4);
+ fixP = fix_new_exp (fragP, where, 4, &exp, 0,
+ BFD_RELOC_NDS32_25_PCREL);
+ fixP->fx_addnumber = fixP->fx_offset;
+ size = 4;
+ }
+ else
+ {
+ insn16 = INSN_J8;
+ md_number_to_chars (buf, insn16, 2);
+ fixP = fix_new_exp (fragP, where, 2, &exp, 0,
+ BFD_RELOC_NDS32_9_PCREL);
+ fixP->fx_addnumber = fixP->fx_offset;
+ /* The addend is not embedded in the instruction,
+ the test of overflow can be passed. */
+ fixP->fx_no_overflow = 1;
+ size = 2;
+ }
+ break;
+
+ case BR_UCOND_BR_S16M:
+ /* j label ; 25_PCREL/INSN16 */
+
+ if (RELAX_RELAXABLE (fragP) && RELAX_RELAXED (fragP)
+ && enable_relax_relocs)
+ {
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = abs_section_sym;
+ exp.X_add_number = 0;
+ fix_new_exp (fragP, where, 4, &exp, 0, BFD_RELOC_NDS32_INSN16);
+ }
+ /* ET - TBI: assume code is within +/-16M, we should handle. */
+ /* Larger range later. */
+ insn = INSN_J;
+ md_number_to_chars (buf, insn, 4);
+ if (pic_code && (S_GET_SEGMENT (fragP->fr_symbol) != sec
+ || S_IS_WEAK (fragP->fr_symbol)))
+ fixP = fix_new_exp (fragP, where, 4, &exp, 0,
+ BFD_RELOC_NDS32_25_PLTREL);
+ else
+ fixP = fix_new_exp (fragP, where, 4, &exp, 0,
+ BFD_RELOC_NDS32_25_PCREL);
+ fixP->fx_addnumber = fixP->fx_offset;
+ size = 4;
+ break;
+
+ case BR_UCOND_BR_U4G:
+ /* sethi $ta, hi20(label) ; LONGJUMP1/HI20
+ ori $ta, $ta, lo12(label) ; LO12S0
+ jr $ta or jr5 $ta ; (INSN16) */
+
+ /* Create sethi. */
+ insn = INSN_SETHI_TA;
+ md_number_to_chars (buf, insn, 4);
+ fixP = fix_new_exp (fragP, where, 4, &exp, 0, BFD_RELOC_NDS32_HI20);
+ fixP->fx_addnumber = fixP->fx_offset;
+
+ /* Create ori. */
+ insn = INSN_ORI_TA;
+ md_number_to_chars (buf + 4, insn, 4);
+ if (enable_relax_relocs)
+ fixP = fix_new_exp (fragP, where + 4, 4, &exp, 0,
+ BFD_RELOC_NDS32_LO12S0_ORI);
+ else
+ fixP = fix_new_exp (fragP, where + 4, 4, &exp, 0,
+ BFD_RELOC_NDS32_LO12S0);
+ fixP->fx_addnumber = fixP->fx_offset;
+
+ if (RELAX_USE_32BIT (fragP) || optimize)
+ {
+ insn = INSN_JR_TA;
+ md_number_to_chars (buf + 8, insn, 4);
+ size = 12;
+ if (!RELAX_USE_32BIT (fragP) && optimize && enable_relax_relocs)
+ {
+ /* Optimize for speed, use jral because next instruction is
+ return address which has a implicit label.
+
+ Instruct 16-bit instruction fixup, so we can contract it
+ to align next label if necessary. Save fixup info for later
+ BFD_RELOC_NDS32_LABEL. */
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = 0;
+ fix_new_exp (fragP, where + 8, 4, &exp_relax, 0,
+ BFD_RELOC_NDS32_INSN16);
+ }
+ }
+ else
+ {
+ /* Use jr5 for smaller code size. */
+ insn16 = INSN_JR5_TA;
+ md_number_to_chars (buf + 8, insn16, 2);
+ size = 10;
+ }
+
+ /* Create a relocation for this sequence, so linker can contract
+ it to smaller size. */
+ if (enable_relax_relocs)
+ {
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = SET_ADDEND (size, is_convertible (fragP),
+ optimize, !RELAX_USE_32BIT (fragP));
+ fix_new_exp (fragP, where, size, &exp_relax, 0,
+ BFD_RELOC_NDS32_LONGJUMP1);
+ }
+ break;
+
+ case BR_COND_BR_S256:
+ /* beqs38 rt3, label or other 16-bit branch. */
+
+ if (insn_num == NDS32_INSN_BEQC || insn_num == NDS32_INSN_BNEC)
+ {
+ /* beqc/bnec */
+ fixP = fix_new_exp (fragP, where, 4, &exp, 0, reloc);
+ fixP->fx_addnumber = fixP->fx_offset;
+ md_number_to_chars (buf, insn, 4);
+ size = 4;
+ }
+ else if (optimize && RELAX_RELAXED (fragP))
+ {
+ if (!RELAX_USE_32BIT (fragP) && optimize
+ && addend >= -0x80 && addend < 0x80 && enable_relax_relocs)
+ {
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = 0;
+ fix_new_exp (fragP, where, 4, &exp_relax, 0,
+ BFD_RELOC_NDS32_INSN16);
+ }
+ fixP = fix_new_exp (fragP, where, 4, &exp, 0, reloc);
+ fixP->fx_addnumber = fixP->fx_offset;
+ if (reloc == BFD_RELOC_NDS32_9_PCREL)
+ fixP->fx_no_overflow = 1;
+ md_number_to_chars (buf, insn, 4);
+ size = 4;
+ }
+ else
+ {
+ md_number_to_chars (buf, insn16, 2);
+ size = 2;
+ /* The addend is not embedded in the instruction,
+ the test of overflow can be passed. */
+ fixP = fix_new_exp (fragP, where, 2, &exp, 0,
+ BFD_RELOC_NDS32_9_PCREL);
+ fixP->fx_addnumber = fixP->fx_offset;
+ fixP->fx_no_overflow = 1;
+ }
+ break;
+
+ case BR_COND_BR_S16K:
+ /* beq label or bne label */
+ if (insn_num == NDS32_INSN_BEQC || insn_num == NDS32_INSN_BNEC)
+ {
+ size=2;
+ md_number_to_chars (buf, comp_insn16, 2);
+ addend -= 1;
+ md_number_to_chars (buf+size, comp_insn, 4);
+ fixP = fix_new_exp (fragP, where+2, 4, &exp, 0,
BFD_RELOC_NDS32_15_PCREL);
+ fixP->fx_addnumber = fixP->fx_offset;
+
+ /* 16-bit instruction fixup,
+ so we can expand it to align next label if necessary. */
+ size += 4;
+ break;
+ }
+ md_number_to_chars (buf, insn, 4);
+ fixP = fix_new_exp (fragP, where, 4, &exp, 0, BFD_RELOC_NDS32_15_PCREL);
+ fixP->fx_addnumber = fixP->fx_offset;
+
+ /* 16-bit instruction fixup, so we can expand it to align next
+ label if necessary. */
+ if ((optimize || optimize_for_space || optimize_for_space_no_align)
+ && is_convertible (fragP) && enable_relax_relocs)
+ {
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = abs_section_sym;
+ exp.X_add_number = 0;
+ fix_new_exp (fragP, where, 4, &exp, 0, BFD_RELOC_NDS32_INSN16);
+ }
+
+ size = 4;
+ break;
+
+ case BR_COND_BR_S64K:
+ /* 32-bit branch instructions except bne or beq. */
+
+ md_number_to_chars (buf, insn, 4);
+
+ fixP = fix_new_exp (fragP, where, 4, &exp, 0, BFD_RELOC_NDS32_17_PCREL);
+ fixP->fx_addnumber = fixP->fx_offset;
+
+ if (!RELAX_USE_32BIT (fragP)
+ && is_convertible (fragP) && enable_relax_relocs)
+ {
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = 0;
+ fix_new_exp (fragP, where, 4, &exp_relax, 0,
+ BFD_RELOC_NDS32_INSN16);
+ }
+
+ size = 4;
+ break;
+
+ case BR_COND_BR_S16M:
+ /* bne $1 ; LONGJUMP2/(INSN16)
+ j label ; 25_PCREL
+ $1: ; (LABEL)
+ Use 32-bit complimentary branch. */
+
+ if (insn_num == NDS32_INSN_BEQC || insn_num == NDS32_INSN_BNEC)
+ {
+ /* beqc/bnec */
+ size = 4;
+ md_number_to_chars (buf, comp_insn, 4);
+ exp_cond.X_op = O_symbol;
+ exp_cond.X_add_symbol = symbol_temp_new (sec, 0, fragP->fr_next);
+ exp_cond.X_add_number = 0;
+
+ fix_new_exp (fragP, where, 4, &exp_cond, 0,
+ BFD_RELOC_NDS32_WORD_9_PCREL);
+ addend -= 2;
+ }
+ /* Set proper relocation type. */
+ else if (RELAX_USE_32BIT (fragP) || optimize
+ || !is_convertible (fragP))
+ {
+ size = 4;
+ /* Add branch offset. */
+ md_number_to_chars (buf, comp_insn, 4);
+ exp_cond.X_op = O_symbol;
+ exp_cond.X_add_symbol = symbol_temp_new (sec, 0, fragP->fr_next);
+ exp_cond.X_add_number = 0;
+
+ fix_new_exp (fragP, where, 4, &exp_cond, 0,
+ BFD_RELOC_NDS32_15_PCREL);
+ addend -= 2;
+
+ if (!RELAX_USE_32BIT (fragP) && optimize)
+ {
+ if (is_convertible (fragP) && enable_relax_relocs)
+ {
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = 0;
+ fix_new_exp (fragP, where, 4, &exp_relax, 0,
+ BFD_RELOC_NDS32_INSN16);
+ }
+ }
+ }
+ else
+ {
+ size = 2;
+ /* Add branch offset. */
+ md_number_to_chars (buf, comp_insn16, 2);
+ exp_cond.X_op = O_symbol;
+ exp_cond.X_add_symbol = symbol_temp_new (sec, 0, fragP->fr_next);
+ exp_cond.X_add_number = 0;
+ fixP = fix_new_exp (fragP, where, 2, &exp_cond, 0,
+ BFD_RELOC_NDS32_9_PCREL);
+ fixP->fx_no_overflow = 1;
+ addend -= 1;
+ }
+
+ /* Create j. */
+ insn = INSN_J;
+ md_number_to_chars (buf + size, insn, 4);
+ if (pic_code && (S_GET_SEGMENT (fragP->fr_symbol) != sec
+ || S_IS_WEAK (fragP->fr_symbol)))
+ fixP = fix_new_exp (fragP, where + size, 4, &exp, 0,
+ BFD_RELOC_NDS32_25_PLTREL);
+ else
+ fixP = fix_new_exp (fragP, where + size, 4, &exp, 0,
+ BFD_RELOC_NDS32_25_PCREL);
+ fixP->fx_addnumber = fixP->fx_offset;
+ size += 4;
+
+ /* Create a relocation for this sequence, so linker can contract
+ it to smaller size. */
+ if (enable_relax_relocs
+ && !(insn_num == NDS32_INSN_BEQC
+ || insn_num == NDS32_INSN_BNEC))
+ {
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = SET_ADDEND (size, is_convertible (fragP),
+ optimize, !RELAX_USE_32BIT (fragP));
+ fix_new_exp (fragP, where, size, &exp_relax, 0,
+ BFD_RELOC_NDS32_LONGJUMP2);
+ }
+ break;
+
+ case BR_COND_BR_U4G:
+ /* bnes38 $1 ; or bne $1
+ sethi $ta, hi20(label)
+ ori $ta, $ta, lo12(label)
+ jr5 $ta ; or jr $ta
+ $1: */
+
+ if (insn_num == NDS32_INSN_BEQC || insn_num == NDS32_INSN_BNEC)
+ {
+ /* beqc/bnec */
+ size = 4;
+ md_number_to_chars (buf, comp_insn, 4);
+ exp_cond.X_op = O_symbol;
+ exp_cond.X_add_symbol = symbol_temp_new (sec, 0, fragP->fr_next);
+ exp_cond.X_add_number = 0;
+ fix_new_exp (fragP, where, 4, &exp_cond, 0,
+ BFD_RELOC_NDS32_WORD_9_PCREL);
+
+ }
+ if (RELAX_USE_32BIT (fragP) || !is_convertible (fragP))
+ {
+ size = 4;
+ md_number_to_chars (buf, comp_insn, 4);
+ exp_cond.X_op = O_symbol;
+ exp_cond.X_add_symbol = symbol_temp_new (sec, 0, fragP->fr_next);
+ exp_cond.X_add_number = 0;
+ fix_new_exp (fragP, where, 4, &exp_cond, 0,
+ BFD_RELOC_NDS32_15_PCREL);
+ }
+ else
+ {
+ size = 2;
+ md_number_to_chars (buf, comp_insn16, 2);
+ exp_cond.X_op = O_symbol;
+ exp_cond.X_add_symbol = symbol_temp_new (sec, 0, fragP->fr_next);
+ exp_cond.X_add_number = 0;
+ fixP = fix_new_exp (fragP, where, 2, &exp_cond, 0,
+ BFD_RELOC_NDS32_9_PCREL);
+ fixP->fx_no_overflow = 1;
+ }
+
+ /* Create sethi. */
+ insn = INSN_SETHI_TA;
+ md_number_to_chars (buf + size, insn, 4);
+ fixP = fix_new_exp (fragP, where + size, 4, &exp, 0,
+ BFD_RELOC_NDS32_HI20);
+ fixP->fx_addnumber = fixP->fx_offset;
+ size += 4;
+
+ /* Create ori. */
+ insn = INSN_ORI_TA;
+ md_number_to_chars (buf + size, insn, 4);
+ if (enable_relax_relocs)
+ fixP = fix_new_exp (fragP, where + size, 4, &exp, 0,
+ BFD_RELOC_NDS32_LO12S0_ORI);
+ else
+ fixP = fix_new_exp (fragP, where + size, 4, &exp, 0,
+ BFD_RELOC_NDS32_LO12S0);
+ fixP->fx_addnumber = fixP->fx_offset;
+ size += 4;
+
+ if (RELAX_USE_32BIT (fragP)
+ || (!is_convertible (fragP) && optimize))
+ {
+ /* 16-bit off or optimize for speed and first instruction is not
+ convertible; use jral. */
+ insn = INSN_JR_TA;
+ md_number_to_chars (buf + size, insn, 4);
+ if (!RELAX_USE_32BIT (fragP) && optimize && enable_relax_relocs)
+ {
+ /* 16-bit instruction fixup, so we can expand it to align
+ next label if necessary. If this instruction is
+ expanded in linker the offset must be incremented by 1. */
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = 0;
+ fix_new_exp (fragP, where + size, 4, &exp_relax, 0,
+ BFD_RELOC_NDS32_INSN16);
+
+ /* An invisible label relocation should have been created in
+ calc_branch_seq_size. */
+ }
+ size += 4;
+ }
+ else
+ {
+ /* use jr5 */
+ insn16 = INSN_JR5_TA;
+ md_number_to_chars (buf + size, insn16, 2);
+ size += 2;
+ }
+
+ if (!(insn_num == NDS32_INSN_BEQC || insn_num == NDS32_INSN_BNEC))
+ {
+ if (enable_relax_relocs)
+ {
+ /* Create a relocation for this sequence, so linker can contract
+ it to smaller size. */
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = SET_ADDEND (size, is_convertible (fragP),
+ optimize, !RELAX_USE_32BIT (fragP));
+ fix_new_exp (fragP, where, size, &exp_relax, 0,
+ BFD_RELOC_NDS32_LONGJUMP3);
+ }
+
+ /* Create label fixup for next frag
+ What's the size of 1st instruction in next frag?
+ The previous 16-bit instruction is 2 instructions above. */
+ if (optimize && enable_relax_relocs)
+ {
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = 2;
+ fix_new_exp (fragP->fr_next, 0,
+ 0, &exp_relax, 0, BFD_RELOC_NDS32_LABEL);
+ }
+ }
+ break;
+ }
+ }
+ else if (RELAX_RELAXABLE (fragP))
+ {
+ org_size = 2;
+ if (RELAX_RELAXED (fragP))
+ {
+ /* Convert from 16-bit to 32. Since this instruction is 32-bit,
+ do not remove LABEL relocation if exists. */
+ if (!convert_16_to_32 (fragP, &insn))
+ as_fatal ("Unable to convert instruction 0x%04x from 16-bit to 32-bit",
+ (unsigned int) bfd_getb16 (fragP->fr_literal + fragP->fr_fix - 2));
+
+ size = 4;
+ md_number_to_chars (buf, insn, 4);
+ if (!RELAX_USE_32BIT (fragP) && optimize && enable_relax_relocs)
+ {
+ /* 16-bit instruction fixup, so we can contract it to align next
+ label if necessary. if this instruction is expanded in linker
+ the offset must be incremented by 1. */
+ exp_relax.X_op = O_symbol;
+ exp_relax.X_add_symbol = abs_section_sym;
+ exp_relax.X_add_number = 0;
+ fix_new_exp (fragP, where, 4, &exp_relax, 0,
+ BFD_RELOC_NDS32_INSN16);
+ }
+ }
+ else
+ {
+ size = 2;
+ }
+ }
+ else
+ size = org_size;
+
+ fragP->fr_fix += size - org_size;
+
+ target_big_endian = saved_endian;
+
+}
+
+/* MD_PCREL_FROM_SECTION */
+
+long
+nds32_pcrel_from_section (fixS *fixP, segT sec)
+{
+ if (fixP->fx_addsy != (symbolS *) NULL
+ && (!S_IS_DEFINED (fixP->fx_addsy)
+ || S_GET_SEGMENT (fixP->fx_addsy) != sec
+ || S_IS_EXTERNAL (fixP->fx_addsy) || S_IS_WEAK (fixP->fx_addsy)))
+ {
+ if (S_GET_SEGMENT (fixP->fx_addsy) != sec
+ && S_IS_DEFINED (fixP->fx_addsy)
+ && !S_IS_EXTERNAL (fixP->fx_addsy) && !S_IS_WEAK (fixP->fx_addsy))
+ return fixP->fx_offset;
+
+ /* The symbol is undefined (or is defined but not in this section).
+ Let the linker figure it out. */
+ return 0;
+ }
+
+ return (fixP->fx_frag->fr_address + fixP->fx_where) & -2L;
+}
+
+/* This function returns the bfd reloc type for OPERAND of INSN at fixup FIXP.
+ It returns BFD_RELOC_NONE if no reloc type can be found. *FIXP may be
+ modified if desired. */
+
+bfd_reloc_code_real_type
+md_cgen_lookup_reloc (const CGEN_INSN *insn,
+ const CGEN_OPERAND *operand, fixS *fixP)
+{
+ switch (operand->type)
+ {
+ case NDS32_OPERAND_DISP16:
+ return BFD_RELOC_NDS32_17_PCREL;
+ case NDS32_OPERAND_DISP24:
+ return BFD_RELOC_NDS32_25_PCREL;
+ case NDS32_OPERAND_DISP14:
+ return BFD_RELOC_NDS32_15_PCREL;
+ case NDS32_OPERAND_DISP8:
+ if (CGEN_INSN_BITSIZE (insn) == 32)
+ return BFD_RELOC_NDS32_WORD_9_PCREL;
+ else
+ return BFD_RELOC_NDS32_9_PCREL;
+ case NDS32_OPERAND_UHI20:
+ case NDS32_OPERAND_ULO15D:
+ case NDS32_OPERAND_ULO15W:
+ case NDS32_OPERAND_ULO15H:
+ case NDS32_OPERAND_ULO15B:
+ case NDS32_OPERAND_ULO15:
+ case NDS32_OPERAND_SLO15D:
+ case NDS32_OPERAND_SLO15W:
+ case NDS32_OPERAND_SLO15H:
+ case NDS32_OPERAND_SLO15B:
+ case NDS32_OPERAND_SLO15:
+ case NDS32_OPERAND_SLO12W:
+ case NDS32_OPERAND_SLO12D:
+ case NDS32_OPERAND_SLO17W:
+ case NDS32_OPERAND_SLO18H:
+ case NDS32_OPERAND_SLO19:
+ /* If low/high/shigh/sda was used, it is recorded in `opinfo'. */
+ if (fixP->fx_cgen.opinfo != 0)
+ return fixP->fx_cgen.opinfo;
+ break;
+ default:
+ /* Avoid -Wall warning. */
+ break;
+ }
+
+ return BFD_RELOC_NONE;
+}
+
+/* This function records a HI20 reloc for later matching with its
LO12 cousin. */
+
+static void
+nds32_record_hi20 (int reloc_type, fixS *fixP, segT seg ATTRIBUTE_UNUSED)
+{
+ struct nds32_hi_fixup *hi_fixup;
+
+ gas_assert (reloc_type == BFD_RELOC_NDS32_HI20);
+
+ hi_fixup = ((struct nds32_hi_fixup *)
+ xmalloc (sizeof (struct nds32_hi_fixup)));
+ hi_fixup->fixp = fixP;
+ hi_fixup->seg = now_seg;
+ hi_fixup->next = nds32_hi_fixup_list;
+
+ nds32_hi_fixup_list = hi_fixup;
+}
+
+#define GOT_NAME "_GLOBAL_OFFSET_TABLE_"
+symbolS *GOT_symbol;
+
+/* This function checks PIC attribute. */
+
+static inline int
+nds32_PIC_related_p (symbolS *sym)
+{
+ expressionS *exp;
+
+ if (!sym)
+ return 0;
+
+ if (sym == GOT_symbol)
+ return 1;
+
+ exp = symbol_get_value_expression (sym);
+
+ return (exp->X_op == O_PIC_reloc
+ || exp->X_md == BFD_RELOC_NDS32_25_PLTREL
+ || exp->X_md == BFD_RELOC_NDS32_PLTREL_HI20
+ || exp->X_md == BFD_RELOC_NDS32_PLTREL_LO12
+ || exp->X_md == BFD_RELOC_NDS32_PLT_GOTREL_HI20
+ || exp->X_md == BFD_RELOC_NDS32_PLT_GOTREL_LO12
+ || nds32_PIC_related_p (exp->X_add_symbol)
+ || nds32_PIC_related_p (exp->X_op_symbol));
+}
+
+/* This function checks the fixup. */
+
+static inline int
+nds32_check_fixup (expressionS *main_exp,
+ bfd_reloc_code_real_type *r_type_p)
+{
+ expressionS *exp = main_exp;
+
+ if (exp->X_op == O_add && nds32_PIC_related_p (exp->X_op_symbol))
+ return 1;
+
+ if (exp->X_op == O_symbol && exp->X_add_symbol)
+ {
+ if (exp->X_add_symbol == GOT_symbol)
+ {
+ *r_type_p = BFD_RELOC_NDS32_GOTPC20;
+ return 0;
+ }
+ }
+ else if (exp->X_op == O_add)
+ {
+ exp = symbol_get_value_expression (exp->X_add_symbol);
+ if (!exp)
+ return 0;
+ }
+
+ if (exp->X_op == O_PIC_reloc || exp->X_md != BFD_RELOC_UNUSED)
+ {
+ *r_type_p = exp->X_md;
+ if (exp == main_exp)
+ exp->X_op = O_symbol;
+ else
+ {
+ main_exp->X_add_symbol = exp->X_add_symbol;
+ main_exp->X_add_number += exp->X_add_number;
+ }
+ }
+ else
+ return (nds32_PIC_related_p (exp->X_add_symbol)
+ || nds32_PIC_related_p (exp->X_op_symbol));
+
+ return 0;
+}
+
+/* md_cgen_record_fixup_exp */
+
+fixS *
+nds32_cgen_record_fixup_exp (fragS *frag,
+ int where,
+ const CGEN_INSN *insn,
+ int length,
+ const CGEN_OPERAND *operand,
+ int opinfo, expressionS *exp)
+{
+ fixS *fixP;
+ bfd_reloc_code_real_type r_type = BFD_RELOC_UNUSED;
+
+ if (nds32_check_fixup (exp, &r_type))
+ as_bad (_("Invalid PIC expression."));
+
+ fixP = gas_cgen_record_fixup_exp (frag, where, insn, length,
+ operand, opinfo, exp);
+
+ switch (operand->type)
+ {
+ case NDS32_OPERAND_UHI20:
+ /* If low/high/shigh/sda was used, it is recorded in `opinfo'. */
+ if (fixP->fx_cgen.opinfo == BFD_RELOC_NDS32_HI20)
+ nds32_record_hi20 (fixP->fx_cgen.opinfo, fixP, now_seg);
+ break;
+ case NDS32_OPERAND_SLO17W:
+ /* jasonwu:
+ In order to make fp-as-gp work, two relocations are required:
+ 1. BFD_RELOC_NDS32_SDA17S2
+ 2. BFD_RELOC_NDS32_INSN16
+ For lwi.gp/swi.gp cases, BFD_RELOC_NDS32_SDA17S2 already exists.
+ We just need to create BFD_RELOC_NDS32_INSN16 relocation field
+ so that the link time optimization may have chance to do fp-as-gp. */
+ if (fixP->fx_cgen.opinfo == BFD_RELOC_NDS32_SDA17S2)
+ fix_new_exp (frag, where, 4, exp, 0, BFD_RELOC_NDS32_INSN16);
+ break;
+ default:
+ /* Avoid -Wall warning. */
+ break;
+ }
+
+ switch (r_type)
+ {
+ default:
+ case BFD_RELOC_UNUSED:
+ return fixP;
+ case BFD_RELOC_NDS32_GOTPC20:
+ if (fixP->fx_cgen.opinfo == BFD_RELOC_NDS32_HI20)
+ r_type = BFD_RELOC_NDS32_GOTPC_HI20;
+ else if (fixP->fx_cgen.opinfo == BFD_RELOC_NDS32_LO12S0)
+ r_type = BFD_RELOC_NDS32_GOTPC_LO12;
+ break;
+ case BFD_RELOC_NDS32_GOT20:
+ if (fixP->fx_cgen.opinfo == BFD_RELOC_NDS32_HI20)
+ r_type = BFD_RELOC_NDS32_GOT_HI20;
+ else if (fixP->fx_cgen.opinfo == BFD_RELOC_NDS32_LO12S0)
+ r_type = BFD_RELOC_NDS32_GOT_LO12;
+ break;
+ case BFD_RELOC_NDS32_GOTOFF:
+ if (fixP->fx_cgen.opinfo == BFD_RELOC_NDS32_HI20)
+ r_type = BFD_RELOC_NDS32_GOTOFF_HI20;
+ else if (fixP->fx_cgen.opinfo == BFD_RELOC_NDS32_LO12S0)
+ r_type = BFD_RELOC_NDS32_GOTOFF_LO12;
+ break;
+ case BFD_RELOC_NDS32_25_PLTREL:
+ if (!pic_code)
+ {
+ as_bad (_("Invalid PIC expression."));
+ }
+ else
+ {
+ if (fixP->fx_cgen.opinfo == BFD_RELOC_NDS32_HI20)
+ r_type = BFD_RELOC_NDS32_PLT_GOTREL_HI20;
+ else if (fixP->fx_cgen.opinfo == BFD_RELOC_NDS32_LO12S0)
+ r_type = BFD_RELOC_NDS32_PLT_GOTREL_LO12;
+ }
+
+ break;
+ }
+
+ fixP->fx_r_type = r_type;
+
+ return fixP;
+}
+
+/* Return BFD reloc type from opinfo field in a fixS.
+ It's tricky using fx_r_type in nds32_frob_file_before_fix because the values
+ are BFD_RELOC_UNUSED + operand number. */
+
+#define FX_OPINFO_R_TYPE(f) ((f)->fx_cgen.opinfo)
+
+/* tc_frob_file_before_fix */
+
+void
+nds32_frob_file_before_fix (void)
+{
+ struct nds32_hi_fixup *l;
+
+ for (l = nds32_hi_fixup_list; l != NULL; l = l->next)
+ {
+ segment_info_type *seginfo;
+ int pass;
+
+ gas_assert (FX_OPINFO_R_TYPE (l->fixp) == BFD_RELOC_NDS32_HI20);
+
+ /* Check quickly whether the next fixup happens to be a matching low. */
+ if (l->fixp->fx_next != NULL
+ && (FX_OPINFO_R_TYPE (l->fixp->fx_next) == BFD_RELOC_NDS32_LO12S3
+ || FX_OPINFO_R_TYPE (l->fixp->fx_next) == BFD_RELOC_NDS32_LO12S2
+ || FX_OPINFO_R_TYPE (l->fixp->fx_next) == BFD_RELOC_NDS32_LO12S2_DP
+ || FX_OPINFO_R_TYPE (l->fixp->fx_next) == BFD_RELOC_NDS32_LO12S2_SP
+ || FX_OPINFO_R_TYPE (l->fixp->fx_next) == BFD_RELOC_NDS32_LO12S1
+ || FX_OPINFO_R_TYPE (l->fixp->fx_next) == BFD_RELOC_NDS32_LO12S0
+ || FX_OPINFO_R_TYPE (l->fixp->fx_next) == BFD_RELOC_NDS32_LO12S0_ORI)
+ && l->fixp->fx_addsy == l->fixp->fx_next->fx_addsy
+ && l->fixp->fx_offset == l->fixp->fx_next->fx_offset)
+ continue;
+
+ /* Look through the fixups for this segment for a matching `low'.
+ When we find one, move the high/shigh just in front of it. We do
+ this in two passes. In the first pass, we try to find a
+ unique `low'. In the second pass, we permit multiple high's
+ relocs for a single `low'. */
+ seginfo = seg_info (l->seg);
+ for (pass = 0; pass < 2; pass++)
+ {
+ fixS *f;
+ fixS *prev = NULL;
+
+ for (f = seginfo->fix_root; f != NULL; f = f->fx_next)
+ {
+ /* Check whether this is a `low' fixup which matches l->fixp. */
+ if ((FX_OPINFO_R_TYPE (f) == BFD_RELOC_NDS32_LO12S3
+ || FX_OPINFO_R_TYPE (f) == BFD_RELOC_NDS32_LO12S2
+ || FX_OPINFO_R_TYPE (f) == BFD_RELOC_NDS32_LO12S1
+ || FX_OPINFO_R_TYPE (f) == BFD_RELOC_NDS32_LO12S0
+ || FX_OPINFO_R_TYPE (f) == BFD_RELOC_NDS32_LO12S0_ORI)
+ && f->fx_addsy == l->fixp->fx_addsy
+ && f->fx_offset == l->fixp->fx_offset
+ && (pass == 1
+ || prev == NULL
+ || (FX_OPINFO_R_TYPE (prev) != BFD_RELOC_NDS32_HI20)
+ || prev->fx_addsy != f->fx_addsy
+ || prev->fx_offset != f->fx_offset))
+ {
+ fixS **pf;
+
+ /* Move l->fixp before f. */
+ for (pf = &seginfo->fix_root; *pf != l->fixp; pf = &(*pf)->fx_next)
+ gas_assert (*pf != NULL);
+
+ *pf = l->fixp->fx_next;
+
+ l->fixp->fx_next = f;
+ if (prev == NULL)
+ seginfo->fix_root = l->fixp;
+ else
+ prev->fx_next = l->fixp;
+
+ break;
+ }
+
+ prev = f;
+ }
+
+ if (f != NULL)
+ break;
+
+ if (pass == 1 && warn_unmatched_high)
+ as_warn_where (l->fixp->fx_file, l->fixp->fx_line,
+ _("Unmatched high/shigh reloc"));
+ }
+ }
+}
+
+static bfd_boolean
+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;
+
+ /* COLE: 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);
+}
+
+/* Equal to MAX_PRECISION in atof-ieee.c. */
+#define MAX_LITTLENUMS 6
+
+/* This function is called to convert an ASCII string into a floating point
+ value in format used by the CPU. */
+
+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;
+}
+
+
+/* Relaxation zone begin. */
+
+#define NDS32_GOT_MASK 1
+#define NDS32_GOTOFF_MASK 2
+#define NDS32_PLT_GOT_MASK 3
+#define NDS32_MULCALL_MASK 4
+
+extern int size_inc_line_addr (int, addressT);
+
+static int analysis_rec_bb (nds32_insn_instant *, basic_block *, int, int);
+static int analysis_rec (nds32_insn_instant *, basic_block *, int, int);
+static int life_time_analysis_ls_succ (nds32_insn_instant *);
+void nds32_set_section_relocs (asection *, arelent **, unsigned int);
+
+static int ld_st_address_len_type;
+static int current_reloc_type_mask;
+static symbolS *current_trace_sym;
+static valueT current_trace_addend;
+static int prev_insn_num;
+
+static int
+get_sym_addend_from_insn (nds32_insn_instant *insn_p, symbolS ** sym,
+ valueT *addend)
+{
+ int i;
+
+ if (insn_p->md_relax)
+ {
+ /* fixups[i] not exists && md_relax
+ For those instructions with machine specific relaxations,
+ we can only get the target through the info stored in the frag. */
+ if (S_GET_SEGMENT (insn_p->frag->fr_symbol) == expr_section)
+ {
+ *sym = NULL;
+ *addend = 0;
+ }
+ else
+ {
+ *sym = insn_p->frag->fr_symbol;
+ *addend = insn_p->frag->fr_offset;
+ }
+ i = insn_p->num_fixups;
+ }
+ else
+ {
+ for (i = 0; i < insn_p->num_fixups; i++)
+ if (insn_p->fixups[i]->fx_addsy != NULL)
+ break;
+
+ /* The basic method to get branch target is get them from fixups. */
+ if (i != insn_p->num_fixups && insn_p->fixups[i]->fx_subsy == NULL)
+ {
+ *sym = insn_p->fixups[i]->fx_addsy;
+ *addend = insn_p->fixups[i]->fx_offset;
+ }
+ else
+ {
+ *sym = NULL;
+ *addend = 0;
+ }
+ }
+ return i;
+}
+
+static void
+check_label_belong_to_wrong_bb (void)
+{
+ basic_block *bb;
+ insn_label_list *sym_list;
+
+ bb = bb_list;
+ while (bb)
+ {
+ sym_list = bb->label_list;
+ while (sym_list)
+ {
+ if (bb->now_seg != S_GET_SEGMENT (sym_list->label))
+ as_warn (_("[gas warning] Basic block contains a label"
+ "seats in different section, file: %s, line:%d"),
+ bb->filename, bb->line_num);
+ sym_list = sym_list->next;
+ }
+ bb = bb->list_next;
+ }
+}
+
+static void
+count_bb_known_pred (void)
+{
+ basic_block *bb;
+ int func_mask;
+
+ bb = bb_list;
+
+ while (bb != NULL)
+ {
+ if (bb->cfg_target)
+ bb->cfg_target->known_pred++;
+ if (bb->cfg_next)
+ bb->cfg_next->known_pred++;
+ else
+ {
+ if (bb->insn_tail)
+ {
+ basic_block *ifc_bb;
+ func_mask = is_func_branch (bb->insn_tail->insn_num);
+
+ switch (func_mask)
+ {
+ case IS_FUNC_CALL_MASK:
+ bb->list_next->known_pred++;
+ break;
+ case IS_COND_FUNC_CALL_MASK:
+ ifc_bb = bb->cfg_target;
+
+ while (ifc_bb && ifc_bb->insn_tail)
+ {
+ /* If J is reached, then list_next is unreachable. */
+ func_mask = is_func_branch (ifc_bb->insn_tail->insn_num);
+ ifc_bb->in_common++;
+ switch (func_mask)
+ {
+ case IS_COND_FUNC_CALL_MASK:
+ as_warn (_("Assembler cannot handle nested IFCALL."));
+ case IS_COND_FUNC_RETURN_MASK:
+ case IS_FUNC_CALL_MASK:
+ bb->list_next->known_pred++;
+ goto out_ifret_scanning;
+ default:
+ if (is_conditional_branch_instruction (ifc_bb->insn_tail->insn_num)
+ || is_direct_branch_instruction (ifc_bb->insn_tail->insn_num))
+ goto out_ifret_scanning;
+ break;
+ }
+ ifc_bb = ifc_bb->list_next;
+ }
+out_ifret_scanning:
+ break;
+ }
+ }
+ }
+
+ bb = bb->list_next;
+ }
+}
+
+static void
+establish_label_hash (void)
+{
+ basic_block *bb;
+ insn_label_list *sym_list;
+ asymbol *bfdsym_t;
+ bfdsym_bb_map *bfdsym_bb_map_t, *bfdsym_bb_map_t2;
+
+ bb = bb_list;
+
+ while (bb != NULL)
+ {
+ sym_list = bb->label_list;
+ while (sym_list != NULL)
+ {
+ bfdsym_t = symbol_get_bfdsym (sym_list->label);
+ bfdsym_bb_map_t = (bfdsym_bb_map *) hash_find (bfdsym_hash, bfdsym_t->name);
+ if (!bfdsym_bb_map_t)
+ {
+ bfdsym_bb_map_t = new_bfdsym_bb_map (bb, bfdsym_t, NULL);
+ hash_insert (bfdsym_hash, bfdsym_t->name, bfdsym_bb_map_t);
+ }
+ else
+ {
+ if ((bfdsym_bb_map_t2 = find_bfdsym_bb_map (bfdsym_bb_map_t, bfdsym_t)))
+ {
+ if (bb != bfdsym_bb_map_t2->bb)
+ as_warn (_("[gas warning] Different basic blocks defined "
+ "the same bfd symbol"));
+ }
+ else
+ {
+ bfdsym_bb_map_t = new_bfdsym_bb_map (bb, bfdsym_t, bfdsym_bb_map_t);
+ hash_replace (bfdsym_hash, bfdsym_t->name, bfdsym_bb_map_t);
+ }
+ }
+ sym_list = sym_list->next;
+ }
+ bb = bb->list_next;
+ }
+}
+
+static void
+build_cfg (void)
+{
+ basic_block *bb, *bb2, *bb_next;
+ int br_type;
+ valueT addend;
+ nds32_insn_instant *insn_s;
+ symbolS *sym;
+ bfdsym_bb_map *bfdsym_bb_map_t;
+
+ bb = bb_list;
+
+ check_label_belong_to_wrong_bb ();
+ establish_label_hash ();
+
+ while (bb != NULL)
+ {
+ insn_s = bb->insn_tail;
+ if (insn_s == NULL)
+ {
+ bb = bb->list_next;
+ continue;
+ }
+
+ /* Get the closest next basic block in the same section. */
+ bb2 = bb->list_next;
+ while (bb2 != NULL && bb2->now_seg != bb->now_seg)
+ bb2 = bb2->list_next;
+ bb_next = bb2;
+
+ br_type = is_direct_branch_instruction (insn_s->insn_num);
+ switch (br_type)
+ {
+ case INDIRECT_BRANCH_MASK:
+ br_type = is_conditional_branch_instruction (insn_s->insn_num);
+ if (br_type == CONDITIONAL_BRANCH_MASK)
+ bb->cfg_next = bb_next;
+ else
+ bb->cfg_next = NULL;
+ bb->cfg_target = bb_indirect;
+ break;
+ case DIRECT_BRANCH_MASK:
+ br_type = is_conditional_branch_instruction (insn_s->insn_num);
+ if (br_type == CONDITIONAL_BRANCH_MASK)
+ bb->cfg_next = bb_next;
+ else
+ bb->cfg_next = NULL;
+
+ /* Check if there were non-null subsy. */
+
+ get_sym_addend_from_insn (insn_s, &sym, &addend);
+ if (sym == NULL)
+ {
+ bb->cfg_target = bb_indirect;
+ break;
+ }
+
+ if ((bfdsym_bb_map_t = (bfdsym_bb_map *) hash_find (bfdsym_hash,
+ symbol_get_bfdsym (sym)->name)))
+ {
+ if ((bfdsym_bb_map_t = find_bfdsym_bb_map (bfdsym_bb_map_t,
+ symbol_get_bfdsym (sym))))
+ {
+ bb->cfg_target = bfdsym_bb_map_t->bb;
+ }
+ }
+
+ /* A function call to external function
+ is considered as indirect branch. */
+ if (bb->cfg_target == NULL)
+ bb->cfg_target = bb_indirect;
+ break;
+ default:
+ bb->cfg_next = bb_next;
+ }
+ bb = bb->list_next;
+ }
+}
+
+/* Build def-use information in terms of a single basic block. */
+
+static void
+def_use_analysis (void)
+{
+ basic_block *bb = bb_list;
+ nds32_insn_instant *insn;
+ dep_node *dep;
+ nds32_insn_instant *set_gr[32];
+ nds32_insn_instant *use_gr[32];
+
+ while (bb != NULL)
+ {
+ memset (set_gr, 0, 32 * sizeof (nds32_insn_instant *));
+ memset (use_gr, 0, 32 * sizeof (nds32_insn_instant *));
+ insn = bb->insn_head;
+ while (insn != NULL && insn != bb->insn_tail->next)
+ {
+ dep = insn->gr_dep_list;
+ while (dep != NULL)
+ {
+ if (dep->flag & (short) CGEN_OPINST_INPUT_MASK)
+ {
+ use_gr[dep->reg_num] = insn;
+ if (set_gr[dep->reg_num] == NULL
+ && bb->use_before_set_gr[dep->reg_num] == NULL)
+ bb->use_before_set_gr[dep->reg_num] = insn;
+ }
+ dep = dep->next;
+ }
+
+ /* FIXME: Please review set_before_use.
+ case 1 case 2
+ addi $r3, $r4, 0 addi $r4, $r3, 0
+ addi $r4, $r3, 0 addi $r3, $r4, 0
+ Both cases are not set_before_use,
+ so I think either the name is misleading,
+ or this is a bug. */
+
+ dep = insn->gr_dep_list;
+ while (dep != NULL)
+ {
+ if (dep->flag & (short) CGEN_OPINST_OUTPUT_MASK)
+ {
+ set_gr[dep->reg_num] = insn;
+ if (use_gr[dep->reg_num] == NULL
+ && bb->set_before_use_gr[dep->reg_num] == NULL)
+ bb->set_before_use_gr[dep->reg_num] = insn;
+ }
+ dep = dep->next;
+ }
+ insn = insn->next;
+ }
+ bb = bb->list_next;
+ }
+}
+
+static int
+check_insn_output_reg_valid (dep_node *node_p)
+{
+ int regT = -1;
+ while (node_p != NULL)
+ {
+ if (node_p->flag & (short) CGEN_OPINST_OUTPUT_MASK)
+ {
+ regT = node_p->reg_num;
+ break;
+ }
+ node_p = node_p->next;
+ }
+ if (regT < 0 || regT > 31)
+ {
+ as_bad ("CFG : invalid register number $rt of sethi\n");
+ abort ();
+ }
+ return regT;
+}
+
+/* This function is used to check if a register is used or defined by
a instruction.
+ It can only check one flag at a time. */
+
+static bfd_boolean
+find_reg_in_insn (dep_node *node_p, short reg_t, short flag_t)
+{
+ dep_node *node_t = node_p;
+
+ while (node_t != NULL)
+ {
+ if (node_t->reg_num == reg_t && (node_t->flag |= flag_t > 0))
+ return TRUE;
+ node_t = node_t->next;
+ }
+ return FALSE;
+}
+
+static void
+insert_relax_type (nds32_insn_instant *insn_p, int reloc_type, int data_size,
+ int arg, expressionS *exp_p)
+{
+ expressionS exp;
+ fragS *fragP;
+ fixS *fixP;
+ int where;
+
+ frchain_now = insn_p->parent->frchain_now;
+ fragP = insn_p->frag;
+ where = (insn_p->addr - insn_p->frag->fr_literal) / OCTETS_PER_BYTE;
+
+ /* Place a relocation data here. */
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = abs_section_sym;
+ exp.X_add_number = 0;
+
+
+ switch (reloc_type)
+ {
+ case BFD_RELOC_NDS32_LOADSTORE:
+ exp.X_add_number = SET_ADDEND (4, is_convertible (insn_p->frag),
+ optimize, !RELAX_USE_32BIT (fragP));
+ exp.X_add_number |= ((arg & 0x3f) << 8);
+ fixP = fix_new_exp (insn_p->frag, where, data_size, &exp, 0, reloc_type);
+ break;
+ case BFD_RELOC_NDS32_RELAX_ENTRY:
+ case BFD_RELOC_NDS32_PTR_COUNT:
+ exp.X_add_number = arg;
+ fixP = fix_new_exp (insn_p->frag, where, 0, &exp, 0, reloc_type);
+ fixP->fx_no_overflow = 1;
+ break;
+ case BFD_RELOC_NDS32_PTR:
+ fixP = fix_new_exp (insn_p->frag, where, 0, exp_p, 0, reloc_type);
+ fixP->fx_no_overflow = 1;
+ break;
+ case BFD_RELOC_NDS32_GOT_SUFF:
+ case BFD_RELOC_NDS32_GOTOFF_SUFF:
+ case BFD_RELOC_NDS32_PLT_GOT_SUFF:
+ fixP = fix_new_exp (insn_p->frag, where, data_size, exp_p, 0,
reloc_type);
+ fixP->fx_no_overflow = 1;
+ break;
+ case BFD_RELOC_NDS32_MULCALL_SUFF:
+ fixP = fix_new_exp (insn_p->frag, where, data_size, exp_p, 0,
reloc_type);
+ fixP->fx_no_overflow = 1;
+ if (insn_p->insn_num == NDS32_INSN_ORI
+ || insn_p->insn_num == NDS32_INSN_MOV55)
+ fixP->fx_addnumber = 1;
+ break;
+ default:
+ if (!exp_p)
+ fixP = fix_new_exp (insn_p->frag, where, data_size, &exp, 0, reloc_type);
+ else
+ fixP = fix_new_exp (insn_p->frag, where, data_size, exp_p, 0, reloc_type);
+ }
+}
+
+/* Make a temp expression for a symbol before making it a fixup. */
+
+static void
+insert_relax_type_sym (nds32_insn_instant *insn_p, int reloc_type,
+ int data_size, symbolS *symp, int addnumber)
+{
+ expressionS exp_t;
+
+ exp_t.X_op = O_symbol;
+ exp_t.X_add_symbol = symp;
+ exp_t.X_add_number = addnumber;
+ insert_relax_type (insn_p, reloc_type, data_size, 0, &exp_t);
+}
+
+/* Give instruction insn_p an NDS32_PTR reloc for each
+ instruction stored in the list insn_list_p. */
+
+static void
+insert_nds32_fix_reloc_node (reloc_tree_node *reloc_node_p,
+ reloc_tree_node *reloc_node_q, int reloc_type)
+{
+ symbolS *sym_t;
+ nds32_insn_instant *insn_t = reloc_node_q->insn, *insn_root =
+ reloc_node_p->insn;
+
+ sym_t = symbol_temp_new (insn_t->parent->now_seg,
+ insn_t->addr - insn_t->frag->fr_literal, insn_t->frag);
+ insert_relax_type_sym (insn_root, reloc_type, 4, sym_t, 0);
+}
+
+static void
+ld_st_data_size (nds32_insn_instant *insn_p)
+{
+ switch (insn_p->insn_num)
+ {
+ case NDS32_INSN_LBI:
+ case NDS32_INSN_SBI:
+ case NDS32_INSN_LBSI:
+ case NDS32_INSN_LB:
+ case NDS32_INSN_LBS:
+ case NDS32_INSN_SB:
+ ld_st_address_len_type |= 0x01;
+ break;
+ case NDS32_INSN_LHI:
+ case NDS32_INSN_SHI:
+ case NDS32_INSN_LHSI:
+ case NDS32_INSN_LH:
+ case NDS32_INSN_LHS:
+ case NDS32_INSN_SH:
+ ld_st_address_len_type |= 0x02;
+ break;
+ case NDS32_INSN_LWI:
+ case NDS32_INSN_SWI:
+ case NDS32_INSN_LW:
+ case NDS32_INSN_SW:
+ ld_st_address_len_type |= 0x04;
+ break;
+ case NDS32_INSN_FLSI:
+ case NDS32_INSN_FSSI:
+ ld_st_address_len_type |= 0x08;
+ break;
+ case NDS32_INSN_FLDI:
+ case NDS32_INSN_FSDI:
+ ld_st_address_len_type |= 0x10;
+ break;
+ case NDS32_INSN_ORI:
+ ld_st_address_len_type |= 0x20;
+ break;
+ default:
+ /* Do nothing. (-Wswitch). */
+ break;
+ }
+ return;
+}
+
+/* Control some relaxation based on USE-DEF pair.
+ Return TRUE for reject. */
+
+static int
+relax_barrier (nds32_insn_instant *insn_def, nds32_insn_instant *insn_use)
+{
+ /* This function acts as a barrier for DEF-USE across common blokc.
+ In general, this is not allowed, because a USE in common block
+ may have multiple reaching definition from precessors, and
+ a DEF in common will be used for all its successors.
+
+ D0 D1 D2
+ \ | /
+ USE (common) DEF (common)
+ / | \ / | \
+ U0 U1 U2
+
+ Besides, in C++, the edge to catch-block is invisible,
+ so we should avoid relax la-bral to jal for correctness.
+ A
+ try:
+ B (throw)
+ goto out
+ catch:
+ C <-- invisible edge from B to C
+ out:
+ D
+
+ However, we keep the definition of insn_p (e.g., SETHI) in common block,
+ ONLY if we cannot find an ORI to relax in the same common block.
+
+ I handle it this way because linker always relax ORI-LO12 to ADDI.GP.
+ If we keep both SETHI and ORI untouched, they becomes a strange
code pattern.
+ For example,
+
+ sethi $r6, hi20(foo) => sethi $r6, hi20(foo)
+ ori $r6, $r6, lo12(foo) addi.gp $r6, foo
+
+ case 1 - Not found. Keep SETHI
+
+ .Lcommon:
+ sethi $r6, hi20(foo) => sethi $r6, hi20(foo)
+ ifret ireft
+
+ case 0 - Found ORI. Relax it.
+
+ .Lcommon:
+ sethi $r6, hi20(foo)
+ ori $r6, $r6, lo12(foo) => addi.gp $r6, foo
+ ifret ireft
+ */
+
+ static int is_cpp = -1;
+
+ if (is_cpp == -1)
+ is_cpp = bfd_get_section_by_name (stdoutput, ".gcc_except_table") != NULL;
+
+ switch (insn_def->insn_num)
+ {
+ case NDS32_INSN_SETHI:
+ if (insn_use->insn_num == NDS32_INSN_ORI
+ && insn_use->parent->in_common)
+ return 0;
+ case NDS32_INSN_ORI:
+ if (is_cpp && (insn_use->insn_num == NDS32_INSN_JRAL
+ || insn_use->insn_num == NDS32_INSN_JRAL5))
+ return 1;
+ case NDS32_INSN_JRAL:
+ case NDS32_INSN_JRAL5:
+ default:
+ break;
+ }
+
+ if (insn_def->parent->in_common
+ || insn_use->parent->in_common)
+ return 1;
+
+ return 0;
+}
+
+static int
+trace_bb_def_use (nds32_insn_instant *insn_p, nds32_insn_instant *insn_r,
+ int regT)
+{
+ nds32_insn_instant *insn_s = insn_r;
+ symbolS *sym, *tmp_sym;
+ valueT addend, tmp_addend;
+ short flag;
+ dep_node *node, *node2;
+ reloc_tree_node *reloc_list_t = NULL, *reloc_node_t;
+ int pass_next = 0, node_cond = 0;
+
+ /* Since sethi and ori both have relocation type,
+ we should check if their targets are the same.
+ Here we get the symbol and addend from relocation type of sethi. */
+ get_sym_addend_from_insn (insn_p, &sym, &addend);
+
+ /* COLE: It seems `prev_insn_num' is used to track
+ sethi-ori and sethi-ori-add patterns. */
+
+ while (insn_s != NULL && insn_s != insn_r->parent->insn_tail->next)
+ {
+ node = insn_s->gr_dep_list;
+ pass_next = pass_next ? pass_next - 1 : pass_next;
+ while (node != NULL)
+ {
+ if (node->reg_num == ((short) 0xffff))
+ {
+ if (node->flag & (short) CGEN_OPINST_INPUT_MASK)
+ return CGEN_OPINST_INPUT_MASK;
+ else if (node->flag & (short) CGEN_OPINST_OUTPUT_MASK)
+ return CGEN_OPINST_OUTPUT_MASK;
+ }
+ switch (insn_p->insn_num)
+ {
+ case NDS32_INSN_SETHI:
+ if (node->reg_num == regT)
+ {
+ flag = node->flag;
+ if (flag & (short) CGEN_OPINST_INPUT_MASK)
+ {
+ get_sym_addend_from_insn (insn_s, &tmp_sym, &tmp_addend);
+
+ if (is_lo12_mem_imm_instruction (insn_s->insn_num) > 0)
+ {
+ /* For cases:
+ sethi $rt, hi20(SYM)
+ lwi $rd, $rt, lo12(SYM)
+ Only happened in non-PIC object. */
+
+ if (tmp_sym == NULL
+ || symbol_get_bfdsym (tmp_sym) != symbol_get_bfdsym (sym)
+ || addend != tmp_addend)
+ return CGEN_OPINST_INPUT_MASK;
+ ld_st_data_size (insn_s);
+ insert_relax_type (insn_s, BFD_RELOC_NDS32_INSN16,
+ 4, 0, NULL);
+ }
+ else if (relax_barrier (insn_p, insn_s))
+ return CGEN_OPINST_INPUT_MASK;
+ else if (insn_s->insn_num == NDS32_INSN_ORI)
+ {
+ /* For cases:
+ sethi $rt, hi20(SYM)
+ ori $rd, $rt, lo12(SYM)
+ May happened in both PIC and non-PIC object.
+ For non-PIC object, we only handle sethi/ori pair.
+ For PIC object, we handle much complicate cases. */
+ if (tmp_sym == NULL
+ || symbol_get_bfdsym (tmp_sym) != symbol_get_bfdsym (sym)
+ || addend != tmp_addend)
+ return CGEN_OPINST_INPUT_MASK;
+ ld_st_data_size (insn_s);
+
+ /* Backup needed data of current sethi instruction for further use. */
+ insert_relax_type (insn_s, BFD_RELOC_NDS32_INSN16,
+ 4, 0, NULL);
+ prev_insn_num = NDS32_INSN_SETHI;
+
+ /* Discover dependency of current ORI instruction and
+ followed instructions. life_time_analysis_ori is
+ invoked recursively till the last instruction in
+ the code pattern. */
+ if (current_reloc_type_mask == NDS32_PLT_GOT_MASK
+ || current_reloc_type_mask == 0)
+ reloc_list_t = reloc_list_now;
+ reloc_list_now = reloc_node_t = NULL;
+
+ if (life_time_analysis_ls_succ (insn_s)
+ && reloc_list_now)
+ {
+ reloc_node_t = new_reloc_node_sibling (NULL, insn_s);
+ if (current_reloc_type_mask != NDS32_PLT_GOT_MASK
+ && current_reloc_type_mask != 0)
+ bind_reloc_list_insn (reloc_node_t);
+ bind_reloc_node_child_sibling (reloc_node_t,
+ reloc_list_now);
+ if (current_reloc_type_mask < 4
+ && current_reloc_type_mask > 0)
+ reloc_node_t->type_mask = current_reloc_type_mask;
+ else if (current_reloc_type_mask == 0)
+ reloc_node_t->type_mask = NDS32_MULCALL_MASK;
+ while (reloc_list_now)
+ {
+ bind_reloc_list_insn (reloc_list_now);
+ reloc_list_now = reloc_list_now->sibling;
+ }
+ }
+ else
+ free_reloc_node_sibling (&reloc_list_now);
+
+ if (current_reloc_type_mask == NDS32_PLT_GOT_MASK
+ || current_reloc_type_mask == 0)
+ {
+ if (!reloc_node_t)
+ reloc_node_t = new_reloc_node_sibling (NULL, insn_s);
+ reloc_node_t->sibling = reloc_list_t;
+ reloc_list_now = reloc_node_t;
+ }
+ /* Restore backed-up data. */
+ prev_insn_num = 0;
+ }
+ else
+ {
+ return CGEN_OPINST_INPUT_MASK;
+ /* It is used. Set this sethi none relaxation. */
+ }
+ }
+
+ if (flag & (short) CGEN_OPINST_OUTPUT_MASK)
+ return CGEN_OPINST_OUTPUT_MASK;
+ }
+ break;
+
+ case NDS32_INSN_ORI:
+ if (node->reg_num == regT)
+ {
+ flag = node->flag;
+ if (flag & (short) CGEN_OPINST_INPUT_MASK)
+ {
+ if (prev_insn_num != NDS32_INSN_SETHI)
+ return CGEN_OPINST_INPUT_MASK;
+ else if (relax_barrier (insn_p, insn_s))
+ return CGEN_OPINST_INPUT_MASK;
+ else if (current_reloc_type_mask == NDS32_GOT_MASK)
+ {
+ /* sethi rt1, hi20(sym@GOT)
+ ori rt2, rt1, lo12(sym@GOT)
+ lw rd, [gp + rt2] */
+ if (insn_s->insn_num == NDS32_INSN_LW)
+ {
+ /* If there were no gp encoded in this instruction,
+ this is not a gp-relative access. Skip it. */
+ if (find_reg_in_insn
+ (insn_s->gr_dep_list, (short) REG_GP,
+ (short) CGEN_OPINST_INPUT_MASK))
+ {
+ reloc_list_now = new_reloc_node_sibling (reloc_list_now, insn_s);
+ bb_list_in_critical (nds32_path_bb_list);
+ }
+ else
+ return CGEN_OPINST_INPUT_MASK;
+ }
+ else
+ return CGEN_OPINST_INPUT_MASK;
+ }
+ else if (current_reloc_type_mask == NDS32_GOTOFF_MASK)
+ {
+ /* sethi rt1, hi20(sym@GOTOFF)
+ ori rt2, rt1, lo12(sym@GOTOFF)
+ lw rd, [gp + rt2] */
+ if (is_lo12_mem_reg_instruction (insn_s->insn_num) >
+ 0 || insn_s->insn_num == NDS32_INSN_ADD
+ || insn_s->insn_num == NDS32_INSN_ADD45)
+ {
+ /* If there were no gp encoded in this instruction,
+ this is not a gp-relative access. Skip it. */
+ if (find_reg_in_insn
+ (insn_s->gr_dep_list, (short) REG_GP,
+ (short) CGEN_OPINST_INPUT_MASK))
+ {
+ reloc_list_now = new_reloc_node_sibling (reloc_list_now, insn_s);
+ bb_list_in_critical (nds32_path_bb_list);
+ }
+ else
+ return CGEN_OPINST_INPUT_MASK;
+ }
+ else
+ return CGEN_OPINST_INPUT_MASK;
+ }
+ else if (current_reloc_type_mask == NDS32_PLT_GOT_MASK)
+ {
+ /* Not sethi if ori contains a relocation data. */
+ if (insn_s->insn_num == NDS32_INSN_ADD
+ || insn_s->insn_num == NDS32_INSN_ADD45)
+ {
+ prev_insn_num = NDS32_INSN_ORI;
+
+ reloc_list_t = new_reloc_node_sibling (reloc_list_now, insn_s);
+ reloc_list_now = NULL;
+
+ if (life_time_analysis_ls_succ (insn_s))
+ {
+ bind_reloc_node_child_sibling (reloc_list_t,
+ reloc_list_now);
+ while (reloc_list_now)
+ {
+ bind_reloc_list_insn (reloc_list_now);
+ reloc_list_now = reloc_list_now->sibling;
+ }
+ }
+ else
+ free_reloc_node_sibling (&reloc_list_now);
+ reloc_list_now = reloc_list_t;
+ bb_list_in_critical (nds32_path_bb_list);
+
+ prev_insn_num = NDS32_INSN_SETHI;
+ }
+ else if (insn_s->insn_num == NDS32_INSN_JRAL
+ || insn_s->insn_num == NDS32_INSN_JRAL5)
+ {
+ reloc_list_now = new_reloc_node_sibling (reloc_list_now, insn_s);
+ bb_list_in_critical (nds32_path_bb_list);
+ }
+ else
+ /* FIXME: */
+ return CGEN_OPINST_INPUT_MASK;
+ }
+ else
+ {
+ if (insn_s->insn_num == NDS32_INSN_JRAL
+ || insn_s->insn_num == NDS32_INSN_JRAL5)
+ {
+ /* If there were no gp encoded in this instruction,
+ this is not a gp-relative access. Skip it. */
+ if (!pass_next)
+ {
+ reloc_list_now = new_reloc_node_sibling (reloc_list_now, insn_s);
+ bb_list_in_critical (nds32_path_bb_list);
+ }
+ }
+ /* Extra support for n1212hc case, is this really make sense since
+ the dynamic linked binary may need to update $ta. */
+ else if ((insn_s->insn_num == NDS32_INSN_MOV55
+ || insn_s->insn_num == NDS32_INSN_ORI)
+ && insn_s->next
+ && (insn_s->next->insn_num == NDS32_INSN_JRAL
+ || insn_s->next->insn_num == NDS32_INSN_JRAL5))
+ {
+ node2 = insn_s->gr_dep_list;
+ node_cond = 0;
+ while (node2)
+ {
+ if (node2->reg_num == regT
+ && (node2->flag & (short)
+ CGEN_OPINST_INPUT_MASK))
+ node_cond |= 1;
+ if (node2->reg_num == REG_R15
+ && (node2->flag & (short)
+ CGEN_OPINST_OUTPUT_MASK))
+ node_cond |= 2;
+ node2 = node2->next;
+ }
+ node2 = insn_s->gr_dep_list;
+ while (node2)
+ {
+ if (node2->reg_num == regT
+ && (node2->flag & (short)
+ CGEN_OPINST_INPUT_MASK))
+ break;
+ }
+ if (node_cond == 3 && node2)
+ {
+ reloc_list_now = new_reloc_node_sibling (reloc_list_now, insn_s);
+ bb_list_in_critical (nds32_path_bb_list);
+ /* A two instruction block. */
+ pass_next = 2;
+ }
+ else
+ return CGEN_OPINST_INPUT_MASK;
+ }
+ else
+ return CGEN_OPINST_INPUT_MASK;
+ }
+ }
+
+ if (flag & (short) CGEN_OPINST_OUTPUT_MASK)
+ return CGEN_OPINST_OUTPUT_MASK;
+ }
+ break;
+
+ case NDS32_INSN_ADD:
+ case NDS32_INSN_ADD45:
+ if (node->reg_num == regT)
+ {
+ flag = node->flag;
+ if (flag & CGEN_OPINST_INPUT_MASK)
+ {
+ if (prev_insn_num != NDS32_INSN_ORI)
+ return CGEN_OPINST_INPUT_MASK;
+ else if (current_reloc_type_mask == NDS32_PLT_GOT_MASK
+ && (insn_s->insn_num == NDS32_INSN_JRAL
+ || insn_s->insn_num == NDS32_INSN_JRAL5))
+ {
+ reloc_list_now = new_reloc_node_sibling (reloc_list_now, insn_s);
+ bb_list_in_critical (nds32_path_bb_list);
+ }
+ else
+ return CGEN_OPINST_INPUT_MASK;
+ }
+ if (flag & (short) CGEN_OPINST_OUTPUT_MASK)
+ return CGEN_OPINST_OUTPUT_MASK;
+ }
+ break;
+
+ case NDS32_INSN_INVALID:
+ if (node->reg_num == regT)
+ {
+ flag = node->flag;
+ if (flag & (short) CGEN_OPINST_INPUT_MASK)
+ return CGEN_OPINST_INPUT_MASK;
+ else if (flag & (short) CGEN_OPINST_OUTPUT_MASK)
+ return CGEN_OPINST_OUTPUT_MASK;
+ }
+ break;
+ default:
+ break;
+ }
+ node = node->next;
+ }
+ insn_s = insn_s->next;
+ }
+
+ /* In general, if the DEF-instruction is in common block,
+ it is used by all the possible successor, so we should
+ keep it alive by assuming it it USED.
+ See relax_barrier () for futher information. */
+ if (insn_p->parent->in_common)
+ return CGEN_OPINST_INPUT_MASK;
+
+ return 0;
+}
+
+static int
+analysis_rec_bb (nds32_insn_instant *insn_p, basic_block *bb_p, int regT,
+ int level)
+{
+ int func_mask, used_by_succ = 0;
+
+ func_mask = is_func_branch (bb_p->insn_tail->insn_num);
+ if (func_mask == 0)
+ {
+ used_by_succ = analysis_rec (insn_p, bb_p->cfg_next, regT, level + 1);
+ if (used_by_succ != CGEN_OPINST_INPUT_MASK)
+ {
+ used_by_succ = analysis_rec (insn_p, bb_p->cfg_target, regT, level + 1);
+ }
+ }
+ else if (func_mask == (int) IS_FUNC_RETURN_MASK)
+ {
+ /* Update of register[0~1] can't be suppressed when function return
+ since these registers are used as return value. */
+ if (regT > 1 && regT < 26)
+ return 0;
+ else
+ return CGEN_OPINST_INPUT_MASK;
+ }
+ else if (func_mask == (int) IS_COND_FUNC_RETURN_MASK)
+ {
+ if (!is_conservative_ifc)
+ {
+ if (ifc_mode == 1)
+ {
+ ifc_mode = 0;
+ return 0;
+ }
+ else
+ used_by_succ = analysis_rec (insn_p, bb_p->list_next, regT, level + 1);
+ }
+ else
+ {
+ ifc_mode = 0;
+ return CGEN_OPINST_INPUT_MASK;
+ }
+ }
+ else if (func_mask == (int) IS_COND_FUNC_CALL_MASK)
+ {
+ if (bb_p->insn_tail->insn_num == NDS32_INSN_IFCALL
+ || bb_p->insn_tail->insn_num == NDS32_INSN_IFCALL9)
+ {
+ enum LEAVING_REASON { LRE_NONE, LRE_RET, LRE_NONRET }
leaving_reason = LRE_NONE;
+ basic_block *ifc_bb;
+
+ ifc_mode = 1;
+ used_by_succ = analysis_rec (insn_p, bb_p->cfg_target, regT, level + 1);
+
+ if (used_by_succ != 0)
+ return used_by_succ;
+
+ /* Find the leaving instruction of ifcall.
+ In most cases, it is IFCRET, but some times, it may be J, JAL, etc.
+ If the leaving instruction is not IFCALL and JAL, this IFCALL wouldn't
+ return. */
+ ifc_bb = bb_p->cfg_target;
+ while (leaving_reason == LRE_NONE && ifc_bb && ifc_bb->insn_tail)
+ {
+ func_mask = is_func_branch (ifc_bb->insn_tail->insn_num);
+
+ switch (func_mask)
+ {
+ case IS_COND_FUNC_CALL_MASK:
+ as_warn (_("Assembler cannot handle nested IFCALL."));
+ case IS_COND_FUNC_RETURN_MASK:
+ case IS_FUNC_CALL_MASK:
+ leaving_reason = LRE_RET;
+ break;
+ default:
+ /* case IS_FUNC_RETURN_MASK: */
+ if (is_conditional_branch_instruction (ifc_bb->insn_tail->insn_num)
+ || is_direct_branch_instruction (ifc_bb->insn_tail->insn_num))
+ leaving_reason = LRE_NONRET;
+ break;
+ }
+ ifc_bb = ifc_bb->list_next;
+ }
+
+ if (leaving_reason != LRE_NONRET)
+ {
+ ifc_mode = 0;
+ used_by_succ = analysis_rec (insn_p, bb_p->list_next, regT, level + 1);
+ }
+ }
+ }
+ else if (func_mask == (int) IS_FUNC_CALL_MASK)
+ {
+ /* Update of register[0~5] can't be suppressed when function call since
+ these registers are used as function argument. */
+ if (regT > 5 && regT < 26)
+ used_by_succ = analysis_rec (insn_p, bb_p->list_next, regT, level + 1);
+ else if ((regT < 6 && regT >= 0) && bb_p->insn_tail->hint_func_args
+ && is_hint_func_args (bb_p->insn_tail->hint_func_args, regT))
+ used_by_succ = analysis_rec (insn_p, bb_p->list_next, regT, level + 1);
+ else
+ {
+ if (bb_p->insn_tail->insn_num == NDS32_INSN_JAL)
+ {
+ used_by_succ = analysis_rec (insn_p, bb_p->cfg_target, regT, level + 1);
+ if (used_by_succ == 0)
+ used_by_succ = analysis_rec (insn_p, bb_p->list_next, regT, level + 1);
+ }
+ else
+ return CGEN_OPINST_INPUT_MASK;
+ }
+ }
+
+ return used_by_succ;
+}
+
+static int
+analysis_rec (nds32_insn_instant *insn_p, basic_block *bb_p, int regT,
+ int level)
+{
+ nds32_insn_instant *insn_s;
+ int finish, used_by_succ = 0;
+
+ if (bb_p == NULL)
+ return 0;
+
+ /* Check if current basic block is reentrant. */
+ bb_p->walk_through++;
+ if (bb_p->walk_through > 1)
+ {
+ /* For ifret case. */
+ if (bb_p->walk_through > 1
+ && bb_p->insn_head != NULL
+ && is_func_branch (bb_p->insn_tail->insn_num) == IS_COND_FUNC_RETURN_MASK
+ && !is_conservative_ifc && ifc_mode == 0)
+ return analysis_rec (insn_p, bb_p->cfg_next, regT, level);
+ /* Update those basic blocks visited in this path. */
+ if (bb_p->in_critical)
+ bb_list_in_critical (nds32_path_bb_list);
+ if (ifc_mode == 1
+ && is_func_branch (bb_p->insn_tail->insn_num) == IS_COND_FUNC_RETURN_MASK)
+ ifc_mode = 0;
+ /* Do nothing. */
+ return 0;
+ }
+
+ push_bb_to_bb_list (&nds32_use_bb_list, bb_p);
+
+ /* For indirect branches, we assume it will be always used. */
+ if (level == 0xfffffff || bb_p == bb_indirect)
+ {
+ /* Rufus: Tag redundant sethi if the final insn is function call. */
+ if (regT > 14 && regT < 26)
+ return 0;
+ else
+ return CGEN_OPINST_INPUT_MASK;
+ }
+
+ if (regT < 0 || regT > 31)
+ {
+ as_bad ("CFG : invalid register number of sethi\n");
+ abort ();
+ }
+
+ push_bb_to_bb_list (&nds32_path_bb_list, bb_p);
+
+ /* FIXME: the use_before_set only checks regT but no regTA here,
+ additional regTA checks need to be introduced in the future.
+ (especially for slt - beqz case). */
+ if (bb_p->set_before_use_gr[regT] != NULL)
+ {
+ if (is_func_branch (bb_p->insn_tail->insn_num) ==
IS_COND_FUNC_RETURN_MASK
+ && !is_conservative_ifc && ifc_mode == 1)
+ ifc_mode = 0;
+ pop_bb_from_bb_list (&nds32_path_bb_list);
+ return CGEN_OPINST_OUTPUT_MASK;
+ }
+ else if (bb_p->use_before_set_gr[regT] != NULL)
+ {
+ insn_s = bb_p->use_before_set_gr[regT];
+ }
+ else
+ {
+ if (bb_p->insn_head == NULL)
+ finish = analysis_rec (insn_p, bb_p->cfg_next, regT, level + 1);
+ else
+ finish = analysis_rec_bb (insn_p, bb_p, regT, level);
+ pop_bb_from_bb_list (&nds32_path_bb_list);
+ return finish;
+ }
+
+ /* regT is used before set in this basic block. An instruction by
instruction
+ check can determine which instruction uses or sets it. There may be 3
+ kinds of results:
+ (1) non-relaxable use before set: return CGEN_OPINST_INPUT_MASK
+ (2) relaxable use before set: return CGEN_OPINST_OUTPUT_MASK
+ (3) relaxable use with no set followed: return 0. */
+ finish = trace_bb_def_use (insn_p, insn_s, regT);
+
+ if (!finish)
+ used_by_succ = analysis_rec_bb (insn_p, bb_p, regT, level);
+
+ if (is_func_branch (bb_p->insn_tail->insn_num) ==
+ (int) IS_COND_FUNC_RETURN_MASK && !is_conservative_ifc && ifc_mode == 1)
+ ifc_mode = 0;
+
+ pop_bb_from_bb_list (&nds32_path_bb_list);
+
+ if (!finish)
+ return used_by_succ;
+ else
+ return finish;
+}
+
+static void
+life_time_analysis_sethi (void)
+{
+ nds32_insn_list *insn_list = nds32_hi_insn_list;
+ nds32_insn_instant *insn_s;
+ int regT, finish = 0, used_by_succ = 0, fix_index, gen_tree, root_of_tree;
+ symbolS *sym;
+ valueT addend;
+ reloc_tree_node *tree_node_t;
+
+ while (insn_list != NULL)
+ {
+ ld_st_address_len_type = gen_tree = root_of_tree = 0;
+ finish = used_by_succ = 0;
+ prev_insn_num = 0;
+ nds32_use_bb_list = NULL;
+ insn_s = insn_list->insn;
+ regT = check_insn_output_reg_valid (insn_s->gr_dep_list);
+
+ fix_index = get_sym_addend_from_insn (insn_s, &sym, &addend);
+ if (fix_index == insn_s->num_fixups)
+ {
+ insn_list = insn_list->next;
+ continue;
+ }
+ current_trace_sym = sym;
+ current_trace_addend = addend;
+
+ switch (insn_s->fixups[fix_index]->fx_r_type)
+ {
+ case BFD_RELOC_NDS32_GOT_HI20:
+ current_reloc_type_mask = NDS32_GOT_MASK;
+ break;
+ case BFD_RELOC_NDS32_GOTOFF_HI20:
+ current_reloc_type_mask = NDS32_GOTOFF_MASK;
+ break;
+ case BFD_RELOC_NDS32_PLT_GOTREL_HI20:
+ current_reloc_type_mask = NDS32_PLT_GOT_MASK;
+ break;
+ case BFD_RELOC_NDS32_GOTPC_HI20:
+ insert_relax_type (insn_s, BFD_RELOC_NDS32_LOADSTORE, 4, 0, NULL);
+ insn_list = insn_list->next;
+ continue;
+ default:
+ current_reloc_type_mask = 0;
+ }
+
+ /* If sethi is not the tail instruction of the basic block, trace
+ the basic block it belongs to first. Otherwise, directly trace
+ the fall through basic block and the branch target one. */
+ if (insn_s->next != NULL && insn_s->parent == insn_s->next->parent)
+ finish = trace_bb_def_use (insn_s, insn_s->next, regT);
+ push_bb_to_bb_list (&nds32_use_bb_list, insn_s->parent);
+
+ if (!finish)
+ {
+ used_by_succ = analysis_rec_bb (insn_s, insn_s->parent, regT, 0);
+ if (used_by_succ != CGEN_OPINST_INPUT_MASK)
+ {
+ insert_relax_type (insn_s, BFD_RELOC_NDS32_LOADSTORE, 4,
+ ld_st_address_len_type, NULL);
+ gen_tree = 1;
+ }
+ }
+ else if (finish == CGEN_OPINST_OUTPUT_MASK)
+ {
+ insert_relax_type (insn_s, BFD_RELOC_NDS32_LOADSTORE, 4,
+ ld_st_address_len_type, NULL);
+ gen_tree = 1;
+ }
+ else if (finish == CGEN_OPINST_INPUT_MASK)
+ {
+ /* Sethi will not be set to relaxation. */
+ }
+
+ if (current_reloc_type_mask == NDS32_PLT_GOT_MASK)
+ root_of_tree = 1;
+ else if (current_reloc_type_mask == 0)
+ {
+ tree_node_t = reloc_list_now;
+ while (tree_node_t)
+ {
+ if (!tree_node_t->child)
+ break;
+ tree_node_t = tree_node_t->next;
+ }
+ if (!tree_node_t)
+ root_of_tree = 1;
+ }
+
+ if (gen_tree && root_of_tree && reloc_list_now)
+ {
+ tree_node_t = new_reloc_node_sibling (NULL, insn_s);
+ if (current_reloc_type_mask == NDS32_PLT_GOT_MASK)
+ tree_node_t->type_mask = NDS32_PLT_GOT_MASK;
+ else
+ tree_node_t->type_mask = NDS32_MULCALL_MASK;
+ bind_reloc_list_insn (tree_node_t);
+ bind_reloc_node_child_sibling (tree_node_t, reloc_list_now);
+ while (reloc_list_now)
+ {
+ bind_reloc_list_insn (reloc_list_now);
+ reloc_list_now = reloc_list_now->sibling;
+ }
+ }
+ else
+ free_reloc_node_sibling (&reloc_list_now);
+
+ clean_temp_var_bb_list (nds32_use_bb_list);
+ push_to_bb_list_all (&nds32_use_bb_list, &nds32_free_bb_list);
+ push_to_bb_list_all (&nds32_path_bb_list, &nds32_free_bb_list);
+
+ insn_list = insn_list->next;
+ }
+}
+
+static int
+life_time_analysis_ls_succ (nds32_insn_instant *insn_p)
+{
+ nds32_insn_instant *insn_s;
+ int regT, finish = 0, used_by_succ = 0;
+ nds32_bb_list *bak_path_bb_list = nds32_path_bb_list;
+ nds32_bb_list *bak_use_bb_list = nds32_use_bb_list;
+ int result = TRUE;
+
+ save_temp_var_bb_list (nds32_use_bb_list);
+ clean_temp_var_bb_list (nds32_use_bb_list);
+
+ nds32_use_bb_list = NULL;
+ nds32_path_bb_list = NULL;
+ insn_s = insn_p;
+ regT = check_insn_output_reg_valid (insn_s->gr_dep_list);
+
+ if (insn_s->next != NULL && insn_s->parent == insn_s->next->parent)
+ finish = trace_bb_def_use (insn_s, insn_s->next, regT);
+ push_bb_to_bb_list (&nds32_use_bb_list, insn_s->parent);
+
+ if (!finish)
+ {
+ used_by_succ = analysis_rec_bb (insn_s, insn_s->parent, regT, 0);
+ if (used_by_succ == CGEN_OPINST_INPUT_MASK)
+ result = FALSE;
+ }
+ else if (finish == CGEN_OPINST_OUTPUT_MASK)
+ {
+ }
+ else if (finish == CGEN_OPINST_INPUT_MASK)
+ {
+ result = FALSE;
+ }
+
+ if (insn_p->parent->in_common)
+ result = FALSE;
+
+ if (!check_unknown_source_in_critical (nds32_use_bb_list))
+ result = FALSE;
+
+ /* See bug8315 and 87a1e3015.
+ This line is moved after check_unknown_source_in_critical ().
+ Otherwise, it always fails to relax sethi-ori-jral to jal.
+ This statement was added in 87a1e301 by Rudolph,
+ but I can't figure out why this is required to fix ifc issue.
+
+ Besides, is_conservative_ifc is used by default now. */
+ insn_s->parent->in_critical = 1;
+
+ clean_temp_var_bb_list (nds32_use_bb_list);
+ push_to_bb_list_all (&nds32_use_bb_list, &nds32_free_bb_list);
+ push_to_bb_list_all (&nds32_path_bb_list, &nds32_free_bb_list);
+
+ nds32_path_bb_list = bak_path_bb_list;
+ nds32_use_bb_list = bak_use_bb_list;
+ restore_temp_var_bb_list (nds32_use_bb_list);
+ return result;
+}
+
+static void
+check_invalid_reloc (void)
+{
+ basic_block *bb;
+ nds32_insn_instant *insn_t;
+ reloc_tree_node *reloc_node_t, *reloc_node_t2;
+
+ bb = bb_list;
+
+ while (bb != NULL)
+ {
+ insn_t = bb->insn_head;
+ while (insn_t != NULL && insn_t != bb->insn_tail->next)
+ {
+ if (insn_t->reloc_node != NULL)
+ {
+ reloc_node_t = insn_t->reloc_node;
+ while (reloc_node_t)
+ {
+ reloc_node_t2 = reloc_node_t;
+ reloc_node_t = reloc_node_t->next;
+ if (reloc_node_t != NULL
+ && reloc_node_t2->parent != reloc_node_t->parent)
+ {
+ if (enable_relax_warning)
+ as_warn (_("%s: %d: Duplicate relaxation type found in the same
instruction"),
+ reloc_node_t->insn->frag->fr_file,
+ reloc_node_t->insn->frag->fr_line);
+ reloc_node_t = insn_t->reloc_node;
+ while (reloc_node_t)
+ {
+ taint_reloc_node_tree (reloc_node_t);
+ reloc_node_t = reloc_node_t->next;
+ }
+ break;
+ }
+ }
+ reloc_node_t = insn_t->reloc_node;
+ while (reloc_node_t)
+ {
+ if (reloc_node_t->parent == NULL
+ && reloc_node_t->type_mask == NDS32_MULCALL_MASK
+ && reloc_node_t->leaf_num > relax_jal_bound)
+ taint_reloc_node_tree (reloc_node_t);
+ reloc_node_t = reloc_node_t->next;
+ }
+ }
+ insn_t = insn_t->next;
+ }
+ bb = bb->list_next;
+ }
+}
+
+static void
+reloc_tree_turn_16_to_32_insn (reloc_tree_node *reloc_node_p)
+{
+ reloc_tree_node *reloc_node_t;
+ fragS *frag_t;
+ uint32_t insn;
+ char *buf;
+
+ if (reloc_node_p->tainted)
+ return;
+
+ if (reloc_node_p->child == NULL)
+ {
+ /* Check if leaf node is a relaxable 32 bit instruction
+ or a 16-bit instruction. If yes, turn the instruction
+ to non-relaxable and 32-bit instruction. */
+ if (reloc_node_p->insn->byte_sz == 2
+ && reloc_node_p->insn->insn_sz == 4)
+ {
+ /* (1) Turn 16-bit instruction to 32-bit.
+ (2) Clear Relaxable bit in frag->subtype.
+ (3) Turn frag->type to rs_fill. */
+ frag_t = reloc_node_p->insn->frag;
+ /* Only jral5 converted here so no need to check if it is success. */
+ convert_16_to_32 (frag_t, &insn);
+ buf = frag_t->fr_literal + frag_t->fr_fix - 2;
+ md_number_to_chars (buf, insn, 4);
+ frag_t->fr_fix += 2;
+ RELAX_CLEAR_RELAXABLE (frag_t);
+ frag_t->fr_type = rs_fill;
+ reloc_node_p->convertible = 1;
+ reloc_node_p->insn->byte_sz = 4;
+ }
+ }
+
+ reloc_node_t = reloc_node_p->child;
+ while (reloc_node_t)
+ {
+ reloc_tree_turn_16_to_32_insn (reloc_node_t);
+ reloc_node_t = reloc_node_t->sibling;
+ }
+}
+
+static void
+adjust_reloc_contents (void)
+{
+ basic_block *bb;
+ nds32_insn_instant *insn_t;
+ reloc_tree_node *reloc_node_t;
+
+ bb = bb_list;
+
+ while (bb != NULL)
+ {
+ insn_t = bb->insn_head;
+ while (insn_t != NULL && insn_t != bb->insn_tail->next)
+ {
+ if (insn_t->reloc_node != NULL)
+ {
+ reloc_node_t = insn_t->reloc_node;
+
+ while (reloc_node_t != NULL)
+ {
+ if (reloc_node_t->parent == NULL)
+ reloc_tree_turn_16_to_32_insn (reloc_node_t);
+ reloc_node_t = reloc_node_t->next;
+ }
+ }
+ insn_t = insn_t->next;
+ }
+ bb = bb->list_next;
+ }
+}
+
+static void
+reloc_tree_gen_relocs (reloc_tree_node *reloc_node_p, int reloc_type)
+{
+ reloc_tree_node *reloc_node_t;
+ static int pattern_level = 0;
+ int i;
+
+ if (reloc_node_p->tainted)
+ return;
+
+ if (reloc_node_p->child == NULL)
+ {
+ for (i = 0; i < pattern_level; i++)
+ {
+ insert_nds32_fix_reloc_node (current_pattern[i], reloc_node_p,
+ BFD_RELOC_NDS32_PTR);
+ }
+ insert_relax_type_sym (reloc_node_p->insn, reloc_type,
+ reloc_node_p->insn->byte_sz, current_trace_sym,
+ current_trace_addend);
+ if (reloc_type == BFD_RELOC_NDS32_MULCALL_SUFF
+ && (reloc_node_p->insn->insn_num == NDS32_INSN_MOV55
+ || reloc_node_p->insn->insn_num == NDS32_INSN_ORI))
+ insert_relax_type_sym (reloc_node_p->insn,
+ BFD_RELOC_NDS32_PTR_RESOLVED,
+ reloc_node_p->insn->byte_sz, abs_section_sym,
+ 2);
+ else
+ insert_relax_type_sym (reloc_node_p->insn,
+ BFD_RELOC_NDS32_PTR_RESOLVED,
+ reloc_node_p->insn->byte_sz, abs_section_sym,
+ 0);
+ }
+
+ current_pattern[pattern_level] = reloc_node_p;
+ pattern_level++;
+ reloc_node_t = reloc_node_p->child;
+ while (reloc_node_t)
+ {
+ reloc_tree_gen_relocs (reloc_node_t, reloc_type);
+ reloc_node_t = reloc_node_t->sibling;
+ }
+ if (reloc_node_p->leaf_num)
+ insert_relax_type (reloc_node_p->insn, BFD_RELOC_NDS32_PTR_COUNT, 0,
+ reloc_node_p->leaf_num, NULL);
+ pattern_level--;
+}
+
+static void
+append_relax_relocs (void)
+{
+ basic_block *bb;
+ nds32_insn_instant *insn_t;
+ reloc_tree_node *reloc_node_t;
+ int converted = 0;
+
+ bb = bb_list;
+
+ while (bb != NULL)
+ {
+ insn_t = bb->insn_head;
+ while (insn_t != NULL && insn_t != bb->insn_tail->next)
+ {
+ if (insn_t->reloc_node != NULL)
+ {
+ reloc_node_t = insn_t->reloc_node;
+ converted = 0;
+
+ while (reloc_node_t != NULL)
+ {
+ if (reloc_node_t->parent == NULL)
+ {
+ get_sym_addend_from_insn (reloc_node_t->insn,
+ ¤t_trace_sym,
+ ¤t_trace_addend);
+ switch (reloc_node_t->type_mask)
+ {
+ case NDS32_GOT_MASK:
+ reloc_tree_gen_relocs (reloc_node_t,
+ BFD_RELOC_NDS32_GOT_SUFF);
+ break;
+ case NDS32_GOTOFF_MASK:
+ reloc_tree_gen_relocs (reloc_node_t,
+ BFD_RELOC_NDS32_GOTOFF_SUFF);
+ break;
+ case NDS32_PLT_GOT_MASK:
+ reloc_tree_gen_relocs (reloc_node_t,
+ BFD_RELOC_NDS32_PLT_GOT_SUFF);
+ break;
+ case NDS32_MULCALL_MASK:
+ reloc_tree_gen_relocs (reloc_node_t,
+ BFD_RELOC_NDS32_MULCALL_SUFF);
+ break;
+ }
+ }
+ if (reloc_node_t->convertible && !converted)
+ {
+ insert_relax_type (reloc_node_t->insn,
+ BFD_RELOC_NDS32_INSN16, 4, 0, NULL);
+ converted = 1;
+ }
+ reloc_node_t = reloc_node_t->next;
+ }
+ }
+ insn_t = insn_t->next;
+ }
+ bb = bb->list_next;
+ }
+}
+
+static void
+reloc_tree_trace_relocs_prefix (reloc_tree_node *reloc_node_p)
+{
+ reloc_tree_node *reloc_node_t;
+ static int pattern_level = 0;
+ int i;
+
+ if (reloc_node_p->tainted)
+ return;
+
+ /* Count leaf number. */
+ if (reloc_node_p->child == NULL)
+ for (i = 0; i < pattern_level; i++)
+ current_pattern[i]->leaf_num++;
+
+ current_pattern[pattern_level] = reloc_node_p;
+ current_pattern[pattern_level]->leaf_num = 0;
+ pattern_level++;
+ reloc_node_t = reloc_node_p->child;
+ while (reloc_node_t)
+ {
+ reloc_tree_trace_relocs_prefix (reloc_node_t);
+ reloc_node_t = reloc_node_t->sibling;
+ }
+ pattern_level--;
+}
+
+static void
+count_reloc_leaf (void)
+{
+ basic_block *bb;
+ nds32_insn_instant *insn_t;
+ reloc_tree_node *reloc_node_t;
+
+ bb = bb_list;
+
+ while (bb != NULL)
+ {
+ insn_t = bb->insn_head;
+ while (insn_t != NULL && insn_t != bb->insn_tail->next)
+ {
+ if (insn_t->reloc_node != NULL)
+ {
+ reloc_node_t = insn_t->reloc_node;
+
+ while (reloc_node_t != NULL)
+ {
+ if (reloc_node_t->parent == NULL)
+ {
+ switch (reloc_node_t->type_mask)
+ {
+ case NDS32_GOT_MASK:
+ case NDS32_GOTOFF_MASK:
+ case NDS32_PLT_GOT_MASK:
+ case NDS32_MULCALL_MASK:
+ reloc_tree_trace_relocs_prefix (reloc_node_t);
+ break;
+ }
+ }
+ reloc_node_t = reloc_node_t->next;
+ }
+ }
+ insn_t = insn_t->next;
+ }
+ bb = bb->list_next;
+ }
+}
+
+static void
+clean_reloc_trees (void)
+{
+ basic_block *bb;
+ nds32_insn_instant *insn_t;
+
+ bb = bb_list;
+ while (bb != NULL)
+ {
+ insn_t = bb->insn_head;
+ while (insn_t != NULL && insn_t != bb->insn_tail->next)
+ {
+ free_reloc_node_next (insn_t->reloc_node);
+ insn_t = insn_t->next;
+ }
+ bb = bb->list_next;
+ }
+}
+
+static void
+cfg_clean (void)
+{
+ nds32_insn_instant *insn_s, *insn_s2;
+ dep_node *node, *node2;
+ basic_block *bb, *bb2;
+ insn_label_list *label_list, *label_list2;
+ nds32_bb_list *nds32_bb_list_l, *nds32_bb_list_l2;
+
+ free_nds32_reloc_node_list (nds32_reloc_node_pool, NULL);
+ free_nds32_insn_list (&nds32_hi_insn_list);
+ free_nds32_insn_list (&nds32_got20_insn_list);
+ free_nds32_insn_list (&nds32_slt_insn_list);
+ free_nds32_insn_list (&nds32_ta_insn_list);
+ free_nds32_insn_list (&nds32_free_insn_list);
+
+ nds32_bb_list_l = nds32_free_bb_list;
+ while (nds32_bb_list_l != NULL)
+ {
+ nds32_bb_list_l2 = nds32_bb_list_l->next;
+ xfree (nds32_bb_list_l);
+ nds32_bb_list_l = nds32_bb_list_l2;
+ }
+
+ if (bb_list == NULL)
+ insn_s = NULL;
+ else
+ {
+ bb = bb_list;
+ while (bb != NULL && bb->insn_head == NULL)
+ bb = bb->list_next;
+ if (bb == NULL)
+ insn_s = NULL;
+ else
+ insn_s = bb->insn_head;
+ }
+ while (insn_s != NULL)
+ {
+ node = insn_s->gr_dep_list;
+ while (node != NULL)
+ {
+ node2 = node->next;
+ xfree (node);
+ node = node2;
+ }
+ insn_s2 = insn_s->next;
+ xfree (insn_s);
+ insn_s = insn_s2;
+ }
+ bb = bb_list;
+ while (bb != NULL)
+ {
+ label_list = bb->label_list;
+ while (label_list != NULL)
+ {
+ label_list2 = label_list->next;
+ xfree (label_list);
+ label_list = label_list2;
+ }
+ bb2 = bb->list_next;
+ xfree (bb);
+ bb = bb2;
+ }
+ clean_lp_bb_list (lp_bb_node_list);
+ free (bb_indirect);
+}
+
+/* md_elf_section_change_hook */
+
+void
+nds32_elf_section_change_hook (void)
+{
+ /* Reset b2bb_prev status. See comments in md_assemble (). */
+ b2bb_prev = 0;
+
+ /* If we have reached the end of a section and we are not on a word
+ boundary then emit a nop to make sure that the section ends on a word
+ boundary. */
+ if (seen_relaxable_p)
+ (void) nds32_start_label (0, 1);
+
+ /* In some cases a previous section may ended with a non-jump
+ instruction because the previous section may integrated with
+ another section in the other object with the same name.
+ To avoid a cross-section basic block happened, a new basic
+ block is needed when current section is changed. */
+ create_new_basic_block ();
+
+ nds32_last_label = NULL;
+}
+
+/* md_cleanup */
+
+void
+nds32_cleanup (void)
+{
+ /* If we have reached the end of a section and we are not on a word
+ boundary then emit a nop to make sure that the section ends on a word
+ boundary. */
+ if (seen_relaxable_p)
+ (void) nds32_start_label (0, 1);
+ create_new_basic_block ();
+}
+
+typedef struct LSDACS_entry LSDACS_entry;
+typedef struct LSDA_entry LSDA_entry;
+struct LSDACS_entry
+{
+ fragS *entries[4];
+ LSDACS_entry *next;
+};
+
+struct LSDA_entry
+{
+ LSDACS_entry *lsdacs;
+ fragS *pseudo_align_frag;
+ int create_state;
+ LSDA_entry *next;
+};
+
+/* 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, PTR 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);
+ }
+}
+
+void
+md_end (void)
+{
+ frchainS *frchain_bak = frchain_now;
+ segT seg_bak = now_seg;
+ fragS *frag_bak = frag_now;
+ int saved_endian = target_big_endian;
+
+ if (enable_relax_relocs && bb_list != NULL)
+ {
+ bfdsym_hash = hash_new ();
+ build_cfg ();
+ bfd_map_over_sections (stdoutput, nds32_insert_leb128_fixes, NULL);
+ count_bb_known_pred ();
+ target_big_endian = 1;
+ def_use_analysis ();
+ life_time_analysis_sethi ();
+ count_reloc_leaf ();
+ check_invalid_reloc ();
+ adjust_reloc_contents ();
+ append_relax_relocs ();
+ hash_traverse (bfdsym_hash, bfdsym_bb_map_delete);
+ hash_die (bfdsym_hash);
+ }
+
+ push_to_insn_list_all (&nds32_hi_insn_list, &nds32_free_insn_list);
+ clean_reloc_trees ();
+ cfg_clean ();
+ clean_seg_fixup_list ();
+
+ frchain_now = frchain_bak;
+ now_seg = seg_bak;
+ frag_now = frag_bak;
+ target_big_endian = saved_endian;
+
+ if (vec_size == 4 || vec_size == 16)
+ {
+ unsigned int flag = 0;
+
+ if (!sec_eflag)
+ create_nds32_seg (NDS32_SEC_EFLAG_ISR);
+ subseg_set (sec_eflag, (subsegT) 0);
+
+ frag_more (4);
+ frag_now->fr_fix += 4;
+ flag |= vec_size == 4 ? 1 : 2;
+ md_number_to_chars (frag_now->fr_literal, flag, 4);
+ }
+}
+
+bfd_boolean
+nds32_allow_local_subtract (expressionS *expr_l ATTRIBUTE_UNUSED,
+ expressionS *expr_r ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED)
+{
+ return FALSE;
+}
+
+/* Sort relocation by address.
+
+ We didn't use qsort () in stdlib, because quick-sort is not a stable
+ sorting algorithm. Relocations at the same address (r_offset) must keep
+ their relative order. For example, RELAX_ENTRY must be the very first
+ relocation entry.
+
+ Currently, this function implements insertion-sort. */
+
+static int
+compar_relent (const void *lhs, const void *rhs)
+{
+ const arelent **l = (const arelent **) lhs;
+ const arelent **r = (const arelent **) rhs;
+
+ if ((*l)->address > (*r)->address)
+ return 1;
+ else if ((*l)->address == (*r)->address)
+ return 0;
+ else
+ return -1;
+}
+
+/* SET_SECTION_RELOCS ()
+
+ Although this macro is originally used to set a relocation for each section,
+ we use it to sort relocations in the same section by the address of the
+ relocation. */
+
+void
+nds32_set_section_relocs (asection *sec, arelent ** relocs ATTRIBUTE_UNUSED,
+ unsigned int n ATTRIBUTE_UNUSED)
+{
+ bfd *abfd ATTRIBUTE_UNUSED = sec->owner;
+ if (bfd_get_section_flags (abfd, sec) & (flagword) SEC_RELOC)
+ nds32_insertion_sort (sec->orelocation, sec->reloc_count, sizeof
(arelent**),
+ compar_relent);
+}
+
+static void
+nds32_insert_relax_entry (bfd *abfd ATTRIBUTE_UNUSED, asection *sec,
+ PTR xxx ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo;
+ fragS *fragP;
+ fixS *fixP;
+ expressionS exp;
+ fixS *fix_tail_bak;
+ fixS *fixp;
+
+ seginfo = seg_info (sec);
+ if (!seginfo || !symbol_rootP || !subseg_text_p (sec))
+ return;
+ /* If there is no relocation and relax is disabled, it is not necessary to
+ insert R_NDS32_RELAX_ENTRY for linker do EX9 or IFC optimization. */
+ for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next)
+ if (!fixp->fx_done)
+ break;
+ if (!fixp && !enable_relax_ex9)
+ return;
+
+ subseg_change (sec, 0);
+ fix_tail_bak = seginfo->fix_tail;
+
+ /* Set RELAX_ENTRY flags for linker. */
+ fragP = seginfo->frchainP->frch_root;
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = section_symbol (sec);
+ 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 linke-time,
+ so we set them anyway. */
+ if (enable_relax_ex9)
+ exp.X_add_number |= R_NDS32_RELAX_ENTRY_EX9_FLAG;
+ if (enable_relax_ifc)
+ exp.X_add_number |= R_NDS32_RELAX_ENTRY_IFC_FLAG;
+ }
+ 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;
+ if (optimize_for_space_no_align)
+ exp.X_add_number |= R_NDS32_RELAX_ENTRY_OPTIMIZE_FOR_SPACE_UNALIGN_FLAG;
+ fixP = fix_new_exp (fragP, 0, 0, &exp, 0, BFD_RELOC_NDS32_RELAX_ENTRY);
+ fixP->fx_no_overflow = 1;
+
+ if (fixP == seginfo->fix_root)
+ fixP->fx_next = NULL;
+ else
+ fixP->fx_next = seginfo->fix_root;
+ seginfo->fix_root = fixP;
+ if (fix_tail_bak != NULL)
+ fix_tail_bak->fx_next = NULL;
+ seginfo->fix_tail = fix_tail_bak;
+}
+
+/* md_frob_file */
+
+void
+nds32_frob_file (void)
+{
+ bfd_map_over_sections (stdoutput, nds32_insert_relax_entry, (char *) 0);
+}
+
+/* tc_fix_adjustable */
+
+bfd_boolean
+nds32_fix_adjustable (fixS *fixP)
+{
+ bfd_reloc_code_real_type reloc_type;
+
+ if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ const CGEN_INSN *insn = NULL;
+ int opindex;
+ const CGEN_OPERAND *operand;
+
+ opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+ operand = cgen_operand_lookup_by_num (gas_cgen_cpu_desc, opindex);
+ reloc_type = md_cgen_lookup_reloc (insn, operand, fixP);
+ }
+ else
+ reloc_type = fixP->fx_r_type;
+
+ if (fixP->fx_addsy == NULL)
+ return 1;
+
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERNAL (fixP->fx_addsy))
+ return 0;
+ if (S_IS_WEAK (fixP->fx_addsy))
+ return 0;
+
+ if (pic_code && (reloc_type == BFD_RELOC_NDS32_20
+ || reloc_type == BFD_RELOC_NDS32_HI20
+ || reloc_type == BFD_RELOC_NDS32_LO12S3
+ || reloc_type == BFD_RELOC_NDS32_LO12S2
+ || reloc_type == BFD_RELOC_NDS32_LO12S2_DP
+ || reloc_type == BFD_RELOC_NDS32_LO12S2_SP
+ || reloc_type == BFD_RELOC_NDS32_LO12S1
+ || reloc_type == BFD_RELOC_NDS32_LO12S0
+ || reloc_type == BFD_RELOC_NDS32_LO12S0_ORI))
+ return 0;
+
+ if (reloc_type == BFD_RELOC_NDS32_GOT20
+ || reloc_type == BFD_RELOC_NDS32_25_PLTREL
+ || reloc_type == BFD_RELOC_NDS32_GOTPC_HI20
+ || reloc_type == BFD_RELOC_NDS32_GOTPC_LO12
+ || reloc_type == BFD_RELOC_NDS32_GOT_HI20
+ || reloc_type == BFD_RELOC_NDS32_GOT_LO12)
+ return 0;
+
+ /* We need the symbol name for the VTABLE entries. */
+ if (reloc_type == BFD_RELOC_VTABLE_INHERIT
+ || reloc_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
+
+/* 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_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_flag will be decided by
+ the enabled options by command line or default configuration. */
+ if (enable_fpu_dp_extension || enable_fpu_sp_extension)
+ {
+ nds32_flags |= enable_fpu_dp_extension ? E_NDS32_HAS_FPU_DP_INST : 0;
+ nds32_flags |= enable_fpu_sp_extension ? E_NDS32_HAS_FPU_INST : 0;
+ }
+ else
+ {
+ /* Should never here. */
+ as_bad (_("Used FPU instructions requires enabling FPU extension"));
+ }
+ }
+
+ if (nds32_flags & (E_NDS32_HAS_FPU_INST | E_NDS32_HAS_FPU_DP_INST))
+ {
+ /* single/double FPU has been used, set FPU register set
+ we did not check the actual number of register used
+ we may want to do it while assemble. */
+ nds32_flags &= ~E_NDS32_FPU_REG_CONF;
+ nds32_flags |= fpu_config;
+ }
+
+ nds32_flags |= elf_ver | abi_ver;
+ if (enable_reduce_regs)
+ nds32_flags |= E_NDS32_HAS_REDUCED_REGS;
+ if (pic_code)
+ nds32_flags |= E_NDS32_HAS_PIC;
+
+ elf_elfheader (stdoutput)->e_flags |= nds32_flags;
+}
+
+/* md_apply_fix */
+
+void
+nds32_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *where = fixP->fx_frag->fr_literal + fixP->fx_where;
+ valueT value = *valP;
+ /* Canonical name, since used a lot. */
+
+ if (fixP->fx_r_type < BFD_RELOC_UNUSED && fixP->fx_r_type > BFD_RELOC_NONE
+ && fixP->fx_r_type != BFD_RELOC_NDS32_DIFF_ULEB128)
+ {
+ /* These are relocations we add not using CGEN. */
+ fixP->fx_addnumber = value;
+ fixP->tc_fix_data = NULL;
+ if (fixP->fx_r_type == BFD_RELOC_NDS32_DATA && !enable_relax_ex9)
+ fixP->fx_done = 1;
+ 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 direcly 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.
+
+ COLE */
+
+ 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_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+ return;
+ }
+ }
+ else if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ CGEN_CPU_DESC cd = gas_cgen_cpu_desc;
+ int opindex;
+ const CGEN_OPERAND *operand;
+ const char *errmsg;
+ bfd_reloc_code_real_type reloc_type;
+ CGEN_FIELDS *fields;
+ const CGEN_INSN *insn;
+
+ opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+ operand = cgen_operand_lookup_by_num (cd, opindex);
+ fields = alloca (CGEN_CPU_SIZEOF_FIELDS (cd));
+ insn = fixP->fx_cgen.insn;
+ /* If the relocation has been fully resolved finish the operand here. */
+ /* FIXME: This duplicates the capabilities of code in BFD. */
+ if (fixP->fx_done
+ /* FIXME: If partial_inplace isn't set bfd_install_relocation won't
+ finish the job. Testing for pcrel is a temporary hack. */
+ || fixP->fx_pcrel)
+ {
+ CGEN_CPU_SET_FIELDS_BITSIZE (cd) (fields, CGEN_INSN_BITSIZE (insn));
+ CGEN_CPU_SET_VMA_OPERAND (cd) (cd, opindex, fields,
+ (bfd_vma) value);
+
+#if CGEN_INT_INSN_P
+ {
+ CGEN_INSN_INT insn_value;
+
+ insn_value = cgen_get_insn_value (cd, (unsigned char *) where,
+ CGEN_INSN_BITSIZE (insn));
+
+ /* ??? 0 is passed for `pc'. */
+ errmsg = CGEN_CPU_INSERT_OPERAND (cd) (cd, opindex, fields,
+ &insn_value, (bfd_vma) 0);
+ cgen_put_insn_value (cd, (unsigned char *) where,
+ CGEN_INSN_BITSIZE (insn), insn_value);
+ }
+#else
+ /* ??? 0 is passed for `pc'. */
+ errmsg = CGEN_CPU_INSERT_OPERAND (cd) (cd, opindex, fields,
+ (unsigned char *) where,
+ (bfd_vma) 0);
+#endif
+ if (errmsg)
+ as_bad_where (fixP->fx_file, fixP->fx_line, "%s", errmsg);
+ }
+
+ if (fixP->fx_done)
+ return;
+
+ /* The operand isn't fully resolved. Determine a BFD reloc value
+ based on the operand information and leave it to
+ bfd_install_relocation. Note that this doesn't work when
+ partial_inplace == false. */
+
+ reloc_type = md_cgen_lookup_reloc (insn, operand, fixP);
+
+ if (reloc_type != BFD_RELOC_NONE)
+ fixP->fx_r_type = reloc_type;
+ else
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unresolved expression that must be resolved"));
+ fixP->fx_done = 1;
+ 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);
+ case BFD_RELOC_NDS32_MINUEND:
+ if (subseg_text_p (seg))
+ {
+ valueT val16;
+ val16 = md_chars_to_number (where, 2);
+ if ((val16 & INSN_LWI45_FE) != INSN_LWI45_FE)
+ break;
+ val16 |= value;
+ md_number_to_chars (where, val16, 2);
+ }
+ break;
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("internal error: can't install fix for reloc type %d (`%s')"),
+ fixP->fx_r_type,
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+ break;
+ }
+ }
+ /* else
+ bfd_install_relocation will be called to finish things up. */
+
+ /* Tuck `value' away for use by tc_gen_reloc.
+ See the comment describing fx_addnumber in write.h.
+ This field is misnamed (or misused :-). */
+ fixP->fx_addnumber = value;
+}
+
+/* A BFD_ASSEMBLER GAS will call this to generate a relocation. GAS will
+ pass the resulting reloc to bfd_install_relocation. This currently works
+ poorly, as bfd_install_relocation often does the wrong thing, and
+ instances of tc_gen_reloc have been written to work around the problems,
+ which in turns makes it difficult to fix bfd_install_relocation. */
+
+arelent *
+tc_gen_reloc (asection *section, fixS *fixP)
+{
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+
+ /* Remove LABEL relocation if it points to a 16-bit instruction and is not
+ defined by .align directive. */
+ if (fixP->fx_r_type == BFD_RELOC_NDS32_LABEL)
+ {
+ if (subseg_text_p (section)
+ && fixP->fx_frag->fr_address + fixP->fx_where < section->size
+ && fixP->fx_offset < 2
+ && (fixP->fx_frag->fr_fix < 2
+ || (fixP->fx_frag->fr_fix > (fixP->fx_where + 1)
+ && 2 == get_frag_insn_size (fixP->fx_frag,
+ fixP->fx_where))))
+ {
+ /* This is a 16-bit instruction, remove this relocation. */
+ return NULL;
+ }
+ }
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
+ reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
+
+ code = fixP->fx_r_type;
+ if (pic_code)
+ {
+ switch (code)
+ {
+ case BFD_RELOC_NDS32_20:
+ if (fixP->fx_addsy != NULL
+ && strcmp (S_GET_NAME (fixP->fx_addsy), GOT_NAME) == 0)
+ code = BFD_RELOC_NDS32_GOTPC20;
+ else
+ code = BFD_RELOC_NDS32_GOT20;
+ break;
+ case BFD_RELOC_NDS32_HI20:
+ if (fixP->fx_addsy != NULL
+ && strcmp (S_GET_NAME (fixP->fx_addsy), GOT_NAME) == 0)
+ code = BFD_RELOC_NDS32_GOTPC_HI20;
+ else
+ code = BFD_RELOC_NDS32_GOT_HI20;
+ break;
+ case BFD_RELOC_NDS32_LO12S0:
+ case BFD_RELOC_NDS32_LO12S0_ORI:
+ if (fixP->fx_addsy != NULL
+ && strcmp (S_GET_NAME (fixP->fx_addsy), GOT_NAME) == 0)
+ code = BFD_RELOC_NDS32_GOTPC_LO12;
+ else
+ code = BFD_RELOC_NDS32_GOT_LO12;
+ break;
+ default:
+ break;
+ }
+ }
+
+ 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;
+ }
+
+ /* Use fx_offset for these cases. */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_NDS32_RELAX_ENTRY
+ || fixP->fx_r_type == BFD_RELOC_NDS32_INSN16
+ || fixP->fx_r_type == BFD_RELOC_NDS32_LABEL
+ || fixP->fx_r_type == BFD_RELOC_NDS32_LONGCALL1
+ || fixP->fx_r_type == BFD_RELOC_NDS32_LONGCALL2
+ || fixP->fx_r_type == BFD_RELOC_NDS32_LONGCALL3
+ || fixP->fx_r_type == BFD_RELOC_NDS32_LONGJUMP1
+ || fixP->fx_r_type == BFD_RELOC_NDS32_LONGJUMP2
+ || fixP->fx_r_type == BFD_RELOC_NDS32_LONGJUMP3
+ || fixP->fx_r_type == BFD_RELOC_NDS32_LOADSTORE
+ || fixP->fx_r_type == BFD_RELOC_NDS32_9_FIXED
+ || fixP->fx_r_type == BFD_RELOC_NDS32_15_FIXED
+ || fixP->fx_r_type == BFD_RELOC_NDS32_17_FIXED
+ || fixP->fx_r_type == BFD_RELOC_NDS32_25_FIXED
+ || fixP->fx_r_type == BFD_RELOC_NDS32_DIFF8
+ || fixP->fx_r_type == BFD_RELOC_NDS32_DIFF16
+ || fixP->fx_r_type == BFD_RELOC_NDS32_DIFF32
+ || fixP->fx_r_type == BFD_RELOC_NDS32_DIFF_ULEB128)
+ reloc->addend = fixP->fx_offset;
+ else if ((!pic_code
+ && code != BFD_RELOC_NDS32_25_PLTREL)
+ && fixP->fx_pcrel
+ && fixP->fx_addsy != NULL
+ && (S_GET_SEGMENT (fixP->fx_addsy) != section)
+ && S_IS_DEFINED (fixP->fx_addsy)
+ && !S_IS_EXTERNAL (fixP->fx_addsy) && !S_IS_WEAK (fixP->fx_addsy))
+ /* Already used fx_offset in the opcode field itself. */
+ reloc->addend = fixP->fx_offset;
+ else if (fixP->fx_r_type == BFD_RELOC_NDS32_DATA)
+ reloc->addend = fixP->fx_size;
+ else
+ reloc->addend = fixP->fx_addnumber;
+
+ return reloc;
+}
+
+/* This function checks whether the "cont" is started with word "what". */
+
+static inline char *
+nds32_end_of_match (char *cont, char *what)
+{
+ int len = strlen (what);
+
+ if (strncasecmp (cont, what, len) == 0 && !is_part_of_name (cont[len]))
+ return cont + len;
+
+ return NULL;
+}
+
+/* md_parse_name ()
+
+ This function is borrowed from tc-sh.c. Sync it when upgrading. */
+
+int
+nds32_parse_name (char const *name, expressionS * exprP, enum expr_mode mode,
+ char *nextcharP)
+{
+ char *next = input_line_pointer;
+ char *next_end;
+ int reloc_type;
+ operatorT op_type;
+ segT segment;
+
+ exprP->X_op_symbol = NULL;
+ exprP->X_md = BFD_RELOC_UNUSED;
+
+ if (strcmp (name, GOT_NAME) == 0 && *nextcharP != '@')
+ {
+ if (!GOT_symbol)
+ GOT_symbol = symbol_find_or_make (name);
+
+ exprP->X_add_symbol = GOT_symbol;
+no_suffix:
+ /* If we have an absolute symbol or a
+ reg, then we know its value now. */
+ segment = S_GET_SEGMENT (exprP->X_add_symbol);
+ if (mode != expr_defer && segment == absolute_section)
+ {
+ exprP->X_op = O_constant;
+ exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+ exprP->X_add_symbol = NULL;
+ }
+ else if (mode != expr_defer && segment == reg_section)
+ {
+ exprP->X_op = O_register;
+ exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+ exprP->X_add_symbol = NULL;
+ }
+ else
+ {
+ exprP->X_op = O_symbol;
+ exprP->X_add_number = 0;
+ }
+
+ return 1;
+ }
+
+ exprP->X_add_symbol = symbol_find_or_make (name);
+
+ if (*nextcharP != '@')
+ goto no_suffix;
+ else if ((next_end = nds32_end_of_match (next + 1, "GOTOFF")))
+ {
+ reloc_type = BFD_RELOC_NDS32_GOTOFF;
+ op_type = O_PIC_reloc;
+ }
+ else if ((next_end = nds32_end_of_match (next + 1, "GOT")))
+ {
+ reloc_type = BFD_RELOC_NDS32_GOT20;
+ op_type = O_PIC_reloc;
+ }
+ else if ((next_end = nds32_end_of_match (next + 1, "PLT")))
+ {
+ reloc_type = BFD_RELOC_NDS32_25_PLTREL;
+ op_type = O_PIC_reloc;
+ }
+ else
+ goto no_suffix;
+
+ *input_line_pointer = *nextcharP;
+ input_line_pointer = next_end;
+ *nextcharP = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ exprP->X_op = op_type;
+ exprP->X_add_number = 0;
+ exprP->X_md = reloc_type;
+
+ return 1;
+}
+
+/* md_cgen_record_fixup_exp */
+
+int
+nds32_cgen_parse_fix_exp (int opinfo, expressionS *exp)
+{
+ /* Since machine independent part of assembler can't handle symbols with
+ machine dependent operation(O_md1), and the RELAXABLE instructions make
+ assembler handle instructions with symbol has operation O_md1, so a
+ procedure which can turn machine dependent operation of symbol to machine
+ independent operation is needed.
+
+ For example:
+ b SYM@PLT
+ since b is an instruction with RELAXABLE attribute, and SYM@PLT
is considered
+ as a symbol with operation O_md1, O_md1 should be turned to O_symbol. */
+
+ if (exp->X_op == O_PIC_reloc && exp->X_md == BFD_RELOC_NDS32_25_PLTREL)
+ {
+ if (!pic_code)
+ {
+ exp->X_op = O_symbol;
+ opinfo = exp->X_md;
+ }
+ else
+ {
+ if (opinfo == 0)
+ {
+ exp->X_op = O_symbol;
+ opinfo = exp->X_md;
+ }
+ else
+ {
+ exp->X_op = O_symbol;
+ }
+ }
+ }
+
+ return opinfo;
+}
+
+int
+tc_nds32_regname_to_dw2regnum (char *regname)
+{
+ symbolS *sym = symbol_find (regname);
+
+ if (S_GET_SEGMENT (sym) == reg_section
+ && sym->sy_value.X_add_number < 32)
+ return sym->sy_value.X_add_number;
+ return -1;
+}
+
+void
+tc_nds32_frame_initial_instructions (void)
+{
+ /* CIE */
+ cfi_add_CFA_def_cfa (REG_SP, 0);
+}
diff --git a/gas/config/tc-nds32.h b/gas/config/tc-nds32.h
new file mode 100644
index 0000000..bd2af8d
--- /dev/null
+++ b/gas/config/tc-nds32.h
@@ -0,0 +1,216 @@
+/* tc-nds32.h -- Header file for tc-nds32.c.
+
+ Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
+ Free Software Foundation, Inc.
+ Contributed by Andes Technology Corporation.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the license, 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 this program; see the file COPYING3. If not,
+ see <http://www.gnu.org/licenses/>. */
+
+#ifndef TC_NDS32
+#define TC_NDS32
+
+#include "bfd_stdint.h"
+
+#define LISTING_HEADER \
+ (target_big_endian ? "NDS32 GAS" : "NDS32 GAS Little Endian")
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_nds32
+
+/* Default to big endian. Please note that for Andes architecture,
+ instructions are always in big-endian format. */
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 1
+#endif
+
+/* This is used to construct expressions out of @GOTOFF, @PLT and @GOT
+ symbols. The relocation type is stored in X_md. */
+#define O_PIC_reloc O_md1
+
+/*
+ * as.c
+ */
+/* extend GAS command line option handling capability */
+extern int nds32_parse_option (int, char *);
+extern void nds32_after_parse_args (void);
+/* The endianness of the target format may change based on command
+ line arguments. */
+extern const char *nds32_target_format (void);
+#define md_parse_option(optc, optarg) nds32_parse_option (optc, optarg)
+#define md_after_parse_args() nds32_after_parse_args ()
+#define TARGET_FORMAT nds32_target_format()
+
+
+/* expr.c */
+extern int nds32_parse_name (char const *, expressionS *, enum
expr_mode, char *);
+extern bfd_boolean nds32_allow_local_subtract (expressionS *,
expressionS *, segT);
+#define md_parse_name(name, exprP, mode, nextcharP) \
+ nds32_parse_name (name, exprP, mode, nextcharP)
+#define md_allow_local_subtract(lhs,rhs,sect) nds32_allow_local_subtract
(lhs, rhs, sect)
+
+/*
+ * dwarf2dbg.c
+ */
+#define DWARF2_USE_FIXED_ADVANCE_PC 1
+
+/*
+ * cgen.c
+ */
+extern int nds32_cgen_parse_fix_exp (int, expressionS *);
+#define TC_CGEN_PARSE_FIX_EXP(opinfo, exp) nds32_cgen_parse_fix_exp
(opinfo, exp)
+/* After creating a fixup for an instruction operand, we need to check for
+ HI20 relocs and queue them up for later sorting. */
+#define md_cgen_record_fixup_exp nds32_cgen_record_fixup_exp
+/* Account for nop if 32 bit insn falls on odd halfword boundary.
TODO: Review this. */
+#define TC_CGEN_MAX_RELAX(insn, len) 20
+
+/*
+ * write.c
+ */
+extern long nds32_pcrel_from_section (struct fix *, segT);
+extern bfd_boolean nds32_fix_adjustable (struct fix *);
+extern void nds32_frob_file (void);
+extern void nds32_frob_file_before_fix (void);
+extern void elf_nds32_final_processing (void);
+extern int nds32_validate_fix_sub (struct fix *, segT);
+extern int nds32_force_relocation (struct fix *);
+extern void nds32_set_section_relocs(asection *, arelent ** , unsigned int );
+/* Fill in rs_align_code fragments. TODO: Review this. */
+extern void nds32_handle_align (fragS *);
+extern long nds32_relax_frag (segT, fragS *, long);
+extern int tc_nds32_regname_to_dw2regnum (char *regname);
+extern void tc_nds32_frame_initial_instructions (void);
+#define MD_PCREL_FROM_SECTION(fix, sect) nds32_pcrel_from_section(fix, sect)
+#define TC_FINALIZE_SYMS_BEFORE_SIZE_SEG 0
+#define tc_fix_adjustable(FIX) nds32_fix_adjustable (FIX)
+#define md_apply_fix(fixP, addn, seg) nds32_apply_fix (fixP, addn, seg)
+#define tc_frob_file() nds32_frob_file ()
+#define tc_frob_file_before_fix() nds32_frob_file_before_fix ()
+#define elf_tc_final_processing() elf_nds32_final_processing ()
+/* COLE: For DIFF relocations.
+ The default behavior is inconsistent with the asm internal document. */
+#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEC) \
+ (! SEG_NORMAL (SEC) || TC_FORCE_RELOCATION (FIX))
+#define TC_FORCE_RELOCATION(fix) nds32_force_relocation (fix)
+#define TC_VALIDATE_FIX_SUB(FIX,SEG) nds32_validate_fix_sub (FIX,SEG)
+#define SET_SECTION_RELOCS(sec, relocs, n) nds32_set_section_relocs
(sec, relocs, n)
+/* Values passed to md_apply_fix don't include the symbol value.
+ COLE: TODO: Review this. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+#define HANDLE_ALIGN(f) nds32_handle_align (f)
+#undef DIFF_EXPR_OK /* They should be fixed in linker. */
+#define md_relax_frag(segment, fragP, stretch) nds32_relax_frag
(segment, fragP, stretch)
+#define WORKING_DOT_WORD /* We don't need to handle .word strangely. */
+/* Using to chain fixup with previous fixup. */
+#define TC_FIX_TYPE struct fix*
+#define TC_INIT_FIX_DATA(fixP) do \
+{ \
+ fixP->tc_fix_data=NULL; \
+} while (0)
+
+/*
+ * read.c
+ */
+/* extend GAS macro handling capability */
+extern void nds32_macro_start (void);
+extern void nds32_macro_end (void);
+extern void nds32_macro_info (void *macro);
+extern void nds32_start_line_hook (void);
+extern void nds32_elf_section_change_hook (void);
+extern void md_begin (void);
+extern void md_end (void);
+extern int nds32_start_label (int, int);
+extern void nds32_cleanup (void);
+extern void nds32_flush_pending_output (void);
+extern void nds32_cons_align (int n);
+extern void nds32_check_label (symbolS *);
+extern void nds32_frob_label (symbolS *);
+void nds32_pre_do_align(int, char*, int, int);
+void nds32_do_align(int);
+#define md_macro_start() nds32_macro_start()
+#define md_macro_end() nds32_macro_end()
+#define md_macro_info(args) nds32_macro_info(args)
+#define TC_START_LABEL(C, S, STR) (C == ':' && nds32_start_label (0, 0))
+#define tc_check_label(label) nds32_check_label (label)
+#define tc_frob_label(label) nds32_frob_label (label)
+#define md_end md_end
+#define md_start_line_hook() nds32_start_line_hook()
+#define md_cons_align(n) nds32_cons_align (n)
+/* COLE: TODO: Review md_do_align. */
+#define md_do_align(N, FILL, LEN, MAX, LABEL) \
+ nds32_pre_do_align(N, FILL, LEN, MAX); \
+ if ((N) > 1 && (subseg_text_p (now_seg) \
+ || strncmp(now_seg->name, ".gcc_except_table",
sizeof(".gcc_except_table") - 1) == 0)) \
+ nds32_do_align (N); \
+ goto LABEL;
+#define md_elf_section_change_hook() nds32_elf_section_change_hook()
+#define md_flush_pending_output() nds32_flush_pending_output ()
+#define md_cleanup() nds32_cleanup ()
+#define LOCAL_LABELS_FB 1 /* Permit temporary numeric labels. */
+
+/*
+ * frags.c
+ */
+#define NDS32_FRAG_FLAG_NUM 2
+typedef struct nds32_frag_type {
+ relax_substateT flag[NDS32_FRAG_FLAG_NUM];
+ int insn_num;
+ uint32_t insn32;
+ /* To Save previos label fixup if existence. */
+ struct fix *fixup;
+} nds32_frag_type;
+
+extern void nds32_frag_init(fragS*);
+
+#define TC_FRAG_TYPE struct nds32_frag_type
+#define TC_FRAG_INIT(fragP) nds32_frag_init(fragP)
+
+/*
+ * CFI directive
+ */
+extern void nds32_elf_frame_initial_instructions(void);
+extern int tc_nds32_regname_to_dw2regnum (char *regname);
+
+#define TARGET_USE_CFIPOP 1
+#define DWARF2_DEFAULT_RETURN_COLUMN 30
+#define DWARF2_CIE_DATA_ALIGNMENT -4
+#define DWARF2_LINE_MIN_INSN_LENGTH 2
+
+#define tc_regname_to_dw2regnum tc_nds32_regname_to_dw2regnum
+#define tc_cfi_frame_initial_instructions tc_nds32_frame_initial_instructions
+
+/*
+ * COLE: TODO: Review These. They seem to be obsoleted.
+ */
+#if 1
+#define TC_RELOC_RTSYM_LOC_FIXUP(FIX) \
+ ((FIX)->fx_addsy == NULL \
+ || (! S_IS_EXTERNAL ((FIX)->fx_addsy) \
+ && ! S_IS_WEAK ((FIX)->fx_addsy) \
+ && S_IS_DEFINED ((FIX)->fx_addsy) \
+ && ! S_IS_COMMON ((FIX)->fx_addsy)))
+#define TC_HANDLES_FX_DONE
+/* This arranges for gas/write.c to not apply a relocation if
+ obj_fix_adjustable() says it is not adjustable. */
+#define TC_FIX_ADJUSTABLE(fixP) obj_fix_adjustable (fixP)
+#endif
+
+/* Because linker may relax the code, assemble-time expression
+ optimization is not allowed. */
+#define md_allow_eh_opt 0
+
+#endif /* TC_NDS32 */
diff --git a/gas/configure.in b/gas/configure.in
index 121fcfc..86a75a2 100644
--- a/gas/configure.in
+++ b/gas/configure.in
@@ -363,6 +363,10 @@ changequote([,])dnl
using_cgen=yes
;;
+ nds32)
+ using_cgen=yes
+ ;;
+
i386 | s390 | sparc)
if test $this_target = $target ; then
AC_DEFINE_UNQUOTED(DEFAULT_ARCH, "${arch}", [Default architecture.])
@@ -499,6 +503,7 @@ esac
cgen_cpu_prefix=""
if test $using_cgen = yes ; then
case ${target_cpu} in
+ nds32*) cgen_cpu_prefix="nds32" ;;
*) cgen_cpu_prefix=${target_cpu} ;;
esac
AC_SUBST(cgen_cpu_prefix)
diff --git a/gas/configure.tgt b/gas/configure.tgt
index 77c1d9b..fdc0612 100644
--- a/gas/configure.tgt
+++ b/gas/configure.tgt
@@ -79,6 +79,8 @@ case ${cpu} in
mips*el) cpu_type=mips endian=little ;;
mips*) cpu_type=mips endian=big ;;
mt) cpu_type=mt endian=big ;;
+ nds32be) cpu_type=nds32 endian=big ;;
+ nds32le) cpu_type=nds32 endian=little ;;
or32*) cpu_type=or32 endian=big ;;
pjl*) cpu_type=pj endian=little ;;
pj*) cpu_type=pj endian=big ;;
@@ -344,6 +346,9 @@ case ${generic_target} in
msp430-*-*) fmt=elf ;;
+ nds32-*-elf*) fmt=elf ;;
+ nds32-*-linux*) fmt=elf em=linux ;;
+
nios2-*-rtems*) fmt=elf ;;
nios2*-linux*) fmt=elf em=linux ;;
--
Best regards,
Kuan-Lin Chen.
kuanlinchentw@gmail.com