blob: e0133c379be8847417d6cf3977108f1a4477149c [file] [log] [blame]
/* tc-csky.c -- Assembler for C-SKY
Copyright (C) 1989-2021 Free Software Foundation, Inc.
Created by Lifang Xia (lifang_xia@c-sky.com)
Contributed by C-SKY Microsystems and Mentor Graphics.
This file is part of GAS, the GNU Assembler.
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GAS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GAS; see the file COPYING. If not, write to the Free
Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
02110-1301, USA. */
#include "as.h"
#include <limits.h>
#include <stdint.h>
#include <stdarg.h>
#include <ctype.h>
#include "safe-ctype.h"
#include "subsegs.h"
#include "obstack.h"
#include "libiberty.h"
#ifdef OBJ_ELF
#include "elf/csky.h"
#include "dw2gencfi.h"
#endif
#include "tc-csky.h"
#include "dwarf2dbg.h"
#define BUILD_AS 1
#define OPCODE_MAX_LEN 20
#define HAS_SUB_OPERAND 0xfffffffful
/* This value is just for lrw to distinguish "[]" label. */
#define NEED_OUTPUT_LITERAL 1
#define IS_EXTERNAL_SYM(sym, sec) (S_GET_SEGMENT (sym) != sec)
#define IS_SUPPORT_OPCODE16(opcode) (opcode->isa_flag16 | isa_flag)
#define IS_SUPPORT_OPCODE32(opcode) (opcode->isa_flag32 | isa_flag)
#define KB * 1024
#define MB KB * 1024
#define GB MB * 1024
/* Define DSP version flags. For different CPU, the version of DSP
instructions may be different. */
#define CSKY_DSP_FLAG_V1 (1 << 0) /* Normal DSP instructions. */
#define CSKY_DSP_FLAG_V2 (1 << 1) /* CK803S enhanced DSP. */
/* Literal pool related macros. */
/* 1024 - 1 entry - 2 byte rounding. */
#define v1_SPANPANIC (998)
#define v1_SPANCLOSE (900)
#define v1_SPANEXIT (600)
#define v2_SPANPANIC (1024 - 4)
/* 1024 is flrw offset.
24 is the biggest size for single instruction.
for lrw16 (3+7, 512 bytes). */
#define v2_SPANCLOSE (512 - 24)
/* For lrw16, 112 average size for a function. */
#define v2_SPANEXIT (512 - 112)
/* For lrw16 (3+7, 512 bytes). */
#define v2_SPANCLOSE_ELRW (1016 - 24)
/* For lrw16, 112 average size for a function. */
#define v2_SPANEXIT_ELRW (1016 - 112)
#define MAX_POOL_SIZE (1024 / 4)
#define POOL_END_LABEL ".LE"
#define POOL_START_LABEL ".LS"
/* Used in v1_relax_table. */
/* These are the two types of relaxable instruction. */
#define COND_JUMP 1
#define UNCD_JUMP 2
#define COND_JUMP_PIC 3
#define UNCD_JUMP_PIC 4
#define UNDEF_DISP 0
#define DISP12 1
#define DISP32 2
#define UNDEF_WORD_DISP 3
#define C12_LEN 2
/* Allow for align: bt/jmpi/.long + align. */
#define C32_LEN 10
/* Allow for align: bt/subi/stw/bsr/lrw/add/ld/addi/jmp/.long + align. */
#define C32_LEN_PIC 24
#define U12_LEN 2
/* Allow for align: jmpi/.long + align. */
#define U32_LEN 8
/* Allow for align: subi/stw/bsr/lrw/add/ld/addi/jmp/.long + align. */
#define U32_LEN_PIC 22
#define C(what,length) (((what) << 2) + (length))
#define UNCD_JUMP_S (do_pic ? UNCD_JUMP_PIC : UNCD_JUMP)
#define COND_JUMP_S (do_pic ? COND_JUMP_PIC : COND_JUMP)
#define U32_LEN_S (do_pic ? U32_LEN_PIC : U32_LEN)
#define C32_LEN_S (do_pic ? C32_LEN_PIC : C32_LEN)
/* Used in v2_relax_table. */
#define COND_DISP10_LEN 2 /* bt/bf_16. */
#define COND_DISP16_LEN 4 /* bt/bf_32. */
#define SCOND_DISP10_LEN 2 /* bt/bf_16, for CK801 only. */
#define SCOND_DISP16_LEN 6 /* !(bt/bf_16) + br_32. */
#define UNCD_DISP10_LEN 2 /* br_16. */
#define UNCD_DISP16_LEN 4 /* br_32. */
#define UNCD_DISP26_LEN 4 /* br32_old. */
#define JCOND_DISP10_LEN 2 /* bt/bf_16. */
#define JCOND_DISP16_LEN 4 /* bt/bf_32. */
#define JCOND_DISP32_LEN 12 /* !(bt/bf_16)/jmpi 32/.align 2/literal 4. */
#define JCOND_DISP26_LEN 8 /* bt/bf_32/br_32 old. */
#define JUNCD_DISP26_LEN 4 /* bt/bf_32 old. */
#define JUNCD_DISP10_LEN 2 /* br_16. */
#define JUNCD_DISP16_LEN 4 /* bt/bf_32. */
#define JUNCD_DISP32_LEN 10 /* jmpi_32/.align 2/literal 4/ CHANGED!. */
#define JCOMP_DISP26_LEN 8 /* bne_32/br_32 old. */
#define JCOMP_DISP16_LEN 4 /* bne_32 old. */
#define JCOMPZ_DISP16_LEN 4 /* bhlz_32. */
#define JCOMPZ_DISP32_LEN 14 /* bsz_32/jmpi 32/.align 2/literal 4. */
#define JCOMPZ_DISP26_LEN 8 /* bsz_32/br_32 old. */
#define JCOMP_DISP32_LEN 14 /* be_32/jmpi_32/.align 2/literal old. */
#define BSR_DISP10_LEN 2 /* bsr_16. */
#define BSR_DISP26_LEN 4 /* bsr_32. */
#define LRW_DISP7_LEN 2 /* lrw16. */
#define LRW_DISP16_LEN 4 /* lrw32. */
/* Declare worker functions. */
bool v1_work_lrw (void);
bool v1_work_jbsr (void);
bool v1_work_fpu_fo (void);
bool v1_work_fpu_fo_fc (void);
bool v1_work_fpu_write (void);
bool v1_work_fpu_read (void);
bool v1_work_fpu_writed (void);
bool v1_work_fpu_readd (void);
bool v2_work_istack (void);
bool v2_work_btsti (void);
bool v2_work_addi (void);
bool v2_work_subi (void);
bool v2_work_add_sub (void);
bool v2_work_rotlc (void);
bool v2_work_bgeni (void);
bool v2_work_not (void);
bool v2_work_jbtf (void);
bool v2_work_jbr (void);
bool v2_work_lrw (void);
bool v2_work_lrsrsw (void);
bool v2_work_jbsr (void);
bool v2_work_jsri (void);
bool v2_work_movih (void);
bool v2_work_ori (void);
bool float_work_fmovi (void);
bool dsp_work_bloop (void);
bool float_work_fpuv3_fmovi (void);
bool float_work_fpuv3_fstore (void);
bool v2_work_addc (void);
/* csky-opc.h must be included after workers are declared. */
#include "opcodes/csky-opc.h"
#include "opcode/csky.h"
enum
{
RELAX_NONE = 0,
RELAX_OVERFLOW,
COND_DISP10 = 20, /* bt/bf_16. */
COND_DISP16, /* bt/bf_32. */
SCOND_DISP10, /* br_16 */
SCOND_DISP16, /* !(bt/bf_32) + br_32. */
UNCD_DISP10, /* br_16. */
UNCD_DISP16, /* br_32. */
JCOND_DISP10, /* bt/bf_16. */
JCOND_DISP16, /* bt/bf_32. */
JCOND_DISP32, /* !(bt/bf_32)/jmpi + literal. */
JUNCD_DISP10, /* br_16. */
JUNCD_DISP16, /* br_32. */
JUNCD_DISP32, /* jmpi + literal. */
JCOMPZ_DISP16, /* bez/bnez/bhz/blsz/blz/bhsz. */
JCOMPZ_DISP32, /* !(jbez/jbnez/jblsz/jblz/jbhsz) + jmpi + literal. */
BSR_DISP26, /* bsr_32. */
LRW_DISP7, /* lrw16. */
LRW2_DISP8, /* lrw16, -mno-bsr16,8 bit offset. */
LRW_DISP16, /* lrw32. */
};
unsigned int mach_flag = 0;
unsigned int arch_flag = 0;
unsigned int other_flag = 0;
BFD_HOST_U_64_BIT isa_flag = 0;
unsigned int dsp_flag = 0;
typedef struct stack_size_entry
{
struct stack_size_entry *next;
symbolS *function;
unsigned int stack_size;
} stack_size_entry;
struct csky_arch_info
{
const char *name;
unsigned int arch_flag;
unsigned int bfd_mach_flag;
};
typedef enum
{
INSN_OPCODE,
INSN_OPCODE16F,
INSN_OPCODE32F,
} inst_flag;
/* Macro information. */
struct csky_macro_info
{
const char *name;
/* How many operands : if operands == 5, all of 1,2,3,4 are ok. */
long oprnd_num;
BFD_HOST_U_64_BIT isa_flag;
/* Do the work. */
void (*handle_func)(void);
};
struct csky_insn_info
{
/* Name of the opcode. */
char *name;
/* Output instruction. */
unsigned int inst;
/* Pointer for frag. */
char *output;
/* End of instruction. */
char *opcode_end;
/* CPU infomations. */
const struct csky_cpu_info *cpu;
/* Flag for INSN_OPCODE16F, INSN_OPCODE32F, INSN_OPCODE, INSN_MACRO. */
inst_flag flag_force;
/* Operand number. */
int number;
struct csky_opcode *opcode;
struct csky_macro_info *macro;
/* Insn size for check_literal. */
unsigned int isize;
unsigned int last_isize;
/* Max size of insn for relax frag_var. */
unsigned int max;
/* Indicates which element is in csky_opcode_info op[] array. */
int opcode_idx;
/* The value of each operand in instruction when layout. */
int idx;
int val[MAX_OPRND_NUM];
struct relax_info
{
int max;
int var;
int subtype;
} relax;
/* The following are used for constant expressions. */
expressionS e1;
expressionS e2;
};
/* Literal pool data structures. */
struct literal
{
unsigned short refcnt;
unsigned int offset;
unsigned char ispcrel;
unsigned char unused;
bfd_reloc_code_real_type r_type;
expressionS e;
struct tls_addend tls_addend;
unsigned char isdouble;
uint64_t dbnum;
LITTLENUM_TYPE bignum[SIZE_OF_LARGE_NUMBER + 6];
};
static void csky_idly (void);
static void csky_rolc (void);
static void csky_sxtrb (void);
static void csky_movtf (void);
static void csky_addc64 (void);
static void csky_subc64 (void);
static void csky_or64 (void);
static void csky_xor64 (void);
static void csky_neg (void);
static void csky_rsubi (void);
static void csky_arith (void);
static void csky_decne (void);
static void csky_lrw (void);
static enum bfd_reloc_code_real insn_reloc;
/* Assembler operand parse errors use these identifiers. */
enum error_number
{
/* The following are errors. */
ERROR_CREG_ILLEGAL = 0,
ERROR_REG_OVER_RANGE,
ERROR_FREG_OVER_RANGE,
ERROR_VREG_OVER_RANGE,
ERROR_GREG_ILLEGAL,
ERROR_802J_REG_OVER_RANGE,
ERROR_REG_FORMAT,
ERROR_REG_LIST,
ERROR_IMM_ILLEGAL,
ERROR_IMM_OVERFLOW, /* 5 */
ERROR_IMM_POWER,
ERROR_JMPIX_OVER_RANGE,
ERROR_EXP_CREG,
ERROR_EXP_GREG,
ERROR_EXP_CONSTANT,
ERROR_EXP_EVEN_FREG,
ERROR_RELOC_ILLEGAL,
ERROR_MISSING_OPERAND, /* 10 */
ERROR_MISSING_COMMA,
ERROR_MISSING_LBRACKET,
ERROR_MISSING_RBRACKET,
ERROR_MISSING_LSQUARE_BRACKETS,
ERROR_MISSING_RSQUARE_BRACKETS, /* 15 */
ERROR_MISSING_LANGLE_BRACKETS,
ERROR_MISSING_RANGLE_BRACKETS,
ERROR_OFFSET_UNALIGNED,
ERROR_BAD_END,
ERROR_UNDEFINE,
ERROR_CPREG_ILLEGAL, /* 20 */
ERROR_OPCODE_PSRBIT,
ERROR_OPERANDS_ILLEGAL,
ERROR_OPERANDS_NUMBER,
ERROR_OPCODE_ILLEGAL,
/* The following are warnings. */
WARNING_OPTIONS,
WARNING_IDLY,
/* Error and warning end. */
ERROR_NONE,
};
/* Global error state. ARG1 and ARG2 are opaque data interpreted
as appropriate for the error code. */
struct csky_error_state
{
enum error_number err_num;
int opnum;
int arg_int;
const void *arg1;
const void *arg2;
} error_state;
/* This macro is used to set error number and arg1 in the global state. */
#define SET_ERROR_STRING(err, msg) \
do { \
if (error_state.err_num > err) \
{ \
error_state.err_num = err; \
error_state.arg1 = (void *)msg; \
} \
} while (0)
#define SET_ERROR_INTEGER(err, integer) \
do { \
if (error_state.err_num > err) \
{ \
error_state.err_num = err; \
error_state.arg_int = integer; \
} \
} while (0)
/* Map error identifiers onto a format string, which will use
arg1 and arg2 from the global error state. */
struct csky_error_format_map
{
enum error_number num;
const char *fmt;
};
static const struct csky_error_format_map err_formats[] =
{
{ERROR_CREG_ILLEGAL, "Operand %d error: control register is illegal."},
{ERROR_REG_OVER_RANGE, "Operand %d error: r%d register is over range."},
{ERROR_FREG_OVER_RANGE, "Operand %d error: vr%d register is over range."},
{ERROR_VREG_OVER_RANGE, "Operand %d error: vr%d register is out of range."},
{ERROR_GREG_ILLEGAL, "Operand %d error: general register is illegal."},
{ERROR_802J_REG_OVER_RANGE, "Operand %d register %s out of range (802j only has registers:0-15,23,24,25,30)"},
{ERROR_REG_FORMAT, "Operand %d error: %s."},
{ERROR_REG_LIST, "Register list format is illegal."},
{ERROR_IMM_ILLEGAL, "Operand %d is not an immediate."},
{ERROR_IMM_OVERFLOW, "Operand %d immediate is overflow."},
{ERROR_IMM_POWER, "immediate %d is not a power of two"},
{ERROR_JMPIX_OVER_RANGE, "The second operand must be 16/24/32/40"},
{ERROR_EXP_CREG, "Operand %d error: control register is expected."},
{ERROR_EXP_GREG, "Operand %d error: general register is expected."},
{ERROR_EXP_CONSTANT, "Operand %d error: constant is expected."},
{ERROR_EXP_EVEN_FREG, "Operand %d error: even float register is expected."},
{ERROR_RELOC_ILLEGAL, "@%s reloc is not supported"},
{ERROR_MISSING_OPERAND, "Operand %d is missing."},
{ERROR_MISSING_COMMA, "Missing ','"},
{ERROR_MISSING_LBRACKET, "Missing '('"},
{ERROR_MISSING_RBRACKET, "Missing ')'"},
{ERROR_MISSING_LSQUARE_BRACKETS, "Missing '['"},
{ERROR_MISSING_RSQUARE_BRACKETS, "Missing ']'"},
{ERROR_MISSING_LANGLE_BRACKETS, "Missing '<'"},
{ERROR_MISSING_RANGLE_BRACKETS, "Missing '>'"},
{ERROR_OFFSET_UNALIGNED, "Operand %d is unaligned. It must be %d aligned!"},
{ERROR_BAD_END, "Operands mismatch, it has a bad end: %s"},
{ERROR_UNDEFINE, NULL},
{ERROR_CPREG_ILLEGAL, "Operand %d illegal, expect a cpreg(cpr0-cpr63)."},
{ERROR_OPCODE_PSRBIT, "The operands must be 'ie'/'ee'/'fe'."},
{ERROR_OPERANDS_ILLEGAL, "Operands mismatch: %s."},
{ERROR_OPERANDS_NUMBER, "Operands number mismatch, %d operands expected."},
{ERROR_OPCODE_ILLEGAL, "The instruction is not recognized."},
{WARNING_OPTIONS, "Option %s is not support in %s."},
{WARNING_IDLY, "idly %d is encoded to: idly 4 "},
{ERROR_NONE, "There is no error."},
};
static int do_pic = 0; /* for jbr/jbf/jbt relax jmpi reloc. */
static int do_pff = -1; /* for insert two br ahead of literals. */
static int do_force2bsr = -1; /* for jbsr->bsr. */
static int do_jsri2bsr = 1; /* for jsri->bsr. */
static int do_nolrw = 0; /* lrw to movih & ori, only for V2. */
static int do_long_jump = -1; /* control if jbf,jbt,jbr relax to jmpi. */
static int do_extend_lrw = -1; /* delete bsr16 in both two options,
add btesti16, lrw offset +1 in -melrw. */
static int do_func_dump = 0; /* dump literals after every function. */
static int do_br_dump = 1; /* work for -mabr/-mno-abr, control the literals dump. */
static int do_intr_stack = -1; /* control interrupt stack module, 801&802&803
default on, 807&810, default off. */
static int float_abi = 0;
#ifdef INCLUDE_BRANCH_STUB
static int do_use_branchstub = -1;
#else
static int do_use_branchstub = 0;
#endif
/* These are only used for options parsing. Values are bitmasks and are
OR'ed into the processor flag bits in md_begin. */
static int do_opt_mmp = 0;
static int do_opt_mcp = 0;
static int do_opt_mcache = 0;
static int do_opt_msecurity = 0;
static int do_opt_mhard_float = 0;
static int do_opt_mtrust = 0;
static int do_opt_mdsp = 0;
static int do_opt_medsp = 0;
static int do_opt_mvdsp = 0;
const relax_typeS *md_relax_table = NULL;
struct literal *literal_insn_offset;
static struct literal litpool[MAX_POOL_SIZE];
static unsigned poolsize = 0;
static unsigned poolnumber = 0;
static unsigned long poolspan = 0;
static unsigned int SPANPANIC;
static unsigned int SPANCLOSE;
static unsigned int SPANEXIT;
static stack_size_entry *all_stack_size_data = NULL;
static stack_size_entry **last_stack_size_data = &all_stack_size_data;
/* Control by ".no_literal_dump N"
* 1 : don't dump literal pool between insn1 and insnN+1
* 0 : do nothing. */
static int do_noliteraldump = 0;
/* Label for current pool. */
static symbolS * poolsym;
static char poolname[8];
static bool mov_r1_before;
static bool mov_r1_after;
const relax_typeS csky_relax_table [] =
{
/* C-SKY V1 relax table. */
{0, 0, 0, 0}, /* RELAX_NONE */
{0, 0, 0, 0}, /* RELAX_OVERFLOW */
{0, 0, 0, 0},
{0, 0, 0, 0},
/* COND_JUMP */
{ 0, 0, 0, 0 }, /* UNDEF_DISP */
{ 2048, -2046, C12_LEN, C (COND_JUMP, DISP32) }, /* DISP12 */
{ 0, 0, C32_LEN, 0 }, /* DISP32 */
{ 0, 0, C32_LEN, 0 }, /* UNDEF_WORD_DISP */
/* UNCD_JUMP */
{ 0, 0, 0, 0 }, /* UNDEF_DISP */
{ 2048, -2046, U12_LEN, C (UNCD_JUMP, DISP32) }, /* DISP12 */
{ 0, 0, U32_LEN, 0 }, /* DISP32 */
{ 0, 0, U32_LEN, 0 }, /* UNDEF_WORD_DISP */
/* COND_JUMP_PIC */
{ 0, 0, 0, 0 }, /* UNDEF_DISP */
{ 2048, -2046, C12_LEN, C (COND_JUMP_PIC, DISP32) }, /* DISP12 */
{ 0, 0, C32_LEN_PIC, 0 }, /* DISP32 */
{ 0, 0, C32_LEN_PIC, 0 }, /* UNDEF_WORD_DISP */
/* UNCD_JUMP_PIC */
{ 0, 0, 0, 0 }, /* UNDEF_DISP */
{ 2048, -2046, U12_LEN, C (UNCD_JUMP_PIC, DISP32) }, /* DISP12 */
{ 0, 0, U32_LEN_PIC, 0 }, /* DISP32 */
{ 0, 0, U32_LEN_PIC, 0 }, /* UNDEF_WORD_DISP */
/* C-SKY V2 relax table. */
/* forward backward length more */
{ 1 KB - 2, -1 KB, COND_DISP10_LEN, COND_DISP16 }, /* COND_DISP10 */
{ 64 KB - 2, -64 KB, COND_DISP16_LEN, RELAX_OVERFLOW }, /* COND_DISP16 */
{ 1 KB - 2, -1 KB, SCOND_DISP10_LEN, SCOND_DISP16 }, /* SCOND_DISP10 */
{ 64 KB - 2, -64 KB, SCOND_DISP16_LEN, RELAX_OVERFLOW }, /* SCOND_DISP16 */
{ 1 KB - 2, -1 KB, UNCD_DISP10_LEN, UNCD_DISP16 }, /* UNCD_DISP10 */
{ 64 KB - 2, -64 KB, UNCD_DISP16_LEN, RELAX_OVERFLOW }, /* UNCD_DISP16 */
{ 1 KB - 2, -1 KB, JCOND_DISP10_LEN, JCOND_DISP16 }, /* JCOND_DISP10 */
{ 64 KB - 2, -64 KB, JCOND_DISP16_LEN, JCOND_DISP32 }, /* JCOND_DISP16 */
{ 0, 0, JCOND_DISP32_LEN, RELAX_NONE }, /* JCOND_DISP32 */
{ 1 KB - 2, -1 KB, JUNCD_DISP10_LEN, JUNCD_DISP16 }, /* JUNCD_DISP10 */
{ 64 KB - 2, -64 KB, JUNCD_DISP16_LEN, JUNCD_DISP32 }, /* JUNCD_DISP16 */
{ 0, 0, JUNCD_DISP32_LEN, RELAX_NONE }, /* JUNCD_DISP32 */
{ 64 KB - 2, -64 KB, JCOMPZ_DISP16_LEN, JCOMPZ_DISP32 }, /* JCOMPZ_DISP16 */
{ 0, 0, JCOMPZ_DISP32_LEN, RELAX_NONE }, /* JCOMPZ_DISP32 */
{ 64 MB - 2, -64 MB, BSR_DISP26_LEN, RELAX_OVERFLOW }, /* BSR_DISP26 */
{ 508, 0, LRW_DISP7_LEN, LRW_DISP16 }, /* LRW_DISP7 */
{ 1016, 0, LRW_DISP7_LEN, LRW_DISP16 }, /* LRW2_DISP8 */
{ 64 KB, 0, LRW_DISP16_LEN, RELAX_OVERFLOW }, /* LRW_DISP16 */
};
static void csky_write_insn (char *ptr, valueT use, int nbytes);
void md_number_to_chars (char * buf, valueT val, int n);
long md_pcrel_from_section (fixS * fixP, segT seg);
/* C-SKY architecture table. */
const struct csky_arch_info csky_archs[] =
{
{"ck510", CSKY_ARCH_510, bfd_mach_ck510},
{"ck610", CSKY_ARCH_610, bfd_mach_ck610},
{"ck801", CSKY_ARCH_801, bfd_mach_ck801},
{"ck802", CSKY_ARCH_802, bfd_mach_ck802},
{"ck803", CSKY_ARCH_803, bfd_mach_ck803},
{"ck807", CSKY_ARCH_807, bfd_mach_ck807},
{"ck810", CSKY_ARCH_810, bfd_mach_ck810},
{"ck860", CSKY_ARCH_860, bfd_mach_ck860},
{NULL, 0, 0}
};
#define CSKY_ARCH_807_BASE CSKY_ARCH_807 | CSKY_ARCH_DSP
#define CSKY_ARCH_810_BASE CSKY_ARCH_810 | CSKY_ARCH_DSP
struct csky_cpu_feature
{
const char unique;
unsigned int arch_flag;
bfd_uint64_t isa_flag;
};
struct csky_cpu_version
{
int r;
int p;
bfd_uint64_t isa_flag;
};
#define CSKY_FEATURE_MAX 10
#define CSKY_CPU_REVERISON_MAX 10
struct csky_cpu_info
{
const char *name;
unsigned int arch_flag;
bfd_uint64_t isa_flag;
struct csky_cpu_feature features[CSKY_FEATURE_MAX];
struct csky_cpu_version ver[CSKY_CPU_REVERISON_MAX];
};
#define FEATURE_DSP_EXT(isa) \
{'e', CSKY_ARCH_DSP, isa}
#define FEATURE_DSP(isa) \
{'d', CSKY_ARCH_DSP, isa}
#define FEATURE_MMU() \
{'m', 0, 0}
#define FEATURE_VDSP(isa) \
{'v', CSKY_ARCH_DSP, isa}
#define FEATURE_FLOAT(isa) \
{'f', CSKY_ARCH_FLOAT, isa}
#define FEATURE_TRUST(isa) \
{'t', 0, isa}
#define FEATURE_JAVA(isa) \
{'j', CSKY_ARCH_JAVA, isa}
#define FEATURE_SHIELD(isa) \
{'h', 0, isa}
#define CSKY_FEATURES_DEF_NULL() \
{{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_e(isa_e) \
{FEATURE_DSP_EXT(isa_e), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_t(isa_t) \
{FEATURE_TRUST(isa_t), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_f(isa_f) \
{FEATURE_FLOAT(isa_f), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_v(isa_v) \
{FEATURE_VDSP(isa_v), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_ef(isa_e, isa_f) \
{FEATURE_DSP_EXT(isa_e), \
FEATURE_FLOAT(isa_f), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_jt(isa_j, isa_t) \
{FEATURE_JAVA(isa_j), \
FEATURE_TRUST(isa_t), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_efht(isa_e, isa_f, isa_h, isa_t) \
{FEATURE_DSP_EXT(isa_e), \
FEATURE_FLOAT(isa_f), \
FEATURE_SHIELD(isa_h), \
FEATURE_TRUST(isa_t), \
{0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_efv(isa_e, isa_f, isa_v) \
{FEATURE_DSP_EXT(isa_e), \
FEATURE_FLOAT(isa_f), \
FEATURE_VDSP(isa_v), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_eft(isa_e, isa_f, isa_t) \
{FEATURE_DSP_EXT(isa_e), \
FEATURE_FLOAT(isa_f), \
FEATURE_TRUST(isa_t), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_d(isa_d) \
{FEATURE_DSP(isa_d), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_df(isa_d, isa_f) \
{FEATURE_DSP(isa_d), \
FEATURE_FLOAT(isa_f), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_ft(isa_f, isa_t) \
{FEATURE_FLOAT(isa_f), \
FEATURE_TRUST(isa_t), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_tv(isa_t, isa_v) \
{FEATURE_TRUST(isa_t), \
FEATURE_VDSP(isa_v), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_fv(isa_f, isa_v) \
{FEATURE_FLOAT(isa_f), \
FEATURE_VDSP(isa_v), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_dft(isa_d, isa_f, isa_t) \
{FEATURE_DSP(isa_d), \
FEATURE_FLOAT(isa_f), \
FEATURE_TRUST(isa_t), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_dfv(isa_d, isa_f, isa_v) \
{FEATURE_DSP(isa_d), \
FEATURE_FLOAT(isa_f), \
FEATURE_VDSP(isa_v), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_ftv(isa_f, isa_t, isa_v) \
{FEATURE_FLOAT(isa_f), \
FEATURE_TRUST(isa_t), \
FEATURE_VDSP(isa_v), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_FEATURES_DEF_eftv(isa_e, isa_f, isa_t, isa_v) \
{FEATURE_DSP_EXT(isa_e), \
FEATURE_FLOAT(isa_f), \
FEATURE_TRUST(isa_t), \
FEATURE_VDSP(isa_v), \
{0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_CPU_REVERISON_r0p0(isa) \
{0, 0, 0}
#define CSKY_CPU_REVERISON_r1p0(isa) \
{1, 0, isa}
#define CSKY_CPU_REVERISON_r2p0(isa) \
{2, 0, isa}
#define CSKY_CPU_REVERISON_r3p0(isa) \
{3, 0, isa}
#define CSKY_CPU_REVERISON_RESERVED() \
{{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}}
#define CSKY_CPU_REVERISON_R3(isa1, isa2, isa3) \
{CSKY_CPU_REVERISON_r1p0(isa1), \
CSKY_CPU_REVERISON_r2p0(isa2), \
CSKY_CPU_REVERISON_r3p0(isa3), \
{0}, {0}, {0}, {0}, {0}, {0}, {0}}
/* CSKY cpus table. */
const struct csky_cpu_info csky_cpus[] =
{
#define CSKYV1_ISA_DSP (CSKY_ISA_DSP | CSKY_ISA_MAC_DSP)
#define CSKY_ISA_510 (CSKYV1_ISA_E1)
#define CSKY_ISA_610 (CSKYV1_ISA_E1 | CSKY_ISA_CP)
{"ck510",
CSKY_ARCH_510,
CSKY_ISA_510,
CSKY_FEATURES_DEF_e(CSKYV1_ISA_DSP),
CSKY_CPU_REVERISON_RESERVED()},
{"ck520",
CSKY_ARCH_510 | CSKY_ARCH_MAC,
CSKY_ISA_510 | CSKY_ISA_MAC | CSKY_ISA_MAC_DSP,
CSKY_FEATURES_DEF_NULL(),
CSKY_CPU_REVERISON_RESERVED()},
{"ck610", CSKY_ARCH_610, CSKY_ISA_610,
CSKY_FEATURES_DEF_ef(CSKYV1_ISA_DSP, CSKY_ISA_FLOAT_E1),
CSKY_CPU_REVERISON_RESERVED()},
{"ck620",
CSKY_ARCH_610 | CSKY_ARCH_MAC,
CSKY_ISA_610 | CSKY_ISA_MAC | CSKY_ISA_MAC_DSP,
CSKY_FEATURES_DEF_NULL(),
CSKY_CPU_REVERISON_RESERVED()},
#define CSKY_ISA_801 (CSKYV2_ISA_E1 | CSKY_ISA_TRUST)
#define CSKYV2_ISA_DSP (CSKY_ISA_DSP | CSKY_ISA_DSP_1E2 | CSKY_ISA_DSPE60)
{"ck801",
CSKY_ARCH_801,
CSKY_ISA_801,
CSKY_FEATURES_DEF_t(0),
CSKY_CPU_REVERISON_RESERVED()},
#define CSKY_ISA_802 (CSKY_ISA_801 | CSKYV2_ISA_1E2 | CSKY_ISA_NVIC)
{"ck802",
CSKY_ARCH_802,
CSKY_ISA_802,
CSKY_FEATURES_DEF_jt(CSKY_ISA_JAVA, 0),
CSKY_CPU_REVERISON_RESERVED()},
#define CSKY_ISA_803 (CSKY_ISA_802 | CSKYV2_ISA_2E3 | CSKY_ISA_MP)
#define CSKY_ISA_803R1 (CSKYV2_ISA_3E3R1)
#define CSKY_ISA_803R2 (CSKYV2_ISA_3E3R1 | CSKYV2_ISA_3E3R2)
#define CSKY_ISA_803R3 (CSKYV2_ISA_3E3R1 | CSKYV2_ISA_3E3R2 | CSKYV2_ISA_3E3R3)
#define CSKY_ISA_FLOAT_803 (CSKY_ISA_FLOAT_E1 | CSKY_ISA_FLOAT_1E3)
#define CSKY_ISA_EDSP (CSKYV2_ISA_3E3R1 | CSKYV2_ISA_3E3R3 | CSKY_ISA_DSP_ENHANCE)
{"ck803s",
CSKY_ARCH_803,
CSKY_ISA_803 | CSKY_ISA_803R1,
CSKY_FEATURES_DEF_eft(CSKYV2_ISA_DSP, CSKY_ISA_FLOAT_803, 0),
CSKY_CPU_REVERISON_RESERVED()},
{"ck803",
CSKY_ARCH_803,
CSKY_ISA_803,
CSKY_FEATURES_DEF_efht(CSKYV2_ISA_DSP, CSKY_ISA_FLOAT_803, 0, 0),
CSKY_CPU_REVERISON_R3(CSKY_ISA_803R1, CSKY_ISA_803R2, CSKY_ISA_803R3)},
#define CSKY_ISA_804 (CSKY_ISA_803 | CSKY_ISA_803R3)
{"ck804",
CSKY_ARCH_804,
CSKY_ISA_804,
CSKY_FEATURES_DEF_efht(CSKY_ISA_EDSP, CSKY_ISA_FLOAT_803, 0, 0),
CSKY_CPU_REVERISON_RESERVED()},
#define CSKY_ISA_805 (CSKY_ISA_804 | CSKY_ISA_VDSP_2)
#define CSKY_ARCH_805V (CSKY_ARCH_805 | CSKY_ARCH_DSP)
#define CSKY_ISA_FLOAT_805 CSKY_ISA_FLOAT_803
{"ck805",
CSKY_ARCH_805,
CSKY_ISA_805,
CSKY_FEATURES_DEF_eft(CSKY_ISA_EDSP, CSKY_ISA_FLOAT_805, 0),
CSKY_CPU_REVERISON_RESERVED()},
#define CSKY_ISA_807 (CSKY_ISA_803 | CSKYV2_ISA_3E7 | CSKY_ISA_MP_1E2 | CSKY_ISA_CACHE | CSKYV2_ISA_DSP)
#define CSKY_ISA_FLOAT_807 (CSKY_ISA_FLOAT_803 | CSKY_ISA_FLOAT_3E4 | CSKY_ISA_FLOAT_1E2)
{"ck807",
CSKY_ARCH_807,
CSKY_ISA_807,
CSKY_FEATURES_DEF_ef(CSKYV2_ISA_DSP, CSKY_ISA_FLOAT_807),
CSKY_CPU_REVERISON_RESERVED()},
#define CSKY_ISA_810 (CSKY_ISA_807 | CSKYV2_ISA_7E10)
#define CSKY_ISA_FLOAT_810 (CSKY_ISA_FLOAT_E1 | CSKY_ISA_FLOAT_1E2)
{"ck810v",
CSKY_ARCH_810 | CSKY_ARCH_DSP,
CSKY_ISA_810 | CSKY_ISA_VDSP,
CSKY_FEATURES_DEF_NULL (),
CSKY_CPU_REVERISON_RESERVED()},
{"ck810",
CSKY_ARCH_810,
CSKY_ISA_810,
CSKY_FEATURES_DEF_eftv(0, CSKY_ISA_FLOAT_810, 0, CSKY_ISA_VDSP),
CSKY_CPU_REVERISON_RESERVED()},
#define CSKY_ISA_860 ((CSKY_ISA_810 & ~(CSKYV2_ISA_DSP)) | CSKYV2_ISA_10E60 | CSKY_ISA_803R3 | CSKY_ISA_DSPE60)
#define CSKY_ISA_860F (CSKY_ISA_860 | CSKY_ISA_FLOAT_7E60)
#define CSKY_ISA_VDSP_860 (CSKY_ISA_VDSP_2)
{"ck860v",
CSKY_ARCH_860 | CSKY_ARCH_DSP,
CSKY_ISA_860 | CSKY_ISA_VDSP_860,
CSKY_FEATURES_DEF_f(CSKY_ISA_FLOAT_7E60),
CSKY_CPU_REVERISON_RESERVED()},
{"ck860",
CSKY_ARCH_860,
CSKY_ISA_860,
CSKY_FEATURES_DEF_fv(CSKY_ISA_FLOAT_7E60, CSKY_ISA_VDSP_860),
CSKY_CPU_REVERISON_RESERVED()},
/* It is a special cpu, support all instructions. */
#define CSKY_ISA_800 (CSKY_ISA_860 | CSKY_ISA_810 | CSKY_ISA_807 | CSKY_ISA_803)
{"ck800",
CSKY_ARCH_800,
CSKY_ISA_800,
CSKY_FEATURES_DEF_NULL(),
CSKY_CPU_REVERISON_RESERVED()},
#define CSKY_ISA_E801 (CSKY_ISA_801)
#define CSKY_ISA_E802 (CSKY_ISA_E801 | CSKYV2_ISA_1E2 | CSKY_ISA_NVIC)
#define CSKY_ISA_E803 (CSKY_ISA_E802 | CSKYV2_ISA_2E3 | CSKY_ISA_MP | CSKYV2_ISA_3E3R1 | CSKYV2_ISA_3E3R2 | CSKYV2_ISA_3E3R3)
#define CSKY_ISA_E804 (CSKY_ISA_E803)
#define CSKY_ISA_FLOAT_V1 (CSKY_ISA_FLOAT_E1 | CSKY_ISA_FLOAT_1E3)
{"e801",
CSKY_ARCH_801,
CSKY_ISA_E801,
CSKY_FEATURES_DEF_NULL(),
CSKY_CPU_REVERISON_RESERVED()},
{"e802",
CSKY_ARCH_802,
CSKY_ISA_E802,
CSKY_FEATURES_DEF_t(0),
CSKY_CPU_REVERISON_RESERVED()},
{"e803",
CSKY_ARCH_803,
CSKY_ISA_E803,
CSKY_FEATURES_DEF_t(0),
CSKY_CPU_REVERISON_RESERVED()},
{"e804",
CSKY_ARCH_804,
CSKY_ISA_E804,
CSKY_FEATURES_DEF_dft(CSKY_ISA_EDSP, CSKY_ISA_FLOAT_V1, 0),
CSKY_CPU_REVERISON_RESERVED()},
#define CSKY_ISA_S802 (CSKY_ISA_E801 | CSKYV2_ISA_1E2 | CSKY_ISA_NVIC | CSKY_ISA_TRUST)
#define CSKY_ISA_S803 (CSKY_ISA_S802 | CSKYV2_ISA_2E3 | CSKY_ISA_MP | CSKYV2_ISA_3E3R1 | CSKYV2_ISA_3E3R2 | CSKYV2_ISA_3E3R3)
{"s802",
CSKY_ARCH_802,
CSKY_ISA_S802,
CSKY_FEATURES_DEF_t(0),
CSKY_CPU_REVERISON_RESERVED()},
{"s803",
CSKY_ARCH_803,
CSKY_ISA_S803,
CSKY_FEATURES_DEF_t(0),
CSKY_CPU_REVERISON_RESERVED()},
#define CSKY_ISA_I805 (CSKY_ISA_S803)
{"i805",
CSKY_ARCH_805 | CSKY_ARCH_DSP,
CSKY_ISA_I805 | CSKY_ISA_VDSP_2,
CSKY_FEATURES_DEF_ft(CSKY_ISA_FLOAT_V1, 0),
CSKY_CPU_REVERISON_RESERVED()},
#define CSKYV2_ISA_DSP (CSKY_ISA_DSP | CSKY_ISA_DSP_1E2 | CSKY_ISA_DSPE60)
#define CSKY_ISA_C807 (CSKY_ISA_E802 | CSKYV2_ISA_2E3 | CSKY_ISA_MP | CSKYV2_ISA_3E7 | CSKY_ISA_MP_1E2 | CSKY_ISA_CACHE | CSKYV2_ISA_DSP)
#define CSKY_ISA_FLOAT_C807 (CSKY_ISA_FLOAT_V1 | CSKY_ISA_FLOAT_3E4 | CSKY_ISA_FLOAT_1E2)
#define CSKY_ISA_FLOAT_C810 (CSKY_ISA_FLOAT_E1 | CSKY_ISA_FLOAT_1E2)
#define CSKY_ARCH_C810 (CSKY_ARCH_810 | CSKY_ARCH_FLOAT)
#define CSKY_ISA_C810 (CSKY_ISA_C807 | CSKYV2_ISA_7E10 | CSKY_ISA_FLOAT_C810)
#define CSKY_ARCH_C860 (CSKY_ARCH_860 | CSKY_ARCH_FLOAT)
#define CSKY_ISA_C860 (CSKY_ISA_860 | CSKY_ISA_FLOAT_7E60)
{"c807",
CSKY_ARCH_807,
CSKY_ISA_C807,
CSKY_FEATURES_DEF_fv(CSKY_ISA_FLOAT_C807, CSKY_ISA_VDSP),
CSKY_CPU_REVERISON_RESERVED()},
{"c810",
CSKY_ARCH_C810,
CSKY_ISA_C810,
CSKY_FEATURES_DEF_tv(0, CSKY_ISA_VDSP),
CSKY_CPU_REVERISON_RESERVED()},
{"c860",
CSKY_ARCH_C860,
CSKY_ISA_C860,
CSKY_FEATURES_DEF_v(CSKY_ISA_VDSP_2),
CSKY_CPU_REVERISON_RESERVED()},
#define CSKY_ISA_R807 (CSKY_ISA_E802 | CSKYV2_ISA_2E3 | CSKY_ISA_MP | CSKYV2_ISA_3E7 | CSKY_ISA_MP_1E2 | CSKY_ISA_CACHE | CSKYV2_ISA_DSP)
#define CSKY_ISA_FLOAT_R807 (CSKY_ISA_FLOAT_V1 | CSKY_ISA_FLOAT_3E4 | CSKY_ISA_FLOAT_1E2)
{"r807",
CSKY_ARCH_807,
CSKY_ISA_R807,
CSKY_FEATURES_DEF_f(CSKY_ISA_FLOAT_R807),
CSKY_CPU_REVERISON_RESERVED()},
/* Start of private CPUs. */
/* End of private CPUs. */
{NULL},
};
int md_short_jump_size = 2;
int md_long_jump_size = 4;
/* This array holds the chars that always start a comment. If the
pre-processor is disabled, these aren't very useful. */
const char comment_chars[] = "#";
/* This array holds the chars that only start a comment at the beginning of
a line. If the line seems to have the form '# 123 filename'
.line and .file directives will appear in the pre-processed output. */
/* Note that input_file.c hand checks for '#' at the beginning of the
first line of the input file. This is because the compiler outputs
#NO_APP at the beginning of its output. */
/* Also note that comments like this one will always work. */
const char line_comment_chars[] = "#";
const char line_separator_chars[] = ";";
/* Chars that can be used to separate mant
from exp in floating point numbers. */
const char EXP_CHARS[] = "eE";
/* Chars that mean this number is a floating point constant.
As in 0f12.456
or 0d1.2345e12 */
const char FLT_CHARS[] = "rRsSfFdDxXeEpP";
const char *md_shortopts = "";
struct option md_longopts[] = {
#define OPTION_MARCH (OPTION_MD_BASE + 0)
{"march", required_argument, NULL, OPTION_MARCH},
#define OPTION_MCPU (OPTION_MD_BASE + 1)
{"mcpu", required_argument, NULL, OPTION_MCPU},
#define OPTION_FLOAT_ABI (OPTION_MD_BASE + 2)
{"mfloat-abi", required_argument, NULL, OPTION_FLOAT_ABI},
/* Remaining options just set boolean flags. */
{"EL", no_argument, &target_big_endian, 0},
{"mlittle-endian", no_argument, &target_big_endian, 0},
{"EB", no_argument, &target_big_endian, 1},
{"mbig-endian", no_argument, &target_big_endian, 1},
{"fpic", no_argument, &do_pic, 1},
{"pic", no_argument, &do_pic, 1},
{"mljump", no_argument, &do_long_jump, 1},
{"mno-ljump", no_argument, &do_long_jump, 0},
{"force2bsr", no_argument, &do_force2bsr, 1},
{"mforce2bsr", no_argument, &do_force2bsr, 1},
{"no-force2bsr", no_argument, &do_force2bsr, 0},
{"mno-force2bsr", no_argument, &do_force2bsr, 0},
{"jsri2bsr", no_argument, &do_jsri2bsr, 1},
{"mjsri2bsr", no_argument, &do_jsri2bsr, 1},
{"no-jsri2bsr", no_argument, &do_jsri2bsr, 0},
{"mno-jsri2bsr", no_argument, &do_jsri2bsr, 0},
{"mnolrw", no_argument, &do_nolrw, 1},
{"mno-lrw", no_argument, &do_nolrw, 1},
{"melrw", no_argument, &do_extend_lrw, 1},
{"mno-elrw", no_argument, &do_extend_lrw, 0},
{"mlaf", no_argument, &do_func_dump, 1},
{"mliterals-after-func", no_argument, &do_func_dump, 1},
{"mno-laf", no_argument, &do_func_dump, 0},
{"mno-literals-after-func", no_argument, &do_func_dump, 0},
{"mlabr", no_argument, &do_br_dump, 1},
{"mliterals-after-br", no_argument, &do_br_dump, 1},
{"mno-labr", no_argument, &do_br_dump, 0},
{"mnoliterals-after-br", no_argument, &do_br_dump, 0},
{"mistack", no_argument, &do_intr_stack, 1},
{"mno-istack", no_argument, &do_intr_stack, 0},
#ifdef INCLUDE_BRANCH_STUB
{"mbranch-stub", no_argument, &do_use_branchstub, 1},
{"mno-branch-stub", no_argument, &do_use_branchstub, 0},
#endif
{"mhard-float", no_argument, &do_opt_mhard_float, CSKY_ARCH_FLOAT},
{"mmp", no_argument, &do_opt_mmp, CSKY_ARCH_MP},
{"mcp", no_argument, &do_opt_mcp, CSKY_ARCH_CP},
{"mcache", no_argument, &do_opt_mcache, CSKY_ARCH_CACHE},
{"msecurity", no_argument, &do_opt_msecurity, CSKY_ARCH_MAC},
{"mtrust", no_argument, &do_opt_mtrust, CSKY_ISA_TRUST},
{"mdsp", no_argument, &do_opt_mdsp, CSKY_DSP_FLAG_V1},
{"medsp", no_argument, &do_opt_medsp, CSKY_DSP_FLAG_V2},
{"mvdsp", no_argument, &do_opt_mvdsp, CSKY_ISA_VDSP},
};
size_t md_longopts_size = sizeof (md_longopts);
static struct csky_insn_info csky_insn;
static htab_t csky_opcodes_hash;
static htab_t csky_macros_hash;
static struct csky_macro_info v1_macros_table[] =
{
{"idly", 1, CSKYV1_ISA_E1, csky_idly},
{"rolc", 2, CSKYV1_ISA_E1, csky_rolc},
{"rotlc", 2, CSKYV1_ISA_E1, csky_rolc},
{"sxtrb0", 2, CSKYV1_ISA_E1, csky_sxtrb},
{"sxtrb1", 2, CSKYV1_ISA_E1, csky_sxtrb},
{"sxtrb2", 2, CSKYV1_ISA_E1, csky_sxtrb},
{"movtf", 3, CSKYV1_ISA_E1, csky_movtf},
{"addc64", 3, CSKYV1_ISA_E1, csky_addc64},
{"subc64", 3, CSKYV1_ISA_E1, csky_subc64},
{"or64", 3, CSKYV1_ISA_E1, csky_or64},
{"xor64", 3, CSKYV1_ISA_E1, csky_xor64},
{NULL,0,0,0}
};
static struct csky_macro_info v2_macros_table[] =
{
{"neg", 1, CSKYV2_ISA_E1, csky_neg},
{"rsubi", 2, CSKYV2_ISA_1E2, csky_rsubi},
{"incf", 1, CSKYV2_ISA_1E2, csky_arith},
{"inct", 1, CSKYV2_ISA_1E2, csky_arith},
{"decf", 1, CSKYV2_ISA_2E3, csky_arith},
{"decgt", 1, CSKYV2_ISA_2E3, csky_arith},
{"declt", 1, CSKYV2_ISA_2E3, csky_arith},
{"decne", 1, CSKYV2_ISA_1E2, csky_decne},
{"dect", 1, CSKYV2_ISA_1E2, csky_arith},
{"lslc", 1, CSKYV2_ISA_1E2, csky_arith},
{"lsrc", 1, CSKYV2_ISA_1E2, csky_arith},
{"xsr", 1, CSKYV2_ISA_1E2, csky_arith},
{NULL,0,0,0}
};
/* For option -mnolrw, replace lrw by movih & ori. */
static struct csky_macro_info v2_lrw_macro_opcode =
{"lrw", 2, CSKYV2_ISA_1E2, csky_lrw};
/* This function is used to show errors or warnings. */
static void
csky_show_error (enum error_number err, int idx, void *arg1, void *arg2)
{
if (err == ERROR_NONE)
return;
switch (err)
{
case ERROR_REG_LIST:
case ERROR_OPCODE_PSRBIT:
case ERROR_OPCODE_ILLEGAL:
case ERROR_JMPIX_OVER_RANGE:
case ERROR_MISSING_COMMA:
case ERROR_MISSING_LBRACKET:
case ERROR_MISSING_RBRACKET:
case ERROR_MISSING_LSQUARE_BRACKETS:
case ERROR_MISSING_RSQUARE_BRACKETS:
case ERROR_MISSING_LANGLE_BRACKETS:
case ERROR_MISSING_RANGLE_BRACKETS:
/* Add NULL to fix warnings. */
as_bad (_(err_formats[err].fmt), NULL);
break;
case ERROR_CREG_ILLEGAL:
case ERROR_GREG_ILLEGAL:
case ERROR_IMM_ILLEGAL:
case ERROR_IMM_OVERFLOW:
case ERROR_EXP_CREG:
case ERROR_EXP_GREG:
case ERROR_EXP_CONSTANT:
case ERROR_EXP_EVEN_FREG:
case ERROR_MISSING_OPERAND:
case ERROR_CPREG_ILLEGAL:
as_bad (_(err_formats[err].fmt), idx);
break;
case ERROR_OPERANDS_NUMBER:
case ERROR_IMM_POWER:
as_bad (_(err_formats[err].fmt), error_state.arg_int);
break;
case ERROR_OFFSET_UNALIGNED:
as_bad (_(err_formats[err].fmt), idx, error_state.arg_int);
break;
case ERROR_RELOC_ILLEGAL:
case ERROR_BAD_END:
case ERROR_OPERANDS_ILLEGAL:
as_bad (_(err_formats[err].fmt), (char *)arg1);
break;
case ERROR_REG_OVER_RANGE:
case ERROR_FREG_OVER_RANGE:
case ERROR_VREG_OVER_RANGE:
as_bad (_(err_formats[err].fmt), idx, error_state.arg_int);
break;
case ERROR_802J_REG_OVER_RANGE:
case ERROR_REG_FORMAT:
as_bad (_(err_formats[err].fmt), idx, (char *)arg1);
break;
case ERROR_UNDEFINE:
/* Add NULL to fix warnings. */
as_bad ((char *)arg1, NULL);
break;
case WARNING_IDLY:
as_warn (_(err_formats[err].fmt), (long)arg1);
break;
case WARNING_OPTIONS:
as_warn (_(err_formats[err].fmt), (char *)arg1, (char *)arg2);
break;
default:
break;
}
}
/* Handle errors in branch relaxation. */
static void
csky_branch_report_error (const char* file, unsigned int line,
symbolS* sym, offsetT val)
{
as_bad_where (file ? file : _("unknown"),
line,
_("pcrel offset for branch to %s too far (0x%lx)"),
sym ? S_GET_NAME (sym) : _("<unknown>"),
(long) val);
}
/* Set appropriate flags for the cpu matching STR. */
static void
parse_cpu (const char *str)
{
int i = 0;
for (; csky_cpus[i].name != NULL; i++)
if (strncasecmp (str, csky_cpus[i].name, strlen (csky_cpus[i].name)) == 0)
{
csky_insn.cpu = &csky_cpus[i];
mach_flag |= csky_cpus[i].arch_flag;
isa_flag = csky_cpus[i].isa_flag;
const char *s = str + strlen (csky_cpus[i].name);
while (*s)
{
const struct csky_cpu_feature *feature = csky_cpus[i].features;
const struct csky_cpu_version *version = csky_cpus[i].ver;
char *next;
if (*s == 'r')
{
s++;
while (version->r)
{
if (version->r == strtol (s, &next, 10))
break;
version++;
}
if (version->r)
{
isa_flag |= version->isa_flag;
s = next;
}
else
goto unknown_cpu;
isa_flag = isa_flag & ~CSKYV2_ISA_DSP;
isa_flag |= CSKY_ISA_EDSP;
continue;
}
/* Parse csky features. */
while (feature->unique)
{
if (feature->unique == *s)
break;
feature++;
}
if (feature->unique)
{
isa_flag |= feature->isa_flag;
mach_flag |= feature->arch_flag;
}
else
goto unknown_cpu;
s++;
}
return;
}
unknown_cpu:
as_bad (_("unknown cpu `%s'"), str);
}
/* Set appropriate flags for the arch matching STR. */
static void
parse_arch (const char *str)
{
int i = 0;
for (; csky_cpus[i].name != NULL; i++)
if (strcasecmp (str, csky_cpus[i].name) == 0)
{
csky_insn.cpu = &csky_cpus[i];
arch_flag |= csky_cpus[i].arch_flag;
isa_flag |= csky_cpus[i].isa_flag;
return;
}
as_bad (_("unknown architecture `%s'"), str);
}
struct csky_option_value_table
{
const char *name;
long value;
};
static const struct csky_option_value_table csky_float_abis[] =
{
{"hard", VAL_CSKY_FPU_ABI_HARD},
{"softfp", VAL_CSKY_FPU_ABI_SOFTFP},
{"soft", VAL_CSKY_FPU_ABI_SOFT},
{NULL, 0}
};
static bool
parse_float_abi (const char *str)
{
const struct csky_option_value_table * opt;
for (opt = csky_float_abis; opt->name != NULL; opt++)
if (strcasecmp (opt->name, str) == 0)
{
float_abi = opt->value;
return true;
}
as_bad (_("unknown floating point abi `%s'\n"), str);
return false;
}
#ifdef OBJ_ELF
/* Implement the TARGET_FORMAT macro. */
const char *
elf32_csky_target_format (void)
{
return (target_big_endian
? "elf32-csky-big"
: "elf32-csky-little");
}
#endif
/* Turn an integer of n bytes (in val) into a stream of bytes appropriate
for use in the a.out file, and stores them in the array pointed to by buf.
This knows about the endian-ness of the target machine and does
THE RIGHT THING, whatever it is. Possible values for n are 1 (byte)
2 (short) and 4 (long) Floating numbers are put out as a series of
LITTLENUMS (shorts, here at least). */
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);
}
/* Get a log2(val). */
static int
csky_log_2 (unsigned int val)
{
int log = -1;
if ((val & (val - 1)) == 0)
for (; val; val >>= 1)
log ++;
else
csky_show_error (ERROR_IMM_POWER, 0, (void *)(long)val, NULL);
return log;
}
/* Output one instruction to the buffer at PTR. */
static void
csky_write_insn (char *ptr, valueT use, int nbytes)
{
if (nbytes == 2)
md_number_to_chars (ptr, use, nbytes);
else /* 32-bit instruction. */
{
/* Significant figures are in low bits. */
md_number_to_chars (ptr, use >> 16, 2);
md_number_to_chars (ptr + 2, use & 0xFFFF, 2);
}
}
/* Read an NBYTES instruction from the buffer at PTR. NBYTES should
be either 2 or 4. This function is used in branch relaxation. */
static valueT
csky_read_insn (char *ptr, int nbytes)
{
unsigned char *uptr = (unsigned char *)ptr;
valueT v = 0;
int lo, hi; /* hi/lo byte index in binary stream. */
if (target_big_endian)
{
hi = 0;
lo = 1;
}
else
{
hi = 1;
lo = 0;
}
v = uptr[lo] | (uptr[hi] << 8);
if (nbytes == 4)
{
v <<= 16;
v |= uptr[lo + 2] | (uptr[hi + 2] << 8);
}
return v;
}
/* Construct a label name into S from the 3-character prefix P and
number N formatted as a 4-digit hex number. */
static void
make_internal_label (char *s, const char *p, int n)
{
static const char hex[] = "0123456789ABCDEF";
s[0] = p[0];
s[1] = p[1];
s[2] = p[2];
s[3] = hex[(n >> 12) & 0xF];
s[4] = hex[(n >> 8) & 0xF];
s[5] = hex[(n >> 4) & 0xF];
s[6] = hex[(n) & 0xF];
s[7] = 0;
}
/* md_operand is a no-op on C-SKY; we do everything elsewhere. */
void
md_operand (expressionS *expressionP ATTRIBUTE_UNUSED)
{
return;
}
/* Under ELF we need to default _GLOBAL_OFFSET_TABLE.
Otherwise we have no need to default values of symbols. */
symbolS *
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
{
#ifdef OBJ_ELF
/* TODO: */
#endif
return NULL;
}
/* Use IEEE format for floating-point constants. */
const char *
md_atof (int type, char *litP, int *sizeP)
{
return ieee_md_atof (type, litP, sizeP, target_big_endian);
}
/* Print option help to FP. */
void
md_show_usage (FILE *fp)
{
int i, n;
const int margin = 48;
fprintf (fp, _("C-SKY assembler options:\n"));
fprintf (fp, _("\
-march=ARCH select architecture ARCH:"));
for (i = 0, n = margin; csky_archs[i].name != NULL; i++)
{
int l = strlen (csky_archs[i].name);
if (n + l >= margin)
{
fprintf (fp, "\n\t\t\t\t");
n = l;
}
else
{
fprintf (fp, " ");
n += l + 1;
}
fprintf (fp, "%s", csky_archs[i].name);
}
fprintf (fp, "\n");
fprintf (fp, _("\
-mcpu=CPU select processor CPU:"));
const struct csky_cpu_feature *feature = NULL;
const struct csky_cpu_version *version = NULL;
for (i = 0; csky_cpus[i].name != NULL; i++)
{
fprintf (fp, "\t\t\t\t%s", csky_cpus[i].name);
feature = csky_cpus[i].features;
version = csky_cpus[i].ver;
while (feature->unique)
{
if ((feature + 1)->unique)
fprintf (fp, "[%c]", feature->unique);
feature++;
}
while (version->r)
{
if (csky_cpus[i].name[0] == 'c'
&& csky_cpus[i].name[1] == 'k')
fprintf (fp, "[r%d]", version->r);
else
fprintf (fp, "[-r%dp%d]", version->r, version->p);
version++;
}
}
fprintf (fp, "\n");
fprintf (fp, _("\
-mfloat-abi=ABI select float ABI:"));
for (i = 0, n = margin; csky_float_abis[i].name != NULL; i++)
{
int l = strlen (csky_float_abis[i].name);
if (n + l >= margin)
{
fprintf (fp, "\n\t\t\t\t");
n = l;
}
else
{
fprintf (fp, " ");
n += l + 1;
}
fprintf (fp, "%s", csky_float_abis[i].name);
}
fprintf (fp, "\n");
fprintf (fp, _("\
-EL -mlittle-endian generate little-endian output\n"));
fprintf (fp, _("\
-EB -mbig-endian generate big-endian output\n"));
fprintf (fp, _("\
-fpic -pic generate position-independent code\n"));
fprintf (fp, _("\
-mljump transform jbf, jbt, jbr to jmpi (CK800 only)\n"));
fprintf (fp, _("\
-mno-ljump\n"));
#ifdef INCLUDE_BRANCH_STUB
fprintf (fp, _("\
-mbranch-stub enable branch stubs for PC-relative calls\n"));
fprintf (fp, _("\
-mno-branch-stub\n"));
#endif
fprintf (fp, _("\
-force2bsr -mforce2bsr transform jbsr to bsr\n"));
fprintf (fp, _("\
-no-force2bsr -mno-force2bsr\n"));
fprintf (fp, _("\
-jsri2bsr -mjsri2bsr transform jsri to bsr\n"));
fprintf (fp, _("\
-no-jsri2bsr -mno-jsri2bsr\n"));
fprintf (fp, _("\
-mnolrw -mno-lrw implement lrw as movih + ori\n"));
fprintf (fp, _("\
-melrw enable extended lrw (CK800 only)\n"));
fprintf (fp, _("\
-mno-elrw\n"));
fprintf (fp, _("\
-mlaf -mliterals-after-func emit literals after each function\n"));
fprintf (fp, _("\
-mno-laf -mno-literals-after-func\n"));
fprintf (fp, _("\
-mlabr -mliterals-after-br emit literals after branch instructions\n"));
fprintf (fp, _("\
-mno-labr -mnoliterals-after-br\n"));
fprintf (fp, _("\
-mistack enable interrupt stack instructions\n"));
fprintf (fp, _("\
-mno-istack\n"));
fprintf (fp, _("\
-mhard-float enable hard float instructions\n"));
fprintf (fp, _("\
-mmp enable multiprocessor instructions\n"));
fprintf (fp, _("\
-mcp enable coprocessor instructions\n"));
fprintf (fp, _("\
-mcache enable cache prefetch instruction\n"));
fprintf (fp, _("\
-msecurity enable security instructions\n"));
fprintf (fp, _("\
-mtrust enable trust instructions\n"));
fprintf (fp, _("\
-mdsp enable DSP instructions\n"));
fprintf (fp, _("\
-medsp enable enhanced DSP instructions\n"));
fprintf (fp, _("\
-mvdsp enable vector DSP instructions\n"));
}
static void set_csky_attribute (void)
{
if (mach_flag & CSKY_ARCH_DSP)
{
if (dsp_flag & CSKY_DSP_FLAG_V2)
{
/* Set DSPV2. */
bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_PROC,
Tag_CSKY_DSP_VERSION,
VAL_CSKY_DSP_VERSION_2);
}
else if (isa_flag & CSKY_ISA_DSP)
{
/* Set DSP extension. */
bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_PROC,
Tag_CSKY_DSP_VERSION,
VAL_CSKY_DSP_VERSION_EXTENSION);
}
/* Set VDSP attribute. */
if (isa_flag & CSKY_ISA_VDSP)
bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_PROC,
Tag_CSKY_VDSP_VERSION,
VAL_CSKY_VDSP_VERSION_1);
else if (isa_flag & CSKY_ISA_VDSP_2)
bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_PROC,
Tag_CSKY_VDSP_VERSION,
VAL_CSKY_VDSP_VERSION_2);
}
if (mach_flag & CSKY_ARCH_FLOAT)
{
unsigned int val = VAL_CSKY_FPU_HARDFP_SINGLE;
if (IS_CSKY_ARCH_V1 (mach_flag)) {
bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_PROC,
Tag_CSKY_FPU_VERSION,
VAL_CSKY_FPU_VERSION_1);
}
else
{
if (isa_flag & CSKY_ISA_FLOAT_3E4)
{
bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_PROC,
Tag_CSKY_FPU_VERSION,
VAL_CSKY_FPU_VERSION_2);
val |= VAL_CSKY_FPU_HARDFP_DOUBLE;
}
else
{
bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_PROC,
Tag_CSKY_FPU_VERSION,
VAL_CSKY_FPU_VERSION_2);
}
bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_PROC,
Tag_CSKY_FPU_HARDFP,
val);
bfd_elf_add_obj_attr_string (stdoutput, OBJ_ATTR_PROC,
Tag_CSKY_FPU_NUMBER_MODULE,
"IEEE 754");
bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_PROC,
Tag_CSKY_FPU_ABI,
float_abi);
}
}
bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_PROC,
Tag_CSKY_ISA_FLAGS, isa_flag);
bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_PROC,
Tag_CSKY_ISA_EXT_FLAGS, (isa_flag >> 32));
}
/* Target-specific initialization and option handling. */
void
md_begin (void)
{
unsigned int bfd_mach_flag = 0;
struct csky_opcode const *opcode;
struct csky_macro_info const *macro;
struct csky_arch_info const *p_arch;
struct csky_cpu_info const *p_cpu;
other_flag = (do_opt_mmp | do_opt_mcp | do_opt_mcache
| do_opt_msecurity | do_opt_mhard_float);
dsp_flag |= do_opt_mdsp | do_opt_medsp;
isa_flag |= do_opt_mtrust | do_opt_mvdsp;
if (dsp_flag)
other_flag |= CSKY_ARCH_DSP;
if (mach_flag != 0)
{
if (((mach_flag & CSKY_ARCH_MASK)
!= (arch_flag & CSKY_ARCH_MASK))
&& arch_flag != 0)
as_warn ("-mcpu conflict with -march option, actually use -mcpu");
}
else if (arch_flag != 0)
mach_flag |= arch_flag | other_flag;
else
{
#ifdef TARGET_WITH_CPU
parse_cpu (TARGET_WITH_CPU);
#else
#if _CSKY_ABI==1
parse_cpu ("ck610");
#else
parse_cpu ("ck810");
#endif
mach_flag |= other_flag;
#endif
}
if (IS_CSKY_ARCH_610 (mach_flag) || IS_CSKY_ARCH_510 (mach_flag))
{
if ((mach_flag & CSKY_ARCH_MP) && (mach_flag & CSKY_ARCH_MAC))
as_fatal ("520/620 conflicts with -mmp option");
else if ((mach_flag & CSKY_ARCH_MP) && (mach_flag & CSKY_ARCH_DSP))
as_fatal ("510e/610e conflicts with -mmp option");
else if ((mach_flag & CSKY_ARCH_DSP) && (mach_flag & CSKY_ARCH_MAC))
as_fatal ("520/620 conflicts with 510e/610e or -mdsp option");
}
if (IS_CSKY_ARCH_510 (mach_flag) && (mach_flag & CSKY_ARCH_FLOAT))
{
mach_flag = (mach_flag & (~CSKY_ARCH_MASK));
mach_flag |= CSKY_ARCH_610;
}
/* Find bfd_mach_flag, it will set to bfd backend data. */
for (p_arch = csky_archs; p_arch->arch_flag != 0; p_arch++)
if ((mach_flag & CSKY_ARCH_MASK) == (p_arch->arch_flag & CSKY_ARCH_MASK))
{
bfd_elf_add_obj_attr_string (stdoutput, OBJ_ATTR_PROC,
Tag_CSKY_ARCH_NAME, p_arch->name);
bfd_mach_flag = p_arch->bfd_mach_flag;
break;
}
/* Find isa_flag. */
for (p_cpu = csky_cpus; p_cpu->arch_flag != 0; p_cpu++)
if ((mach_flag & CPU_ARCH_MASK) == p_cpu->arch_flag)
{
bfd_elf_add_obj_attr_string (stdoutput, OBJ_ATTR_PROC,
Tag_CSKY_CPU_NAME, p_cpu->name);
isa_flag |= p_cpu->isa_flag;
break;
}
/* Check if -mdsp and -medsp conflict. If cpu is ck803, we will
use enhanced dsp instruction. Otherwise, we will use normal dsp. */
if (dsp_flag)
{
if (IS_CSKY_ARCH_803 (mach_flag))
{
if ((dsp_flag & CSKY_DSP_FLAG_V1))
{
if (isa_flag & CSKY_ISA_DSP_ENHANCE)
{
/* Option -mdsp conflicts with -mcpu=ck803ern,
CPU already indicates the dsp version. */
as_warn ("Option -mdsp conflicts with -mcpu=ck803ern which "
"has indicated DSP version, ignoring -mdsp.");
isa_flag &= ~(CSKY_ISA_MAC_DSP | CSKY_ISA_DSP);
isa_flag |= CSKY_ISA_DSP_ENHANCE;
}
else
{
isa_flag |= (CSKY_ISA_MAC_DSP | CSKY_ISA_DSP);
isa_flag &= ~CSKY_ISA_DSP_ENHANCE;
}
}
if ((dsp_flag & CSKY_DSP_FLAG_V2))
{
isa_flag &= ~(CSKY_ISA_MAC_DSP | CSKY_ISA_DSP);
isa_flag |= CSKY_ISA_DSP_ENHANCE;
}
if ((dsp_flag & CSKY_DSP_FLAG_V1)
&& (dsp_flag & CSKY_DSP_FLAG_V2))
{
/* In 803, dspv1 is conflict with dspv2. We keep dspv2. */
as_warn ("option -mdsp conflicts with -medsp, only enabling -medsp");
dsp_flag &= ~CSKY_DSP_FLAG_V1;
isa_flag &= ~(CSKY_ISA_MAC_DSP | CSKY_ISA_DSP);
isa_flag |= CSKY_ISA_DSP_ENHANCE;
}
}
else
{
if (dsp_flag & CSKY_DSP_FLAG_V2)
{
dsp_flag &= ~CSKY_DSP_FLAG_V2;
isa_flag &= ~CSKY_ISA_DSP_ENHANCE;
as_warn ("-medsp option is only supported by ck803s, ignoring -medsp");
}
}
;
}
if (do_use_branchstub == -1)
do_use_branchstub = !IS_CSKY_ARCH_V1 (mach_flag);
else if (do_use_branchstub == 1)
{
if (IS_CSKY_ARCH_V1 (mach_flag))
{
as_warn (_("C-SKY ABI v1 (ck510/ck610) does not support -mbranch-stub"));
do_use_branchstub = 0;
}
else if (do_force2bsr == 0)
{
as_warn (_("-mno-force2bsr is ignored with -mbranch-stub"));
do_force2bsr = 1;
}
}
if (IS_CSKY_ARCH_801 (mach_flag) || IS_CSKY_ARCH_802 (mach_flag))
{
if (!do_force2bsr)
as_warn (_("-mno-force2bsr is ignored for ck801/ck802"));
do_force2bsr = 1;
}
else if (do_force2bsr == -1)
do_force2bsr = do_use_branchstub;
if (do_pff == -1)
{
if (IS_CSKY_ARCH_V1 (mach_flag))
do_pff = 1;
else
do_pff = 0;
}
if (do_extend_lrw == -1)
{
if ((mach_flag & CSKY_ARCH_MASK) == CSKY_ARCH_801
|| (mach_flag & CSKY_ARCH_MASK) == CSKY_ARCH_802
|| (mach_flag & CSKY_ARCH_MASK) == CSKY_ARCH_803
|| (mach_flag & CSKY_ARCH_MASK) == CSKY_ARCH_860)
do_extend_lrw = 1;
else
do_extend_lrw = 0;
}
if (IS_CSKY_ARCH_801 (mach_flag) || IS_CSKY_ARCH_802 (mach_flag))
{
if (do_long_jump > 0)
as_warn (_("-mljump is ignored for ck801/ck802"));
do_long_jump = 0;
}
else if (do_long_jump == -1)
do_long_jump = 1;
if (do_intr_stack == -1)
{
/* control interrupt stack module, 801&802&803 default on
807&810, default off. */
if (IS_CSKY_ARCH_807 (mach_flag) || IS_CSKY_ARCH_810 (mach_flag))
do_intr_stack = 0;
else
do_intr_stack = 1;
}
/* Add isa_flag(SIMP/CACHE/APS). */
isa_flag |= (mach_flag & CSKY_ARCH_MAC) ? CSKY_ISA_MAC : 0;
isa_flag |= (mach_flag & CSKY_ARCH_MP) ? CSKY_ISA_MP : 0;
isa_flag |= (mach_flag & CSKY_ARCH_CP) ? CSKY_ISA_CP : 0;
/* Set abi flag and get table address. */
if (IS_CSKY_ARCH_V1 (mach_flag))
{
mach_flag = mach_flag | CSKY_ABI_V1;
opcode = csky_v1_opcodes;
macro = v1_macros_table;
SPANPANIC = v1_SPANPANIC;
SPANCLOSE = v1_SPANCLOSE;
SPANEXIT = v1_SPANEXIT;
md_relax_table = csky_relax_table;
}
else
{
mach_flag = mach_flag | CSKY_ABI_V2;
opcode = csky_v2_opcodes;
macro = v2_macros_table;
SPANPANIC = v2_SPANPANIC;
if (do_extend_lrw)
{
SPANCLOSE = v2_SPANCLOSE_ELRW;
SPANEXIT = v2_SPANEXIT_ELRW;
}
else
{
SPANCLOSE = v2_SPANCLOSE;
SPANEXIT = v2_SPANEXIT;
}
md_relax_table = csky_relax_table;
}
/* Establish hash table for opcodes and macros. */
csky_macros_hash = str_htab_create ();
csky_opcodes_hash = str_htab_create ();
for ( ; opcode->mnemonic != NULL; opcode++)
if ((isa_flag & (opcode->isa_flag16 | opcode->isa_flag32)) != 0)
str_hash_insert (csky_opcodes_hash, opcode->mnemonic, opcode, 0);
for ( ; macro->name != NULL; macro++)
if ((isa_flag & macro->isa_flag) != 0)
str_hash_insert (csky_macros_hash, macro->name, macro, 0);
if (do_nolrw && (isa_flag & CSKYV2_ISA_1E2) != 0)
str_hash_insert (csky_macros_hash,
v2_lrw_macro_opcode.name, &v2_lrw_macro_opcode, 0);
/* Set e_flag to ELF Head. */
bfd_set_private_flags (stdoutput, mach_flag | CSKY_VERSION_V1);
/* Set bfd_mach to bfd backend data. */
bfd_set_arch_mach (stdoutput, bfd_arch_csky, bfd_mach_flag);
set_csky_attribute ();
}
/* The C-SKY assembler emits mapping symbols $t and $d to mark the
beginning of a sequence of instructions and data (such as a constant pool),
respectively. This is similar to what ARM does. */
static void
make_mapping_symbol (map_state state, valueT value, fragS *frag)
{
symbolS * symbolP;
const char * symname;
int type;
switch (state)
{
case MAP_DATA:
symname = "$d";
type = BSF_NO_FLAGS;
break;
case MAP_TEXT:
symname = "$t";
type = BSF_NO_FLAGS;
break;
default:
abort ();
}
symbolP = symbol_new (symname, now_seg, frag, value);
symbol_get_bfdsym (symbolP)->flags |= type | BSF_LOCAL;
}
/* We need to keep track of whether we are emitting code or data; this
function switches state and emits a mapping symbol if necessary. */
static void
mapping_state (map_state state)
{
map_state current_state
= seg_info (now_seg)->tc_segment_info_data.current_state;
if (current_state == state)
return;
else if (current_state == MAP_UNDEFINED && state == MAP_DATA)
return;
else if (current_state == MAP_UNDEFINED && state == MAP_TEXT)
{
struct frag * const frag_first = seg_info (now_seg)->frchainP->frch_root;
if (frag_now != frag_first || frag_now_fix () > 0)
make_mapping_symbol (MAP_DATA, (valueT) 0, frag_first);
}
seg_info (now_seg)->tc_segment_info_data.current_state = state;
make_mapping_symbol (state, (valueT) frag_now_fix (), frag_now);
}
/* Dump the literal pool. */
static void
dump_literals (int isforce)
{
#define CSKYV1_BR_INSN 0xF000
#define CSKYV2_BR_INSN 0x0400
unsigned int i;
struct literal * p;
symbolS * brarsym = NULL;
/* V1 nop encoding: 0x1200 : mov r0, r0. */
static char v1_nop_insn_big[2] = {0x12, 0x00};
static char v1_nop_insn_little[2] = {0x00, 0x12};
if (poolsize == 0)
return;
/* Must we branch around the literal table? */
if (isforce)
{
char brarname[8];
make_internal_label (brarname, POOL_END_LABEL, poolnumber);
brarsym = symbol_make (brarname);
symbol_table_insert (brarsym);
mapping_state (MAP_TEXT);
if (IS_CSKY_ARCH_V1 (mach_flag))
{
csky_insn.output
= frag_var (rs_machine_dependent,
csky_relax_table[C (UNCD_JUMP_S, DISP32)].rlx_length,
csky_relax_table[C (UNCD_JUMP_S, DISP12)].rlx_length,
C (UNCD_JUMP_S, 0), brarsym, 0, 0);
md_number_to_chars (csky_insn.output, CSKYV1_BR_INSN, 2);
}
else
{
csky_insn.output
= frag_var (rs_machine_dependent,
UNCD_DISP16_LEN,
UNCD_DISP10_LEN,
UNCD_DISP10,
brarsym, 0, 0);
md_number_to_chars (csky_insn.output, CSKYV2_BR_INSN, 2);
}
}
/* Make sure that the section is sufficiently aligned and that
the literal table is aligned within it. */
if (do_pff)
{
valueT br_self;
csky_insn.output = frag_more (2);
/* .Lxx: br .Lxx */
if (IS_CSKY_V1 (mach_flag))
br_self = CSKYV1_BR_INSN | 0x7ff;
else
br_self = CSKYV2_BR_INSN;
md_number_to_chars (csky_insn.output, br_self, 2);
if (!isforce)
{
csky_insn.output = frag_more (2);
/* .Lxx: br .Lxx */
md_number_to_chars (csky_insn.output, br_self, 2);
}
}
mapping_state (MAP_DATA);
record_alignment (now_seg, 2);
if (IS_CSKY_ARCH_V1 (mach_flag))
frag_align_pattern (2,
(target_big_endian
? v1_nop_insn_big : v1_nop_insn_little),
2, 0);
else
frag_align (2, 0, 3);
colon (S_GET_NAME (poolsym));
for (i = 0, p = litpool; i < poolsize; p++)
{
insn_reloc = p->r_type;
if (insn_reloc == BFD_RELOC_CKCORE_TLS_IE32
|| insn_reloc == BFD_RELOC_CKCORE_TLS_LDM32
|| insn_reloc == BFD_RELOC_CKCORE_TLS_GD32)
literal_insn_offset = p;
if (p->isdouble)
{
if (target_big_endian)
{
p->e.X_add_number = p->dbnum >> 32;
emit_expr (& p->e, 4);
p->e.X_add_number = p->dbnum & 0xffffffff;
emit_expr (& p->e, 4);
}
else
{
p->e.X_add_number = p->dbnum & 0xffffffff;
emit_expr (& p->e, 4);
p->e.X_add_number = p->dbnum >> 32;
emit_expr (& p->e, 4);
}
}
else if (p->e.X_op == O_big)
{
memcpy (generic_bignum, p->bignum, sizeof (p->bignum));
emit_expr (& p->e, p->e.X_add_number * CHARS_PER_LITTLENUM);
}
else
emit_expr (& p->e, 4);
if (p->e.X_op == O_big)
i += (p->e.X_add_number & 1) +
((p->e.X_add_number * CHARS_PER_LITTLENUM) >> 2);
else
i += (p->isdouble ? 2 : 1);
}
if (isforce && IS_CSKY_ARCH_V2 (mach_flag))
{
/* Add one nop insn at end of literal for disassembler. */
mapping_state (MAP_TEXT);
csky_insn.output = frag_more (2);
md_number_to_chars (csky_insn.output, CSKYV2_INST_NOP, 2);
}
insn_reloc = BFD_RELOC_NONE;
if (brarsym != NULL)
colon (S_GET_NAME (brarsym));
poolsize = 0;
}
static struct literal *
enter_literal (expressionS *e,
int ispcrel,
unsigned char isdouble,
uint64_t dbnum)
{
unsigned int i;
struct literal * p;
if (poolsize >= MAX_POOL_SIZE - 2)
{
/* The literal pool is as full as we can handle. We have
to be 2 entries shy of the 1024/4=256 entries because we
have to allow for the branch (2 bytes) and the alignment
(2 bytes before the first insn referencing the pool and
2 bytes before the pool itself) == 6 bytes, rounds up
to 2 entries. */
/* Save the parsed symbol's reloc. */
enum bfd_reloc_code_real last_reloc_before_dump = insn_reloc;
dump_literals (1);
insn_reloc = last_reloc_before_dump;
}
if (poolsize == 0)
{
/* Create new literal pool. */
if (++ poolnumber > 0xFFFF)
as_fatal (_("more than 65K literal pools"));
make_internal_label (poolname, POOL_START_LABEL, poolnumber);
poolsym = symbol_make (poolname);
symbol_table_insert (poolsym);
poolspan = 0;
}
/* Search pool for value so we don't have duplicates. */
for (p = litpool,i = 0; i < poolsize; p++)
{
if (e->X_op == p->e.X_op
&& e->X_add_symbol == p->e.X_add_symbol
&& e->X_add_number == p->e.X_add_number
&& ispcrel == p->ispcrel
&& insn_reloc == p->r_type
&& isdouble == p->isdouble
&& insn_reloc != BFD_RELOC_CKCORE_TLS_GD32
&& insn_reloc != BFD_RELOC_CKCORE_TLS_LDM32
&& insn_reloc != BFD_RELOC_CKCORE_TLS_LDO32
&& insn_reloc != BFD_RELOC_CKCORE_TLS_IE32
&& insn_reloc != BFD_RELOC_CKCORE_TLS_LE32
&& (e->X_op != O_big
|| (memcmp (generic_bignum, p->bignum,
p->e.X_add_number * sizeof (LITTLENUM_TYPE)) == 0)))
{
p->refcnt ++;
return p;
}
if (p->e.X_op == O_big)
{
i += (p->e.X_add_number >> 1);
i += (p->e.X_add_number & 0x1);
}
else
i += (p->isdouble ? 2 : 1);
}
p->refcnt = 1;
p->ispcrel = ispcrel;
p->e = *e;
p->r_type = insn_reloc;
p->isdouble = isdouble;
p->offset = i;
if (isdouble)
p->dbnum = dbnum;
if (e->X_op == O_big)
memcpy (p->bignum, generic_bignum, sizeof (p->bignum));
if (insn_reloc == BFD_RELOC_CKCORE_TLS_GD32
|| insn_reloc == BFD_RELOC_CKCORE_TLS_LDM32
|| insn_reloc == BFD_RELOC_CKCORE_TLS_IE32)
{
p->tls_addend.frag = frag_now;
p->tls_addend.offset = csky_insn.output - frag_now->fr_literal;
literal_insn_offset = p;
}
if (p->e.X_op == O_big) {
poolsize += (p->e.X_add_number >> 1);
poolsize += (p->e.X_add_number & 0x1);
} else
poolsize += (p->isdouble ? 2 : 1);
return p;
}
/* Check whether we must dump the literal pool here.
kind == 0 is any old instruction.
kind > 0 means we just had a control transfer instruction.
kind == 1 means within a function.
kind == 2 means we just left a function.
OFFSET is the length of the insn being processed.
SPANCLOSE and SPANEXIT are smaller numbers than SPANPANIC.
SPANPANIC means that we must dump now.
The dump_literals (1) call inserts a branch around the table, so
we first look to see if its a situation where we won't have to
insert a branch (e.g., the previous instruction was an unconditional
branch).
SPANPANIC is the point where we must dump a single-entry pool.
it accounts for alignments and an inserted branch.
the 'poolsize*2' accounts for the scenario where we do:
lrw r1,lit1; lrw r2,lit2; lrw r3,lit3
Note that the 'lit2' reference is 2 bytes further along
but the literal it references will be 4 bytes further along,
so we must consider the poolsize into this equation.
This is slightly over-cautious, but guarantees that we won't
panic because a relocation is too distant. */
static void
check_literals (int kind, int offset)
{
poolspan += offset;
if ((poolspan > SPANEXIT || do_func_dump)
&& kind > 1
&& (do_br_dump || do_func_dump))
dump_literals (0);
else if (poolspan > SPANCLOSE && (kind > 0) && do_br_dump)
dump_literals (0);
else if (poolspan
>= (SPANPANIC - (IS_CSKY_ARCH_V1 (mach_flag) ? poolsize * 2 : 0)))
dump_literals (1);
/* We have not dumped literal pool before insn1,
and will not dump literal pool between insn1 and insnN+1,
so reset poolspan to original length. */
else if (do_noliteraldump == 1)
poolspan -= offset;
if (do_noliteraldump == 1)
do_noliteraldump = 0;
}
/* The next group of functions are helpers for parsing various kinds
of instruction operand syntax. */
/* Parse operands of the form
<symbol>@GOTOFF+<nnn>
and similar .plt or .got references.
If we find one, set up the correct relocation in RELOC and copy the
input string, minus the `@GOTOFF' into a malloc'd buffer for
parsing by the calling routine. Return this buffer, and if ADJUST
is non-null set it to the length of the string we removed from the
input line. Otherwise return NULL. */
static char *
lex_got (enum bfd_reloc_code_real *reloc,
int *adjust)
{
struct _gotrel
{
const char *str;
const enum bfd_reloc_code_real rel;
};
static const struct _gotrel gotrel[] =
{
{ "GOTOFF", BFD_RELOC_CKCORE_GOTOFF },
{ "GOTPC", BFD_RELOC_CKCORE_GOTPC },
{ "GOTTPOFF", BFD_RELOC_CKCORE_TLS_IE32 },
{ "GOT", BFD_RELOC_CKCORE_GOT32 },
{ "PLT", BFD_RELOC_CKCORE_PLT32 },
{ "BTEXT", BFD_RELOC_CKCORE_TOFFSET_LO16},
{ "BDATA", BFD_RELOC_CKCORE_DOFFSET_LO16},
{ "TLSGD32", BFD_RELOC_CKCORE_TLS_GD32 },
{ "TLSLDM32", BFD_RELOC_CKCORE_TLS_LDM32 },
{ "TLSLDO32", BFD_RELOC_CKCORE_TLS_LDO32 },
{ "TPOFF", BFD_RELOC_CKCORE_TLS_LE32 }
};
char *cp;
unsigned int j;
for (cp = input_line_pointer; *cp != '@'; cp++)
if (is_end_of_line[(unsigned char) *cp])
return NULL;
for (j = 0; j < sizeof (gotrel) / sizeof (gotrel[0]); j++)
{
int len = strlen (gotrel[j].str);
if (strncasecmp (cp + 1, gotrel[j].str, len) == 0)
{
if (gotrel[j].rel != 0)
{
*reloc = gotrel[j].rel;
if (adjust)
*adjust = len;
/* input_line_pointer is the str pointer after relocation
token like @GOTOFF. */
input_line_pointer += len + 1;
return input_line_pointer;
}
csky_show_error (ERROR_RELOC_ILLEGAL, 0,
(void *)gotrel[j].str, NULL);
return NULL;
}
}
/* Might be a symbol version string. Don't as_bad here. */
return NULL;
}
/* Parse an expression, returning it in E. */
static char *
parse_exp (char *s, expressionS *e)
{
char *save;
char *new;
/* Skip whitespace. */
while (ISSPACE (*s))
++s;
save = input_line_pointer;
input_line_pointer = s;
insn_reloc = BFD_RELOC_NONE;
expression (e);
lex_got (&insn_reloc, NULL);
if (e->X_op == O_absent)
SET_ERROR_STRING (ERROR_MISSING_OPERAND, NULL);
new = input_line_pointer;
input_line_pointer = save;
return new;
}
/* Parse a floating-point number from S into its target representation.
If ISDOUBLE is true, return the result in *DBNUM; otherwise
it's returned in E->X_add_number. Returns the result of advancing
S past the constant. */
static char *
parse_fexp (char *s, expressionS *e, unsigned char isdouble, uint64_t *dbnum)
{
int length; /* Number of chars in an object. */
const char *err = NULL; /* Error from scanning float literal. */
unsigned char temp[8];
/* input_line_pointer->1st char of a flonum (we hope!). */
input_line_pointer = s;
if (input_line_pointer[0] == '0'
&& ISALPHA (input_line_pointer[1]))
input_line_pointer += 2;
if (isdouble)
err = md_atof ('d', (char *) temp, &length);
else
err = md_atof ('f', (char *) temp, &length);
know (length <= 8);
know (err != NULL || length > 0);
if (!is_end_of_line[(unsigned char) *input_line_pointer])
as_bad (_("immediate operand required"));
while (!is_end_of_line[(unsigned char) *input_line_pointer])
input_line_pointer++;
if (err)
{
as_bad (_("bad floating literal: %s"), err);
while (!is_end_of_line[(unsigned char) *input_line_pointer])
input_line_pointer++;
know (is_end_of_line[(unsigned char) input_line_pointer[-1]]);
return input_line_pointer;
}
e->X_add_symbol = 0x0;
e->X_op_symbol = 0x0;
e->X_op = O_constant;
e->X_unsigned = 1;
e->X_md = 0x0;
if (!isdouble)
{
uint32_t fnum;
if (target_big_endian)
fnum = (((uint32_t) temp[0] << 24)
| (temp[1] << 16)
| (temp[2] << 8)
| temp[3]);
else
fnum = (((uint32_t) temp[3] << 24)
| (temp[2] << 16)
| (temp[1] << 8)
| temp[0]);
e->X_add_number = fnum;
}
else
{
if (target_big_endian)
{
*dbnum = (((uint32_t) temp[0] << 24)
| (temp[1] << 16)
| (temp[2] << 8)
| temp[3]);
*dbnum <<= 32;
*dbnum |= (((uint32_t) temp[4] << 24)
| (temp[5] << 16)
| (temp[6] << 8)
| temp[7]);
}
else
{
*dbnum = (((uint32_t) temp[7] << 24)
| (temp[6] << 16)
| (temp[5] << 8)
| temp[4]);
*dbnum <<= 32;
*dbnum |= (((uint32_t) temp[3] << 24)
| (temp[2] << 16)
| (temp[1] << 8)
| temp[0]);
}
}
return input_line_pointer;
}
static char *
parse_rt (char *s,
int ispcrel,
expressionS *ep,
long reg ATTRIBUTE_UNUSED)
{
expressionS e;
if (ep)
/* Indicate nothing there. */
ep->X_op = O_absent;
if (*s == '[')
{
s = parse_exp (s + 1, &e);
if (*s == ']')
s++;
else
SET_ERROR_STRING (ERROR_MISSING_RSQUARE_BRACKETS, NULL);
if (ep)
*ep = e;
}
else
{
s = parse_exp (s, &e);
if (BFD_RELOC_CKCORE_DOFFSET_LO16 == insn_reloc
|| BFD_RELOC_CKCORE_TOFFSET_LO16 == insn_reloc)
{
if (ep)
*ep = e;
return s;
}
if (ep)
*ep = e;
/* If the instruction has work, literal handling is in the work. */
if (!csky_insn.opcode->work)
{
struct literal *p = enter_literal (&e, ispcrel, 0, 0);
if (ep)
*ep = e;
/* Create a reference to pool entry. */
ep->X_op = O_symbol;
ep->X_add_symbol = poolsym;
ep->X_add_number = p->offset << 2;
}
}
return s;
}
static int float_to_half (void *f, void *h)
{
int imm_e;
int imm_f;
unsigned int value_f = *(unsigned int *)f;
unsigned short value_h;
imm_e = ((value_f >> 23) & 0xff);
imm_f = ((value_f & 0x7fffff));
imm_e = ((imm_e - 127 + 15) << 10);
imm_f = ((imm_f & 0x7fe000) >> 13);
value_h = (value_f & 0x80000000 ? 0x8000 : 0x0) | imm_e | imm_f;
if (h)
*(unsigned short *)h = value_h;
return value_h;
}
static char *
parse_rtf (char *s, int ispcrel, expressionS *ep)
{
expressionS e;
struct literal *p = NULL;
if (ep)
/* Indicate nothing there. */
ep->X_op = O_absent;
if (*s == '[')
{
s = parse_exp (s + 1, & e);
if (*s == ']')
s++;
else
as_bad (_("missing ']'"));
if (ep)
*ep = e;
}
else
{
uint64_t dbnum;
if (strstr(csky_insn.opcode->mnemonic, "flrws")
|| strstr(csky_insn.opcode->mnemonic, "flrw.32"))
{
s = parse_fexp (s, &e, 0, &dbnum);
p = enter_literal (& e, ispcrel, 0, dbnum);
}
else if (strstr(csky_insn.opcode->mnemonic, "flrwd")
|| strstr(csky_insn.opcode->mnemonic, "flrw.64"))
{
s = parse_fexp (s, &e, 1, &dbnum);
p = enter_literal (& e, ispcrel, 1, dbnum);
}
else if (strstr(csky_insn.opcode->mnemonic, "flrwh")
|| strstr(csky_insn.opcode->mnemonic, "flrw.16"))
{
s = parse_fexp (s, &e, 0, NULL);
e.X_add_number = float_to_half (&e.X_add_number, &e.X_add_number);
p = enter_literal (& e, ispcrel, 0, 0);
}
else
as_bad (_("unrecognized opcode"));
if (ep)
*ep = e;
/* Create a reference to pool entry. */
ep->X_op = O_symbol;
ep->X_add_symbol = poolsym;
ep->X_add_number = p->offset << 2;
}
return s;
}
static bool
parse_type_ctrlreg (char** oper)
{
int i = -1;
int group = 0;
int crx;
int sel;
char *s = *oper;
expressionS e;
if (TOLOWER (*(*oper + 0)) == 'c'
&& TOLOWER (*(*oper + 1)) == 'r'
&& ISDIGIT (*(*oper + 2)))
{
/* The control registers are named crxx. */
s = *oper+2;
s = parse_exp (s, &e);
if (e.X_op == O_constant)
{
i = e.X_add_number;
*oper = s;
}
}
if (IS_CSKY_V2 (mach_flag))
{
s = *oper;
if (i != -1)
{
crx = i;
sel = group;
}
else if (TOLOWER (*(*oper + 0)) == 'c'
&& TOLOWER (*(*oper + 1)) == 'r')
{
s += 2;
if (*s != '<')
{
SET_ERROR_STRING (ERROR_CREG_ILLEGAL, s);
return false;
}
s++;
crx = strtol(s, &s, 10);
if (crx < 0 || crx > 31 || *s != ',')
{
SET_ERROR_STRING (ERROR_CREG_ILLEGAL, s);
return false;
}
s++;
sel = strtol(s, &s, 10);
if (sel < 0 || sel > 31 || *s != '>')
{
SET_ERROR_STRING (ERROR_CREG_ILLEGAL, s);
return false;
}
s++;
}
else
{
crx = csky_get_control_regno (mach_flag, s, &s, &sel);
if (crx < 0)
{
SET_ERROR_STRING (ERROR_CREG_ILLEGAL, s);
return false;
}
}
i = (sel << 5) | crx;
}
else if (i == -1)
{
i = csky_get_control_regno (mach_flag, s, &s, &sel);
if (i < 0)
{
SET_ERROR_STRING (ERROR_CREG_ILLEGAL, s);
return false;
}
}
*oper = s;
csky_insn.val[csky_insn.idx++] = i;
return true;
}
static int
csky_get_reg_val (char *str, int *len)
{
int regno = 0;
char *s = str;
regno = csky_get_general_regno (mach_flag, str, &s);
*len = (s - str);
return regno;
}
static bool
is_reg_sp_with_bracket (char **oper)
{
int reg;
int sp_idx;
int len;
if (IS_CSKY_V1 (mach_flag))
sp_idx = 0;
else
sp_idx = 14;
if (**oper != '(')
return false;
*oper += 1;
reg = csky_get_reg_val (*oper, &len);
*oper += len;
if (reg == sp_idx)
{
if (**oper != ')')
{
SET_ERROR_STRING (ERROR_UNDEFINE,
"Operand format is error. '(sp)' expected");
return false;
}
*oper += 1;
csky_insn.val[csky_insn.idx++] = sp_idx;
return true;
}
SET_ERROR_STRING (ERROR_UNDEFINE,
"Operand format is error. '(sp)' expected");
return false;
}
static bool
is_reg_sp (char **oper)
{
char sp_name[16];
int sp_idx;
int len;
if (IS_CSKY_V1 (mach_flag))
sp_idx = 0;
else
sp_idx = 14;
/* ABI names: "sp". */
if (memcmp (*oper, "sp", 2) == 0)
{
*oper += 2;
csky_insn.val[csky_insn.idx++] = sp_idx;
return true;
}
len = sprintf (sp_name, "r%d", sp_idx);
if (memcmp (*oper, sp_name, len) == 0)
{
*oper += len;
csky_insn.val[csky_insn.idx++] = sp_idx;
return true;
}
return false;
}
static int
csky_get_freg_val (char *str, int *len)
{
int reg = 0;
char *s = NULL;
if ((TOLOWER(str[0]) == 'v' || TOLOWER(str[0]) == 'f')
&& (TOLOWER(str[1]) == 'r'))
{
/* It is fpu register. */
s = &str[2];
while (ISDIGIT (*s))
{
reg = reg * 10 + (*s) - '0';
s++;
}
if (reg > 31)
return -1;
}
else
return -1;
*len = s - str;
return reg;
}
static bool
is_reglist_legal (char **oper)
{
int reg1 = -1;
int reg2 = -1;
int len = 0;
reg1 = csky_get_reg_val (*oper, &len);
*oper += len;
if (reg1 == -1 || (IS_CSKY_V1 (mach_flag) && (reg1 == 0 || reg1 == 15)))
{
SET_ERROR_STRING (ERROR_REG_FORMAT,
"The first reg must not be r0/r15");
return false;
}
if (**oper != '-')
{
SET_ERROR_STRING (ERROR_REG_FORMAT,
"The operand format must be rx-ry");
return false;
}
*oper += 1;
reg2 = csky_get_reg_val (*oper, &len);
*oper += len;
if (reg2 == -1 || (IS_CSKY_V1 (mach_flag) && reg1 == 15))
{
SET_ERROR_STRING (ERROR_REG_FORMAT,
"The operand format must be r15 in C-SKY V1");
return false;
}
if (IS_CSKY_V2 (mach_flag))
{
if (reg2 < reg1)
{
SET_ERROR_STRING (ERROR_REG_FORMAT,
"The operand format must be rx-ry (rx < ry)");
return false;
}
reg2 = reg2 - reg1;
reg1 <<= 5;
reg1 |= reg2;
}
csky_insn.val[csky_insn.idx++] = reg1;
return true;
}
static bool
is_freglist_legal (char **oper)
{
int reg1 = -1;
int reg2 = -1;
int len = 0;
int shift = 0;
reg1 = csky_get_freg_val (*oper, &len);
*oper += len;
if (reg1 == -1)
{
SET_ERROR_STRING (ERROR_REG_FORMAT,
"The fpu register format is not recognized.");
return false;
}
if (**oper != '-')
{
SET_ERROR_STRING (ERROR_REG_FORMAT,
"The operand format must be vrx-vry/frx-fry.");
return false;
}
*oper += 1;
reg2 = csky_get_freg_val (*oper, &len);
*oper += len;
if (reg2 == -1)
{
SET_ERROR_STRING (ERROR_REG_FORMAT,
"The fpu register format is not recognized.");
return false;
}
if (reg2 < reg1)
{
SET_ERROR_STRING (ERROR_REG_FORMAT,
"The operand format must be rx-ry(rx < ry)");
return false;
}
reg2 = reg2 - reg1;
/* The fldm/fstm in CSKY_ISA_FLOAT_7E60 has 5 bits frz(reg1). */
shift = 4;
if (startswith (csky_insn.opcode->mnemonic, "fstm")
|| startswith (csky_insn.opcode->mnemonic, "fldm"))
{
if ((!(isa_flag & CSKY_ISA_FLOAT_7E60)
&& (reg2 > (int)15 || reg1 > 15))
|| ((isa_flag & CSKY_ISA_FLOAT_7E60)
&& (reg2 > (int)31 || reg1 > (int)31)))
{
/* ISA_FLOAT_E1 fstm/fldm fry-frx is within 15.
ISA_FLOAT_7E60 fstm(u)/fldm(u) frx-fry is within 31. */
SET_ERROR_STRING(ERROR_REG_FORMAT, (void *)"frx-fry is over range");
return false;
}
if ((mach_flag & CSKY_ARCH_MASK) == CSKY_ARCH_860)
{
shift = 5;
}
}
else
{
if (reg2 > (int)0x3) {
SET_ERROR_STRING(ERROR_REG_FORMAT, (void *)"vry-vrx is over range");
return false;
}
}
reg2 <<= shift;
reg1 |= reg2;
csky_insn.val[csky_insn.idx++] = reg1;
return true;
}
static bool
is_reglist_dash_comma_legal (char **oper, struct operand *oprnd)
{
int reg1 = -1;
int reg2 = -1;
int len = 0;
int list = 0;
int flag = 0;
int temp = 0;
while (**oper != '\n' && **oper != '\0')
{
reg1 = csky_get_reg_val (*oper, &len);
if (reg1 == -1)
{
SET_ERROR_STRING (ERROR_REG_LIST, NULL);
return false;
}
flag |= (1 << reg1);
*oper += len;
if (**oper == '-')
{
*oper += 1;
reg2 = csky_get_reg_val (*oper, &len);
if (reg2 == -1)
{
SET_ERROR_STRING (ERROR_REG_LIST, NULL);
return false;
}
*oper += len;
if (reg1 > reg2)
{
SET_ERROR_STRING (ERROR_REG_LIST, NULL);
return false;
}
while (reg2 >= reg1)
{
flag |= (1 << reg2);
reg2--;
}
}
if (**oper == ',')
*oper += 1;
}
/* The reglist: r4-r11, r15, r16-r17, r28. */
#define REGLIST_BITS 0x10038ff0
if (flag & ~(REGLIST_BITS))
{
SET_ERROR_STRING (ERROR_REG_LIST, NULL);
return false;
}
/* Check r4-r11. */
int i = 4;
while (i <= 11)
{
if (flag & (1 << i))
temp = i - 4 + 1;
i++;
}
list |= temp;
/* Check r15. */
if (flag & (1 << 15))
list |= (1 << 4);
/* Check r16-r17. */
i = 16;
temp = 0;
while (i <= 17)
{
if (flag & (1 << i))
temp = i - 16 + 1;
i++;
}
list |= (temp << 5);
/* Check r28. */
if (flag & (1 << 28))
list |= (1 << 8);
if (oprnd->mask == OPRND_MASK_0_4 && (list & ~OPRND_MASK_0_4))
{
SET_ERROR_STRING (ERROR_REG_LIST, NULL);
return false;
}
csky_insn.val[csky_insn.idx++] = list;
return true;
}
static bool
is_reg_lshift_illegal (char **oper, int is_float)
{
int value;
int len;
int reg;
reg = csky_get_reg_val (*oper, &len);
if (reg == -1)
{
SET_ERROR_STRING (ERROR_REG_FORMAT, "The register must be r0-r31.");
return false;
}
*oper += len;
if ((*oper)[0] != '<' || (*oper)[1] != '<')
{
SET_ERROR_STRING (ERROR_UNDEFINE,
"Operand format error; should be (rx, ry << n)");
return false;
}
*oper += 2;
expressionS e;
char *new_oper = parse_exp (*oper, &e);
if (e.X_op == O_constant)
{
*oper = new_oper;
/* The immediate must be in [0, 3]. */
if (e.X_add_number < 0 || e.X_add_number > 3)
{
SET_ERROR_STRING (ERROR_IMM_OVERFLOW, NULL);
return false;
}
}
else
{
SET_ERROR_STRING (ERROR_EXP_CONSTANT, NULL);
return false;
}
if (is_float)
value = (reg << 2) | e.X_add_number;
else
value = (reg << 5) | (1 << e.X_add_number);
csky_insn.val[csky_insn.idx++] = value;
return true;
}
static bool
is_imm_within_range (char **oper, int min, int max)
{
expressionS e;
bool ret = false;
char *new_oper = parse_exp (*oper, &e);
if (e.X_op == O_constant)
{
ret = true;
*oper = new_oper;
if (e.X_add_number < min || e.X_add_number > max)
{
ret = false;
SET_ERROR_STRING (ERROR_IMM_OVERFLOW, NULL);
}
if (!e.X_unsigned)
e.X_add_number |= 0x80000000;
csky_insn.val[csky_insn.idx++] = e.X_add_number;
}
else
SET_ERROR_STRING(ERROR_IMM_ILLEGAL, NULL);
return ret;