#include <stdio.h>
#include "as.h"
#include "safe-ctype.h"
#include "subsegs.h"
#include "opcode/cris.h"
#include "dwarf2dbg.h"
#define SYNTAX_RELAX_REG_PREFIX "no_register_prefix"
#define SYNTAX_ENFORCE_REG_PREFIX "register_prefix"
#define SYNTAX_USER_SYM_LEADING_UNDERSCORE "leading_underscore"
#define SYNTAX_USER_SYM_NO_LEADING_UNDERSCORE "no_leading_underscore"
#define REGISTER_PREFIX_CHAR '$'
#define SIMPLE_EXPR(EXP) \
((EXP)->X_op == O_constant || (EXP)->X_op == O_symbol)
#define PIC_SUFFIX_CHAR ':'
enum cris_insn_kind
{
CRIS_INSN_NORMAL, CRIS_INSN_NONE, CRIS_INSN_BRANCH, CRIS_INSN_MUL
};
enum prefix_kind
{
PREFIX_NONE, PREFIX_BDAP_IMM, PREFIX_BDAP, PREFIX_BIAP, PREFIX_DIP,
PREFIX_PUSH
};
struct cris_prefix
{
enum prefix_kind kind;
int base_reg_number;
unsigned int opcode;
expressionS expr;
enum bfd_reloc_code_real reloc;
};
struct cris_instruction
{
enum cris_insn_kind insn_type;
const struct cris_spec_reg *spec_reg;
unsigned int opcode;
expressionS expr;
enum bfd_reloc_code_real reloc;
int imm_oprnd_size;
};
enum cris_archs
{
arch_cris_unknown,
arch_crisv0, arch_crisv3, arch_crisv8, arch_crisv10,
arch_cris_any_v0_v10, arch_crisv32, arch_cris_common_v10_v32
};
static enum cris_archs cris_arch_from_string PARAMS ((char **));
static int cris_insn_ver_valid_for_arch PARAMS ((enum cris_insn_version_usage,
enum cris_archs));
static void cris_process_instruction PARAMS ((char *,
struct cris_instruction *,
struct cris_prefix *));
static int get_bwd_size_modifier PARAMS ((char **, int *));
static int get_bw_size_modifier PARAMS ((char **, int *));
static int get_gen_reg PARAMS ((char **, int *));
static int get_spec_reg PARAMS ((char **,
const struct cris_spec_reg **));
static int get_sup_reg PARAMS ((char **, int *));
static int get_autoinc_prefix_or_indir_op PARAMS ((char **,
struct cris_prefix *,
int *, int *, int *,
expressionS *));
static int get_3op_or_dip_prefix_op PARAMS ((char **,
struct cris_prefix *));
static int cris_get_expression PARAMS ((char **, expressionS *));
static int get_flags PARAMS ((char **, int *));
static void gen_bdap PARAMS ((int, expressionS *));
static int branch_disp PARAMS ((int));
static void gen_cond_branch_32 PARAMS ((char *, char *, fragS *,
symbolS *, symbolS *, long int));
static void cris_number_to_imm PARAMS ((char *, long, int, fixS *, segT));
static void cris_create_short_jump PARAMS ((char *, addressT, addressT,
fragS *, symbolS *));
static void s_syntax PARAMS ((int));
static void s_cris_file PARAMS ((int));
static void s_cris_loc PARAMS ((int));
static void s_cris_arch PARAMS ((int));
static void cris_get_pic_suffix PARAMS ((char **,
bfd_reloc_code_real_type *,
expressionS *));
static unsigned int cris_get_pic_reloc_size
PARAMS ((bfd_reloc_code_real_type));
static void cris_force_reg_prefix PARAMS ((void));
static void cris_relax_reg_prefix PARAMS ((void));
static void cris_sym_leading_underscore PARAMS ((void));
static void cris_sym_no_leading_underscore PARAMS ((void));
static char *cris_insn_first_word_frag PARAMS ((void));
static struct hash_control *op_hash = NULL;
#ifdef TE_LINUX
#define DEFAULT_CRIS_AXIS_LINUX_GNU TRUE
#else
#define DEFAULT_CRIS_AXIS_LINUX_GNU FALSE
#endif
static bfd_boolean demand_register_prefix = DEFAULT_CRIS_AXIS_LINUX_GNU;
static bfd_boolean symbols_have_leading_underscore
= !DEFAULT_CRIS_AXIS_LINUX_GNU;
static bfd_boolean pic = FALSE;
#ifndef DEFAULT_CRIS_ARCH
#define DEFAULT_CRIS_ARCH cris_any_v0_v10
#endif
static enum cris_archs cris_arch = XCONCAT2 (arch_,DEFAULT_CRIS_ARCH);
const pseudo_typeS md_pseudo_table[] =
{
{"dword", cons, 4},
{"syntax", s_syntax, 0},
{"file", s_cris_file, 0},
{"loc", s_cris_loc, 0},
{"arch", s_cris_arch, 0},
{NULL, 0, 0}
};
static int warn_for_branch_expansion = 0;
static int err_for_dangerous_mul_placement
= (XCONCAT2 (arch_,DEFAULT_CRIS_ARCH) != arch_crisv32);
const char cris_comment_chars[] = ";";
const char line_comment_chars[] = "#";
const char line_separator_chars[] = "@";
const char EXP_CHARS[] = "";
const char FLT_CHARS[] = "";
#define STATE_COND_BRANCH (1)
#define STATE_BASE_PLUS_DISP_PREFIX (2)
#define STATE_MUL (3)
#define STATE_COND_BRANCH_V32 (4)
#define STATE_COND_BRANCH_COMMON (5)
#define STATE_ABS_BRANCH_V32 (6)
#define STATE_LAPC (7)
#define STATE_LENGTH_MASK (3)
#define STATE_BYTE (0)
#define STATE_WORD (1)
#define STATE_DWORD (2)
#define STATE_UNDF (3)
#define STATE_MAX_LENGTH (3)
#define BRANCH_BF ( 254)
#define BRANCH_BB (-256)
#define BRANCH_BF_V32 ( 252)
#define BRANCH_BB_V32 (-258)
#define BRANCH_WF (2 + 32767)
#define BRANCH_WB (2 + -32768)
#define BRANCH_WF_V32 (-2 + 32767)
#define BRANCH_WB_V32 (-2 + -32768)
#define BDAP_BF ( 127)
#define BDAP_BB (-128)
#define BDAP_WF ( 32767)
#define BDAP_WB (-32768)
#define ENCODE_RELAX(what, length) (((what) << 2) + (length))
const relax_typeS md_cris_relax_table[] =
{
{1, 1, 0, 0},
{1, 1, 0, 0},
{1, 1, 0, 0},
{1, 1, 0, 0},
{BRANCH_BF, BRANCH_BB, 0, ENCODE_RELAX (1, 1)},
{BRANCH_WF, BRANCH_WB, 2, ENCODE_RELAX (1, 2)},
{0, 0, 10, 0},
{1, 1, 0, 0},
{BDAP_BF, BDAP_BB, 0, ENCODE_RELAX (2, 1)},
{BDAP_WF, BDAP_WB, 2, ENCODE_RELAX (2, 2)},
{0, 0, 4, 0},
{1, 1, 0, 0},
{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0},
{BRANCH_BF_V32, BRANCH_BB_V32, 0, ENCODE_RELAX (4, 1)},
{BRANCH_WF_V32, BRANCH_WB_V32, 2, ENCODE_RELAX (4, 2)},
{0, 0, 12, 0},
{1, 1, 0, 0},
{BRANCH_BF_V32, BRANCH_BB_V32, 0, ENCODE_RELAX (5, 1)},
{BRANCH_WF_V32, BRANCH_WB_V32, 2, ENCODE_RELAX (5, 2)},
{0, 0, 12, 0},
{1, 1, 0, 0},
{BRANCH_BF_V32, BRANCH_BB_V32, 0, ENCODE_RELAX (6, 1)},
{BRANCH_WF_V32, BRANCH_WB_V32, 2, ENCODE_RELAX (6, 2)},
{0, 0, 4, 0},
{1, 1, 0, 0},
{14*2, -1*2, 0, ENCODE_RELAX (7, 2)},
{1, 1, 0, 0},
{0, 0, 4, 0},
{1, 1, 0, 0}
};
#undef BDAP_BF
#undef BDAP_BB
#undef BDAP_WF
#undef BDAP_WB
struct option md_longopts[] =
{
#define OPTION_NO_US (OPTION_MD_BASE + 0)
{"no-underscore", no_argument, NULL, OPTION_NO_US},
#define OPTION_US (OPTION_MD_BASE + 1)
{"underscore", no_argument, NULL, OPTION_US},
#define OPTION_PIC (OPTION_US + 1)
{"pic", no_argument, NULL, OPTION_PIC},
#define OPTION_MULBUG_ABORT_ON (OPTION_PIC + 1)
{"mul-bug-abort", no_argument, NULL, OPTION_MULBUG_ABORT_ON},
#define OPTION_MULBUG_ABORT_OFF (OPTION_MULBUG_ABORT_ON + 1)
{"no-mul-bug-abort", no_argument, NULL, OPTION_MULBUG_ABORT_OFF},
#define OPTION_ARCH (OPTION_MULBUG_ABORT_OFF + 1)
{"march", required_argument, NULL, OPTION_ARCH},
{NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof (md_longopts);
const char *md_shortopts = "hHN";
int md_short_jump_size = 6;
#define cris_any_v0_v10_long_jump_size 6
#define crisv32_long_jump_size 8
int md_long_jump_size = XCONCAT2 (DEFAULT_CRIS_ARCH,_long_jump_size);
const char *
cris_target_format ()
{
switch (OUTPUT_FLAVOR)
{
case bfd_target_aout_flavour:
return "a.out-cris";
case bfd_target_elf_flavour:
if (symbols_have_leading_underscore)
return "elf32-us-cris";
return "elf32-cris";
default:
abort ();
return NULL;
}
}
unsigned int
cris_mach ()
{
unsigned int retval = 0;
switch (cris_arch)
{
case arch_cris_common_v10_v32:
retval = bfd_mach_cris_v10_v32;
break;
case arch_crisv32:
retval = bfd_mach_cris_v32;
break;
case arch_crisv10:
case arch_cris_any_v0_v10:
retval = bfd_mach_cris_v0_v10;
break;
default:
BAD_CASE (cris_arch);
}
return retval;
}
long
cris_relax_frag (seg, fragP, stretch)
segT seg ATTRIBUTE_UNUSED;
fragS *fragP;
long stretch ATTRIBUTE_UNUSED;
{
long growth;
offsetT aim = 0;
symbolS *symbolP;
const relax_typeS *this_type;
const relax_typeS *start_type;
relax_substateT next_state;
relax_substateT this_state;
const relax_typeS *table = TC_GENERIC_RELAX_TABLE;
switch (fragP->fr_subtype)
{
case ENCODE_RELAX (STATE_COND_BRANCH, STATE_DWORD):
case ENCODE_RELAX (STATE_COND_BRANCH_V32, STATE_DWORD):
case ENCODE_RELAX (STATE_COND_BRANCH_COMMON, STATE_DWORD):
case ENCODE_RELAX (STATE_ABS_BRANCH_V32, STATE_DWORD):
case ENCODE_RELAX (STATE_LAPC, STATE_DWORD):
case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_DWORD):
return 0;
case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_WORD):
case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_BYTE):
if (fragP->fr_symbol == NULL
|| S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
as_fatal (_("internal inconsistency problem in %s: fr_symbol %lx"),
__FUNCTION__, (long) fragP->fr_symbol);
symbolP = fragP->fr_symbol;
if (symbol_resolved_p (symbolP))
as_fatal (_("internal inconsistency problem in %s: resolved symbol"),
__FUNCTION__);
aim = S_GET_VALUE (symbolP);
break;
case ENCODE_RELAX (STATE_MUL, STATE_BYTE):
return 0;
default:
as_fatal (_("internal inconsistency problem in %s: fr_subtype %d"),
__FUNCTION__, fragP->fr_subtype);
}
this_state = fragP->fr_subtype;
start_type = this_type = table + this_state;
if (aim < 0)
{
for (next_state = this_type->rlx_more; next_state;)
if (aim >= this_type->rlx_backward)
next_state = 0;
else
{
this_state = next_state;
this_type = table + this_state;
next_state = this_type->rlx_more;
}
}
else
{
for (next_state = this_type->rlx_more; next_state;)
if (aim <= this_type->rlx_forward)
next_state = 0;
else
{
this_state = next_state;
this_type = table + this_state;
next_state = this_type->rlx_more;
}
}
growth = this_type->rlx_length - start_type->rlx_length;
if (growth != 0)
fragP->fr_subtype = this_state;
return growth;
}
int
md_estimate_size_before_relax (fragP, segment_type)
fragS *fragP;
segT segment_type;
{
int old_fr_fix;
symbolS *symbolP = fragP->fr_symbol;
#define HANDLE_RELAXABLE(state) \
case ENCODE_RELAX (state, STATE_UNDF): \
if (symbolP != NULL \
&& S_GET_SEGMENT (symbolP) == segment_type \
&& !S_IS_WEAK (symbolP)) \
\
fragP->fr_subtype \
= ENCODE_RELAX (state, STATE_BYTE); \
else \
\
fragP->fr_subtype \
= ENCODE_RELAX (state, STATE_DWORD); \
fragP->fr_var \
= md_cris_relax_table[fragP->fr_subtype].rlx_length; \
break
old_fr_fix = fragP->fr_fix;
switch (fragP->fr_subtype)
{
HANDLE_RELAXABLE (STATE_COND_BRANCH);
HANDLE_RELAXABLE (STATE_COND_BRANCH_V32);
HANDLE_RELAXABLE (STATE_COND_BRANCH_COMMON);
HANDLE_RELAXABLE (STATE_ABS_BRANCH_V32);
case ENCODE_RELAX (STATE_LAPC, STATE_UNDF):
if (symbolP != NULL
&& S_GET_SEGMENT (symbolP) == segment_type
&& !S_IS_WEAK (symbolP))
{
bfd_vma target_address
= (symbolP
? S_GET_VALUE (symbolP)
: 0) + fragP->fr_offset;
bfd_vma var_part_offset = fragP->fr_fix;
bfd_vma address_of_var_part = fragP->fr_address + var_part_offset;
long offset = target_address - (address_of_var_part - 2);
fragP->fr_subtype
= (offset & 1)
? ENCODE_RELAX (STATE_LAPC, STATE_DWORD)
: ENCODE_RELAX (STATE_LAPC, STATE_BYTE);
}
else
fragP->fr_subtype
= ENCODE_RELAX (STATE_LAPC, STATE_DWORD);
fragP->fr_var
= md_cris_relax_table[fragP->fr_subtype].rlx_length;
break;
case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_UNDF):
if (S_GET_SEGMENT (symbolP) != absolute_section)
{
fragP->fr_subtype
= ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_DWORD);
fragP->fr_var = md_cris_relax_table[fragP->fr_subtype].rlx_length;
}
else if (!symbol_resolved_p (fragP->fr_symbol))
{
fragP->fr_subtype
= ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_BYTE);
fragP->fr_var = md_cris_relax_table[fragP->fr_subtype].rlx_length;
}
else
{
long int value;
value = (symbolP != NULL
? S_GET_VALUE (symbolP) : 0) + fragP->fr_offset;
if (value >= -128 && value <= 127)
{
(fragP->fr_opcode)[0] = value;
}
else
{
int pow2_of_size = 1;
char *writep;
if (value < -32768 || value > 32767)
{
pow2_of_size = 2;
}
(fragP->fr_opcode)[0] = BDAP_PC_LOW + pow2_of_size * 16;
(fragP->fr_opcode)[1] &= 0xF0;
(fragP->fr_opcode)[1] |= BDAP_INCR_HIGH;
fragP->fr_fix += 1 << pow2_of_size;
writep = fragP->fr_literal + old_fr_fix;
md_number_to_chars (writep, value, 1 << pow2_of_size);
}
frag_wane (fragP);
}
break;
case ENCODE_RELAX (STATE_COND_BRANCH, STATE_BYTE):
case ENCODE_RELAX (STATE_COND_BRANCH, STATE_WORD):
case ENCODE_RELAX (STATE_COND_BRANCH, STATE_DWORD):
case ENCODE_RELAX (STATE_COND_BRANCH_V32, STATE_BYTE):
case ENCODE_RELAX (STATE_COND_BRANCH_V32, STATE_WORD):
case ENCODE_RELAX (STATE_COND_BRANCH_V32, STATE_DWORD):
case ENCODE_RELAX (STATE_COND_BRANCH_COMMON, STATE_BYTE):
case ENCODE_RELAX (STATE_COND_BRANCH_COMMON, STATE_WORD):
case ENCODE_RELAX (STATE_COND_BRANCH_COMMON, STATE_DWORD):
case ENCODE_RELAX (STATE_ABS_BRANCH_V32, STATE_BYTE):
case ENCODE_RELAX (STATE_ABS_BRANCH_V32, STATE_WORD):
case ENCODE_RELAX (STATE_ABS_BRANCH_V32, STATE_DWORD):
case ENCODE_RELAX (STATE_LAPC, STATE_BYTE):
case ENCODE_RELAX (STATE_LAPC, STATE_DWORD):
case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_BYTE):
case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_WORD):
case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_DWORD):
fragP->fr_var = md_cris_relax_table[fragP->fr_subtype].rlx_length;
break;
case ENCODE_RELAX (STATE_MUL, STATE_BYTE):
break;
default:
BAD_CASE (fragP->fr_subtype);
}
return fragP->fr_var + (fragP->fr_fix - old_fr_fix);
}
void
md_convert_frag (abfd, sec, fragP)
bfd *abfd ATTRIBUTE_UNUSED;
segT sec ATTRIBUTE_UNUSED;
fragS *fragP;
{
char *var_partp;
char *opcodep;
int length_code;
int var_part_size = 0;
symbolS *symbolP;
unsigned long var_part_offset;
unsigned long address_of_var_part = 0;
unsigned long target_address;
know (fragP->fr_type == rs_machine_dependent);
length_code = fragP->fr_subtype & STATE_LENGTH_MASK;
know (length_code >= 0 && length_code < STATE_MAX_LENGTH);
var_part_offset = fragP->fr_fix;
var_partp = fragP->fr_literal + var_part_offset;
opcodep = fragP->fr_opcode;
symbolP = fragP->fr_symbol;
target_address = (symbolP ? S_GET_VALUE (symbolP) : 0) + fragP->fr_offset;
address_of_var_part = fragP->fr_address + var_part_offset;
switch (fragP->fr_subtype)
{
case ENCODE_RELAX (STATE_COND_BRANCH, STATE_BYTE):
case ENCODE_RELAX (STATE_COND_BRANCH_V32, STATE_BYTE):
case ENCODE_RELAX (STATE_COND_BRANCH_COMMON, STATE_BYTE):
case ENCODE_RELAX (STATE_ABS_BRANCH_V32, STATE_BYTE):
opcodep[0] = branch_disp ((target_address - address_of_var_part));
var_part_size = 0;
break;
case ENCODE_RELAX (STATE_COND_BRANCH, STATE_WORD):
case ENCODE_RELAX (STATE_COND_BRANCH_V32, STATE_WORD):
case ENCODE_RELAX (STATE_COND_BRANCH_COMMON, STATE_WORD):
case ENCODE_RELAX (STATE_ABS_BRANCH_V32, STATE_WORD):
opcodep[0] = BRANCH_PC_LOW;
opcodep[1] &= 0xF0;
opcodep[1] |= BRANCH_INCR_HIGH;
md_number_to_chars (var_partp,
(long)
(target_address
- (address_of_var_part
+ (cris_arch == arch_crisv32
|| cris_arch == arch_cris_common_v10_v32
? -2 : 2))),
2);
var_part_size = 2;
break;
case ENCODE_RELAX (STATE_COND_BRANCH, STATE_DWORD):
gen_cond_branch_32 (fragP->fr_opcode, var_partp, fragP,
fragP->fr_symbol, (symbolS *) NULL,
fragP->fr_offset);
var_part_size = 2 + 2 + 4 + 2;
break;
case ENCODE_RELAX (STATE_COND_BRANCH_V32, STATE_DWORD):
gen_cond_branch_32 (fragP->fr_opcode, var_partp, fragP,
fragP->fr_symbol, (symbolS *) NULL,
fragP->fr_offset);
var_part_size = 2 + 2 + 2 + 4 + 2;
break;
case ENCODE_RELAX (STATE_COND_BRANCH_COMMON, STATE_DWORD):
as_bad_where (fragP->fr_file, fragP->fr_line,
_("Relaxation to long branches for .arch common_v10_v32\
not implemented"));
var_part_size = 2 + 2 + 2 + 4 + 2;
break;
case ENCODE_RELAX (STATE_ABS_BRANCH_V32, STATE_DWORD):
opcodep[0] = BA_DWORD_OPCODE & 255;
opcodep[1] = (BA_DWORD_OPCODE >> 8) & 255;
fix_new (fragP, var_partp - fragP->fr_literal, 4, symbolP,
fragP->fr_offset + 6, 1, BFD_RELOC_32_PCREL);
var_part_size = 4;
break;
case ENCODE_RELAX (STATE_LAPC, STATE_BYTE):
{
long offset = target_address - (address_of_var_part - 2);
if (offset & 1)
as_bad_where (fragP->fr_file, fragP->fr_line,
_("Complicated LAPC target operand is not\
a multiple of two. Use LAPC.D"));
if (offset > 15*2 || offset < 0)
as_fatal (_("Internal error found in md_convert_frag: offset %ld.\
Please report this."),
offset);
opcodep[0] |= (offset / 2) & 0xf;
var_part_size = 0;
}
break;
case ENCODE_RELAX (STATE_LAPC, STATE_DWORD):
{
md_number_to_chars (opcodep,
LAPC_DWORD_OPCODE + (opcodep[1] & 0xf0) * 256,
2);
fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
fragP->fr_offset + 6, 1, BFD_RELOC_32_PCREL);
var_part_size = 4;
}
break;
case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_BYTE):
if (symbolP == NULL)
as_fatal (_("internal inconsistency in %s: bdapq no symbol"),
__FUNCTION__);
opcodep[0] = S_GET_VALUE (symbolP);
var_part_size = 0;
break;
case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_WORD):
opcodep[0] = BDAP_PC_LOW + (1 << 4);
opcodep[1] &= 0xF0;
opcodep[1] |= BDAP_INCR_HIGH;
if (symbolP == NULL)
as_fatal (_("internal inconsistency in %s: bdap.w with no symbol"),
__FUNCTION__);
md_number_to_chars (var_partp, S_GET_VALUE (symbolP), 2);
var_part_size = 2;
break;
case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_DWORD):
opcodep[0] = BDAP_PC_LOW + (2 << 4);
opcodep[1] &= 0xF0;
opcodep[1] |= BDAP_INCR_HIGH;
if (fragP->fr_symbol == NULL)
md_number_to_chars (var_partp, fragP->fr_offset, 4);
else
fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
fragP->fr_offset, 0, BFD_RELOC_32);
var_part_size = 4;
break;
case ENCODE_RELAX (STATE_MUL, STATE_BYTE):
if (sec->alignment_power < 2)
as_bad_where (fragP->fr_file, fragP->fr_line,
_("section alignment must be >= 4 bytes to check MULS/MULU safeness"));
else
{
if ((address_of_var_part
& ((1 << sec->alignment_power) - 1) & 31) == 0)
as_bad_where (fragP->fr_file, fragP->fr_line,
_("dangerous MULS/MULU location; give it higher alignment"));
}
break;
default:
BAD_CASE (fragP->fr_subtype);
break;
}
fragP->fr_fix += var_part_size;
}
static void
cris_create_short_jump (storep, from_addr, to_addr, fragP, to_symbol)
char *storep;
addressT from_addr;
addressT to_addr;
fragS *fragP ATTRIBUTE_UNUSED;
symbolS *to_symbol ATTRIBUTE_UNUSED;
{
long int distance;
long int max_minimal_minus_distance;
long int max_minimal_plus_distance;
int nop_opcode;
if (cris_arch == arch_crisv32)
{
max_minimal_minus_distance = BRANCH_BB_V32 + 2;
max_minimal_plus_distance = BRANCH_BF_V32 + 2;
nop_opcode = NOP_OPCODE_V32;
}
else
{
max_minimal_minus_distance = BRANCH_BB + 2;
max_minimal_plus_distance = BRANCH_BF + 2;
nop_opcode = NOP_OPCODE;
}
distance = to_addr - from_addr;
if (max_minimal_minus_distance <= distance
&& distance <= max_minimal_plus_distance)
{
storep[0] = branch_disp (distance - 2);
storep[1] = BA_QUICK_HIGH;
md_number_to_chars (storep + 2, nop_opcode, 2);
md_number_to_chars (storep + 4, nop_opcode, 2);
}
else
{
md_number_to_chars (storep, BA_PC_INCR_OPCODE, 2);
md_number_to_chars (storep + 2,
(long) (distance - 4
- (cris_arch == arch_crisv32
? -4 : 0)),
2);
md_number_to_chars (storep + 4, nop_opcode, 2);
}
}
void
md_create_long_jump (storep, from_addr, to_addr, fragP, to_symbol)
char *storep;
addressT from_addr;
addressT to_addr;
fragS *fragP;
symbolS *to_symbol;
{
long int distance;
long int max_short_minus_distance
= cris_arch != arch_crisv32 ? BRANCH_WB + 3 : BRANCH_WB_V32 + 3;
long int max_short_plus_distance
= cris_arch != arch_crisv32 ? BRANCH_WF + 3 : BRANCH_WF_V32 + 3;
if (cris_arch == arch_cris_common_v10_v32)
as_fatal (_("Out-of-range .word offset handling\
is not implemented for .arch common_v10_v32"));
distance = to_addr - from_addr;
if (max_short_minus_distance <= distance
&& distance <= max_short_plus_distance)
cris_create_short_jump (storep, from_addr, to_addr, fragP,
to_symbol);
else
{
md_number_to_chars (storep,
cris_arch == arch_crisv32
? BA_DWORD_OPCODE
: (pic ? ADD_PC_INCR_OPCODE : JUMP_PC_INCR_OPCODE),
2);
fix_new (fragP, storep + 2 - fragP->fr_literal, 4, to_symbol,
cris_arch == arch_crisv32 ? 6 : 0,
cris_arch == arch_crisv32 || pic ? 1 : 0,
cris_arch == arch_crisv32 || pic
? BFD_RELOC_32_PCREL : BFD_RELOC_32);
if (cris_arch == arch_crisv32)
md_number_to_chars (storep + 6, NOP_OPCODE_V32, 2);
}
}
static char *
cris_insn_first_word_frag ()
{
char *insnp = frag_more (2);
if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
dwarf2_emit_insn (2);
return insnp;
}
void
md_begin ()
{
const char *hashret = NULL;
int i = 0;
op_hash = hash_new ();
if (op_hash == NULL)
as_fatal (_("Virtual memory exhausted"));
symbol_table_insert (symbol_new ("..asm.arch.cris.v32", absolute_section,
(cris_arch == arch_crisv32),
&zero_address_frag));
symbol_table_insert (symbol_new ("..asm.arch.cris.v10", absolute_section,
(cris_arch == arch_crisv10),
&zero_address_frag));
symbol_table_insert (symbol_new ("..asm.arch.cris.common_v10_v32",
absolute_section,
(cris_arch == arch_cris_common_v10_v32),
&zero_address_frag));
symbol_table_insert (symbol_new ("..asm.arch.cris.any_v0_v10",
absolute_section,
(cris_arch == arch_cris_any_v0_v10),
&zero_address_frag));
while (cris_opcodes[i].name != NULL)
{
const char *name = cris_opcodes[i].name;
if (! cris_insn_ver_valid_for_arch (cris_opcodes[i].applicable_version,
cris_arch))
{
i++;
continue;
}
hashret = hash_insert (op_hash, name, (PTR) &cris_opcodes[i]);
if (hashret != NULL && *hashret != '\0')
as_fatal (_("Can't hash `%s': %s\n"), cris_opcodes[i].name,
*hashret == 0 ? _("(unknown reason)") : hashret);
do
{
if (cris_opcodes[i].match & cris_opcodes[i].lose)
as_fatal (_("Buggy opcode: `%s' \"%s\"\n"), cris_opcodes[i].name,
cris_opcodes[i].args);
++i;
}
while (cris_opcodes[i].name != NULL
&& strcmp (cris_opcodes[i].name, name) == 0);
}
}
void
md_assemble (str)
char *str;
{
struct cris_instruction output_instruction;
struct cris_prefix prefix;
char *opcodep;
char *p;
know (str);
cris_process_instruction (str, &output_instruction, &prefix);
switch (prefix.kind)
{
case PREFIX_NONE:
break;
case PREFIX_BDAP_IMM:
if (prefix.reloc == BFD_RELOC_NONE)
{
gen_bdap (prefix.base_reg_number, &prefix.expr);
break;
}
case PREFIX_BDAP:
case PREFIX_BIAP:
case PREFIX_DIP:
opcodep = cris_insn_first_word_frag ();
md_number_to_chars (opcodep, (long) prefix.opcode, 2);
if (prefix.reloc != BFD_RELOC_NONE)
{
unsigned int relocsize
= (prefix.kind == PREFIX_DIP
? 4 : cris_get_pic_reloc_size (prefix.reloc));
p = frag_more (relocsize);
fix_new_exp (frag_now, (p - frag_now->fr_literal), relocsize,
&prefix.expr, 0, prefix.reloc);
}
break;
case PREFIX_PUSH:
opcodep = cris_insn_first_word_frag ();
if (output_instruction.spec_reg != NULL)
{
opcodep[0] = -output_instruction.spec_reg->reg_size;
}
else
{
opcodep[0] = -4;
}
opcodep[1] = (REG_SP << 4) + (BDAP_QUICK_OPCODE >> 8);
break;
default:
BAD_CASE (prefix.kind);
}
if (output_instruction.insn_type == CRIS_INSN_NONE)
return;
if (prefix.kind == PREFIX_NONE)
opcodep = cris_insn_first_word_frag ();
else
opcodep = frag_more (2);
md_number_to_chars (opcodep, (long) (output_instruction.opcode), 2);
if (output_instruction.insn_type == CRIS_INSN_BRANCH)
{
segT to_seg = absolute_section;
int is_undefined = 0;
int length_code;
if (output_instruction.expr.X_op != O_constant)
{
to_seg = S_GET_SEGMENT (output_instruction.expr.X_add_symbol);
if (to_seg == undefined_section)
is_undefined = 1;
}
if (to_seg == now_seg || is_undefined
|| (cris_arch == arch_crisv32
&& output_instruction.opcode == BA_QUICK_OPCODE))
{
valueT addvalue
= (SIMPLE_EXPR (&output_instruction.expr)
? output_instruction.expr.X_add_number
: 0);
symbolS *sym
= (SIMPLE_EXPR (&output_instruction.expr)
? output_instruction.expr.X_add_symbol
: make_expr_symbol (&output_instruction.expr));
length_code = to_seg == now_seg ? STATE_BYTE : STATE_UNDF;
frag_var (rs_machine_dependent,
(cris_arch == arch_crisv32
|| cris_arch == arch_cris_common_v10_v32) ? 12 : 10, 0,
ENCODE_RELAX (cris_arch == arch_crisv32
? (output_instruction.opcode
== BA_QUICK_OPCODE
? STATE_ABS_BRANCH_V32
: STATE_COND_BRANCH_V32)
: (cris_arch == arch_cris_common_v10_v32
? STATE_COND_BRANCH_COMMON
: STATE_COND_BRANCH),
length_code),
sym, addvalue, opcodep);
}
else
{
char *cond_jump
= frag_more ((cris_arch == arch_crisv32
|| cris_arch == arch_cris_common_v10_v32)
? 12 : 10);
gen_cond_branch_32 (opcodep, cond_jump, frag_now,
output_instruction.expr.X_add_symbol,
(symbolS *) NULL,
output_instruction.expr.X_add_number);
}
}
else if (output_instruction.insn_type == CRIS_INSN_MUL
&& err_for_dangerous_mul_placement)
frag_variant (rs_machine_dependent, 0, 0,
ENCODE_RELAX (STATE_MUL, STATE_BYTE),
NULL, 0, opcodep);
else
{
if (output_instruction.imm_oprnd_size > 0)
{
enum bfd_reloc_code_real reloc = BFD_RELOC_NONE;
switch (output_instruction.imm_oprnd_size)
{
case 2:
if (output_instruction.reloc != BFD_RELOC_NONE)
reloc = output_instruction.reloc;
else
reloc = BFD_RELOC_16;
break;
case 4:
if (output_instruction.reloc != BFD_RELOC_NONE)
reloc = output_instruction.reloc;
else
reloc = BFD_RELOC_32;
break;
default:
BAD_CASE (output_instruction.imm_oprnd_size);
}
p = frag_more (output_instruction.imm_oprnd_size);
fix_new_exp (frag_now, (p - frag_now->fr_literal),
output_instruction.imm_oprnd_size,
&output_instruction.expr,
reloc == BFD_RELOC_32_PCREL
|| reloc == BFD_RELOC_16_PCREL
|| reloc == BFD_RELOC_8_PCREL, reloc);
}
else if (output_instruction.reloc == BFD_RELOC_CRIS_LAPCQ_OFFSET
&& output_instruction.expr.X_md != 0)
{
valueT addvalue
= (output_instruction.expr.X_op_symbol != NULL
? 0 : output_instruction.expr.X_add_number);
symbolS *sym
= (output_instruction.expr.X_op_symbol != NULL
? make_expr_symbol (&output_instruction.expr)
: output_instruction.expr.X_add_symbol);
frag_var (rs_machine_dependent,
4, 0,
ENCODE_RELAX (STATE_LAPC, STATE_UNDF),
sym, addvalue, opcodep);
}
else if (output_instruction.reloc != BFD_RELOC_NONE)
{
fix_new_exp (frag_now, (opcodep - frag_now->fr_literal), 2,
&output_instruction.expr,
output_instruction.reloc == BFD_RELOC_32_PCREL
|| output_instruction.reloc == BFD_RELOC_16_PCREL
|| output_instruction.reloc == BFD_RELOC_8_PCREL
|| (output_instruction.reloc
== BFD_RELOC_CRIS_LAPCQ_OFFSET),
output_instruction.reloc);
}
}
}
static void
cris_process_instruction (insn_text, out_insnp, prefixp)
char *insn_text;
struct cris_instruction *out_insnp;
struct cris_prefix *prefixp;
{
char *s;
char modified_char = 0;
const char *args;
struct cris_opcode *instruction;
char *operands;
int match = 0;
int mode;
int regno;
int size_bits;
prefixp->kind = PREFIX_NONE;
prefixp->reloc = BFD_RELOC_NONE;
out_insnp->insn_type = CRIS_INSN_NONE;
out_insnp->imm_oprnd_size = 0;
for (operands = insn_text; ISLOWER (*operands); ++operands)
;
switch (*operands)
{
case '\0':
break;
case '.':
modified_char = *operands;
case ' ':
*operands++ = '\0';
break;
default:
as_bad (_("Unknown opcode: `%s'"), insn_text);
return;
}
instruction = (struct cris_opcode *) hash_find (op_hash, insn_text);
if (instruction == NULL)
{
as_bad (_("Unknown opcode: `%s'"), insn_text);
return;
}
switch (modified_char)
{
case 0:
break;
default:
*--operands = modified_char;
}
for (s = operands;;)
{
int imm_expr_found;
prefixp->kind = PREFIX_NONE;
prefixp->reloc = BFD_RELOC_NONE;
memset (out_insnp, 0, sizeof (*out_insnp));
out_insnp->opcode = instruction->match;
out_insnp->reloc = BFD_RELOC_NONE;
out_insnp->insn_type = CRIS_INSN_NORMAL;
out_insnp->imm_oprnd_size = 0;
imm_expr_found = 0;
for (args = instruction->args;; ++args)
{
switch (*args)
{
case '\0':
if (*s == '\0')
match = 1;
break;
case '!':
continue;
case '[':
case ']':
case ',':
case ' ':
if (*s++ == *args)
continue;
break;
case 'A':
if (*s == REGISTER_PREFIX_CHAR)
s++;
else if (demand_register_prefix)
break;
if ((*s++ != 'a' && s[-1] != 'A')
|| (*s++ != 'c' && s[-1] != 'C')
|| (*s++ != 'r' && s[-1] != 'R'))
break;
continue;
case 'B':
prefixp->kind = PREFIX_PUSH;
continue;
case 'b':
break;
case 'c':
if (! cris_get_expression (&s, &out_insnp->expr))
break;
else
{
if (out_insnp->expr.X_op == O_constant
&& (out_insnp->expr.X_add_number < 0
|| out_insnp->expr.X_add_number > 31))
as_bad (_("Immediate value not in 5 bit unsigned range: %ld"),
out_insnp->expr.X_add_number);
out_insnp->reloc = BFD_RELOC_CRIS_UNSIGNED_5;
continue;
}
case 'C':
if (! cris_get_expression (&s, &out_insnp->expr))
break;
else
{
if (out_insnp->expr.X_op == O_constant
&& (out_insnp->expr.X_add_number < 0
|| out_insnp->expr.X_add_number > 15))
as_bad (_("Immediate value not in 4 bit unsigned range: %ld"),
out_insnp->expr.X_add_number);
out_insnp->reloc = BFD_RELOC_CRIS_UNSIGNED_4;
continue;
}
case 'd':
if (modified_char == '.' && *s == '.')
{
if ((s[1] != 'd' && s[1] == 'D')
|| ! ISSPACE (s[2]))
break;
s += 2;
continue;
}
continue;
case 'D':
if (! get_gen_reg (&s, ®no))
break;
else
{
out_insnp->opcode |= regno ;
out_insnp->opcode |= regno << 12;
continue;
}
case 'f':
{
int flags = 0;
if (! get_flags (&s, &flags))
break;
out_insnp->opcode |= ((flags & 0xf0) << 8) | (flags & 0xf);
continue;
}
case 'i':
if (! cris_get_expression (&s, &out_insnp->expr))
break;
else
{
if (out_insnp->expr.X_op == O_constant
&& (out_insnp->expr.X_add_number < -32
|| out_insnp->expr.X_add_number > 31))
as_bad (_("Immediate value not in 6 bit range: %ld"),
out_insnp->expr.X_add_number);
out_insnp->reloc = BFD_RELOC_CRIS_SIGNED_6;
continue;
}
case 'I':
if (! cris_get_expression (&s, &out_insnp->expr))
break;
else
{
if (out_insnp->expr.X_op == O_constant
&& (out_insnp->expr.X_add_number < 0
|| out_insnp->expr.X_add_number > 63))
as_bad (_("Immediate value not in 6 bit unsigned range: %ld"),
out_insnp->expr.X_add_number);
out_insnp->reloc = BFD_RELOC_CRIS_UNSIGNED_6;
continue;
}
case 'M':
if (! get_bwd_size_modifier (&s, &size_bits))
break;
else
{
switch (size_bits)
{
case 0:
out_insnp->opcode |= 0 << 12;
break;
case 1:
out_insnp->opcode |= 4 << 12;
break;
case 2:
out_insnp->opcode |= 8 << 12;
break;
}
continue;
}
case 'm':
if (modified_char != '.'
|| ! get_bwd_size_modifier (&s, &size_bits))
break;
else
{
out_insnp->opcode |= size_bits << 4;
continue;
}
case 'o':
if (! cris_get_expression (&s, &out_insnp->expr))
break;
else
{
out_insnp->insn_type = CRIS_INSN_BRANCH;
continue;
}
case 'Q':
if (! cris_get_expression (&s, &out_insnp->expr))
break;
if (*s != ',')
break;
s++;
if (!get_gen_reg (&s, ®no))
break;
out_insnp->opcode |= regno << 12;
out_insnp->reloc = BFD_RELOC_CRIS_SIGNED_8;
continue;
case 'O':
if (! cris_get_expression (&s, &prefixp->expr))
break;
else
{
if (*s != ',')
break;
s++;
if (!get_gen_reg (&s, &prefixp->base_reg_number))
break;
prefixp->kind = PREFIX_BDAP_IMM;
prefixp->opcode
= BDAP_QUICK_OPCODE | (prefixp->base_reg_number << 12);
out_insnp->insn_type = CRIS_INSN_NONE;
continue;
}
case 'P':
if (! get_spec_reg (&s, &out_insnp->spec_reg))
break;
else
{
if (out_insnp->spec_reg->warning)
as_warn (out_insnp->spec_reg->warning);
else if (out_insnp->spec_reg->applicable_version
== cris_ver_warning)
as_warn (_("Unimplemented register `%s' specified"),
out_insnp->spec_reg->name);
out_insnp->opcode
|= out_insnp->spec_reg->number << 12;
continue;
}
case 'p':
continue;
case 'R':
if (! get_gen_reg (&s, ®no))
break;
else
{
out_insnp->opcode |= regno << 12;
continue;
}
case 'r':
if (! get_gen_reg (&s, ®no))
break;
else
{
out_insnp->opcode |= regno ;
continue;
}
case 'S':
if (! get_3op_or_dip_prefix_op (&s, prefixp))
break;
else
continue;
case 's':
if (! get_autoinc_prefix_or_indir_op (&s, prefixp, &mode,
®no,
&imm_expr_found,
&out_insnp->expr))
break;
else
{
if (prefixp->kind != PREFIX_NONE)
{
out_insnp->opcode |= (AUTOINCR_BIT << 8);
}
else
{
out_insnp->opcode |= (mode << 10);
if (prefixp->reloc != BFD_RELOC_NONE)
out_insnp->reloc = prefixp->reloc;
}
out_insnp->opcode |= regno ;
continue;
}
case 'N':
case 'Y':
if (cris_get_expression (&s, &out_insnp->expr))
{
imm_expr_found = 1;
continue;
}
break;
case 'n':
if (cris_get_expression (&s, &out_insnp->expr))
{
imm_expr_found = 1;
out_insnp->reloc = BFD_RELOC_32_PCREL;
out_insnp->expr.X_add_number += 6;
if (pic && *s == PIC_SUFFIX_CHAR)
cris_get_pic_suffix (&s, &out_insnp->reloc,
&out_insnp->expr);
continue;
}
break;
case 'U':
if (cris_get_expression (&s, &out_insnp->expr))
{
out_insnp->reloc = BFD_RELOC_CRIS_LAPCQ_OFFSET;
out_insnp->expr.X_md = 1;
continue;
}
break;
case 'u':
if (cris_get_expression (&s, &out_insnp->expr))
{
out_insnp->reloc = BFD_RELOC_CRIS_LAPCQ_OFFSET;
out_insnp->expr.X_md = 0;
out_insnp->expr.X_add_number += 2;
continue;
}
break;
case 'x':
if (! get_gen_reg (&s, ®no)
|| ! get_bwd_size_modifier (&s, &size_bits))
break;
else
{
out_insnp->opcode |= (regno << 12) | (size_bits << 4);
continue;
}
case 'y':
if (! get_autoinc_prefix_or_indir_op (&s, prefixp,
&mode, ®no,
&imm_expr_found,
&out_insnp->expr)
|| imm_expr_found)
break;
else
{
if (prefixp->kind != PREFIX_NONE)
{
out_insnp->opcode |= (AUTOINCR_BIT << 8);
}
else
{
out_insnp->opcode |= (mode << 10);
}
out_insnp->opcode |= regno ;
continue;
}
case 'z':
if (! get_bw_size_modifier (&s, &size_bits))
break;
else
{
out_insnp->opcode |= size_bits << 4;
continue;
}
case 'T':
if (cris_arch == arch_crisv32
&& get_sup_reg (&s, ®no))
{
out_insnp->opcode |= regno << 12;
continue;
}
break;
default:
BAD_CASE (*args);
}
break;
}
if (match == 0)
{
while (instruction[1].name != NULL
&& strcmp (instruction->name, instruction[1].name) == 0
&& ! cris_insn_ver_valid_for_arch (instruction[1]
.applicable_version,
cris_arch))
++instruction;
if (instruction[1].name != NULL
&& strcmp (instruction->name, instruction[1].name) == 0
&& cris_insn_ver_valid_for_arch (instruction[1]
.applicable_version,
cris_arch))
{
++instruction;
s = operands;
continue;
}
else
{
as_bad (_("Illegal operands"));
while (!is_end_of_line[(unsigned char) *input_line_pointer])
input_line_pointer++;
return;
}
}
else
{
if (imm_expr_found)
{
switch (instruction->imm_oprnd_size)
{
default:
case SIZE_NONE:
BAD_CASE (instruction->imm_oprnd_size);
break;
case SIZE_FIX_32:
out_insnp->imm_oprnd_size = 4;
break;
case SIZE_SPEC_REG:
if (cris_arch == arch_crisv32)
out_insnp->imm_oprnd_size = 4;
else
switch (out_insnp->spec_reg->reg_size)
{
case 1:
if (out_insnp->expr.X_op == O_constant
&& (out_insnp->expr.X_add_number < -128
|| out_insnp->expr.X_add_number > 255))
as_bad (_("Immediate value not in 8 bit range: %ld"),
out_insnp->expr.X_add_number);
case 2:
if (out_insnp->expr.X_op == O_constant
&& (out_insnp->expr.X_add_number < -32768
|| out_insnp->expr.X_add_number > 65535))
as_bad (_("Immediate value not in 16 bit range: %ld"),
out_insnp->expr.X_add_number);
out_insnp->imm_oprnd_size = 2;
break;
case 4:
out_insnp->imm_oprnd_size = 4;
break;
default:
BAD_CASE (out_insnp->spec_reg->reg_size);
}
break;
case SIZE_FIELD:
case SIZE_FIELD_SIGNED:
case SIZE_FIELD_UNSIGNED:
switch (size_bits)
{
case 0:
if (out_insnp->expr.X_op == O_constant)
{
if (instruction->imm_oprnd_size == SIZE_FIELD
&& (out_insnp->expr.X_add_number < -128
|| out_insnp->expr.X_add_number > 255))
as_bad (_("Immediate value not in 8 bit range: %ld"),
out_insnp->expr.X_add_number);
else if (instruction->imm_oprnd_size == SIZE_FIELD_SIGNED
&& (out_insnp->expr.X_add_number < -128
|| out_insnp->expr.X_add_number > 127))
as_bad (_("Immediate value not in 8 bit signed range: %ld"),
out_insnp->expr.X_add_number);
else if (instruction->imm_oprnd_size == SIZE_FIELD_UNSIGNED
&& (out_insnp->expr.X_add_number < 0
|| out_insnp->expr.X_add_number > 255))
as_bad (_("Immediate value not in 8 bit unsigned range: %ld"),
out_insnp->expr.X_add_number);
}
case 1:
if (out_insnp->expr.X_op == O_constant)
{
if (instruction->imm_oprnd_size == SIZE_FIELD
&& (out_insnp->expr.X_add_number < -32768
|| out_insnp->expr.X_add_number > 65535))
as_bad (_("Immediate value not in 16 bit range: %ld"),
out_insnp->expr.X_add_number);
else if (instruction->imm_oprnd_size == SIZE_FIELD_SIGNED
&& (out_insnp->expr.X_add_number < -32768
|| out_insnp->expr.X_add_number > 32767))
as_bad (_("Immediate value not in 16 bit signed range: %ld"),
out_insnp->expr.X_add_number);
else if (instruction->imm_oprnd_size == SIZE_FIELD_UNSIGNED
&& (out_insnp->expr.X_add_number < 0
|| out_insnp->expr.X_add_number > 65535))
as_bad (_("Immediate value not in 16 bit unsigned range: %ld"),
out_insnp->expr.X_add_number);
}
out_insnp->imm_oprnd_size = 2;
break;
case 2:
out_insnp->imm_oprnd_size = 4;
break;
default:
BAD_CASE (out_insnp->spec_reg->reg_size);
}
}
if (out_insnp->reloc != BFD_RELOC_NONE
&& (cris_get_pic_reloc_size (out_insnp->reloc)
!= (unsigned int) out_insnp->imm_oprnd_size))
as_bad (_("PIC relocation size does not match operand size"));
}
else if (instruction->op == cris_muls_op
|| instruction->op == cris_mulu_op)
out_insnp->insn_type = CRIS_INSN_MUL;
}
break;
}
}
static int
get_bwd_size_modifier (cPP, size_bitsp)
char **cPP;
int *size_bitsp;
{
if (**cPP != '.')
return 0;
else
{
(*cPP)++;
switch (**cPP)
{
case 'B':
case 'b':
*size_bitsp = 0;
break;
case 'W':
case 'w':
*size_bitsp = 1;
break;
case 'D':
case 'd':
*size_bitsp = 2;
break;
default:
return 0;
}
(*cPP)++;
return 1;
}
}
static int
get_bw_size_modifier (cPP, size_bitsp)
char **cPP;
int *size_bitsp;
{
if (**cPP != '.')
return 0;
else
{
(*cPP)++;
switch (**cPP)
{
case 'B':
case 'b':
*size_bitsp = 0;
break;
case 'W':
case 'w':
*size_bitsp = 1;
break;
default:
return 0;
}
(*cPP)++;
return 1;
}
}
static int
get_gen_reg (cPP, regnop)
char **cPP;
int *regnop;
{
char *oldp;
oldp = *cPP;
if (**cPP == REGISTER_PREFIX_CHAR)
(*cPP)++;
else if (demand_register_prefix)
return 0;
switch (**cPP)
{
case 'P':
case 'p':
(*cPP)++;
if ((**cPP == 'C' || **cPP == 'c')
&& ! ISALNUM ((*cPP)[1])
&& ((cris_arch != arch_crisv32
&& cris_arch != arch_cris_common_v10_v32)
|| ((*cPP)[1] == '+' && (*cPP)[2] == ']')))
{
(*cPP)++;
*regnop = REG_PC;
return 1;
}
break;
case 'A':
case 'a':
if (cris_arch != arch_crisv32
|| ((*cPP)[1] != 'c' && (*cPP)[1] != 'C')
|| ((*cPP)[2] != 'r' && (*cPP)[2] != 'R')
|| ISALNUM ((*cPP)[3])
|| (*cPP)[3] == '+')
break;
(*cPP) += 3;
*regnop = 15;
return 1;
case 'R':
case 'r':
(*cPP)++;
if (ISDIGIT (**cPP))
{
*regnop = **cPP - '0';
(*cPP)++;
if (! ISALNUM (**cPP))
{
return 1;
}
else
{
*regnop = *regnop * 10 + (**cPP - '0');
if (*regnop <= MAX_REG)
{
(*cPP)++;
return 1;
}
}
}
break;
case 'S':
case 's':
(*cPP)++;
if (**cPP == 'P' || **cPP == 'p')
{
(*cPP)++;
*regnop = REG_SP;
return 1;
}
break;
default:
;
}
*cPP = oldp;
return 0;
}
static int
get_spec_reg (cPP, sregpp)
char **cPP;
const struct cris_spec_reg **sregpp;
{
char *s1;
const char *s2;
char *name_begin = *cPP;
const struct cris_spec_reg *sregp;
if (*name_begin == REGISTER_PREFIX_CHAR)
name_begin++;
else if (demand_register_prefix)
return 0;
for (sregp = cris_spec_regs; sregp->name != NULL; sregp++)
{
s1 = name_begin;
s2 = sregp->name;
while (*s2 != '\0' && TOLOWER (*s1) == *s2)
{
s1++;
s2++;
}
if (*s2 == 0 && ! ISALNUM (*s1)
&& cris_insn_ver_valid_for_arch (sregp->applicable_version,
cris_arch))
{
*cPP = s1;
*sregpp = sregp;
return 1;
}
}
return 0;
}
static int
get_sup_reg (cPP, regnop)
char **cPP;
int *regnop;
{
char *s1;
const char *s2;
char *name_begin = *cPP;
const struct cris_support_reg *sregp;
if (*name_begin == REGISTER_PREFIX_CHAR)
name_begin++;
else if (demand_register_prefix)
return 0;
for (sregp = cris_support_regs; sregp->name != NULL; sregp++)
{
s1 = name_begin;
s2 = sregp->name;
while (*s2 != '\0' && TOLOWER (*s1) == *s2)
{
s1++;
s2++;
}
if (*s2 == 0 && ! ISALNUM (*s1))
{
*cPP = s1;
*regnop = sregp->number;
return 1;
}
}
return 0;
}
static int
get_autoinc_prefix_or_indir_op (cPP, prefixp, is_autoincp, src_regnop,
imm_foundp, imm_exprP)
char **cPP;
struct cris_prefix *prefixp;
int *is_autoincp;
int *src_regnop;
int *imm_foundp;
expressionS *imm_exprP;
{
*imm_foundp = 0;
if (**cPP == '[')
{
(*cPP)++;
if (! get_gen_reg (cPP, src_regnop))
return 0;
else
{
switch (**cPP)
{
case ']':
prefixp->kind = PREFIX_NONE;
*is_autoincp = 0;
break;
case '+':
prefixp->kind = PREFIX_NONE;
*is_autoincp = 1;
(*cPP)++;
break;
case '=':
if (cris_arch == arch_crisv32
|| cris_arch == arch_cris_common_v10_v32)
return 0;
(*cPP)++;
if (! get_gen_reg (cPP, &prefixp->base_reg_number))
return 0;
else
{
if (**cPP == '+')
{
int index_reg_number;
(*cPP)++;
if (**cPP == '[')
{
int size_bits;
(*cPP)++;
if (!get_gen_reg (cPP, &index_reg_number))
return 0;
prefixp->kind = PREFIX_BDAP;
prefixp->opcode
= (BDAP_INDIR_OPCODE
+ (prefixp->base_reg_number << 12)
+ index_reg_number);
if (**cPP == '+')
{
(*cPP)++;
prefixp->opcode |= AUTOINCR_BIT << 8;
}
if (**cPP != ']')
return 0;
(*cPP)++;
if (! get_bwd_size_modifier (cPP, &size_bits))
return 0;
prefixp->opcode |= size_bits << 4;
break;
}
else if (get_gen_reg (cPP, &index_reg_number))
{
int size_bits;
prefixp->kind = PREFIX_BIAP;
prefixp->opcode
= (BIAP_OPCODE + (index_reg_number << 12)
+ prefixp->base_reg_number );
if (! get_bwd_size_modifier (cPP, &size_bits))
return 0;
else
{
prefixp->opcode |= size_bits << 4;
break;
}
}
else if (cris_get_expression (cPP, &prefixp->expr))
{
prefixp->kind = PREFIX_BDAP_IMM;
prefixp->opcode
= (BDAP_INDIR_OPCODE
| (prefixp->base_reg_number << 12)
| (AUTOINCR_BIT << 8)
| (2 << 4)
| REG_PC );
if (pic && **cPP == PIC_SUFFIX_CHAR)
{
unsigned int relocsize;
cris_get_pic_suffix (cPP, &prefixp->reloc,
&prefixp->expr);
relocsize
= cris_get_pic_reloc_size (prefixp->reloc);
if (relocsize != 4)
prefixp->opcode
= ((prefixp->opcode & ~(3 << 4))
| ((relocsize >> 1) << 4));
}
break;
}
else
return 0;
}
else if (**cPP == '-')
{
if (! cris_get_expression (cPP, &prefixp->expr))
return 0;
else
{
prefixp->kind = PREFIX_BDAP_IMM;
break;
}
}
else
return 0;
}
default:
return 0;
}
}
if (**cPP != ']')
return 0;
else
{
(*cPP)++;
return 1;
}
}
else if (cris_get_expression (cPP, imm_exprP))
{
prefixp->kind = PREFIX_NONE;
*is_autoincp = 1;
*src_regnop = REG_PC;
*imm_foundp = 1;
if (pic && **cPP == PIC_SUFFIX_CHAR)
cris_get_pic_suffix (cPP, &prefixp->reloc, imm_exprP);
return 1;
}
return 0;
}
static int
get_3op_or_dip_prefix_op (cPP, prefixp)
char **cPP;
struct cris_prefix *prefixp;
{
int reg_number;
if (**cPP != '[')
return 0;
(*cPP)++;
if (**cPP == '[')
{
(*cPP)++;
prefixp->kind = PREFIX_DIP;
prefixp->opcode = DIP_OPCODE;
if (! get_gen_reg (cPP, ®_number))
return 0;
else
{
prefixp->opcode |= reg_number ;
if (**cPP == '+')
{
(*cPP)++;
prefixp->opcode |= AUTOINCR_BIT << 8;
}
}
if (**cPP != ']')
return 0;
(*cPP)++;
}
else if (get_gen_reg (cPP, &prefixp->base_reg_number))
{
if (**cPP == '+')
{
int index_reg_number;
(*cPP)++;
if (**cPP == '[')
{
int size_bits;
(*cPP)++;
if (!get_gen_reg (cPP, &index_reg_number))
return 0;
prefixp->kind = PREFIX_BDAP;
prefixp->opcode
= (BDAP_INDIR_OPCODE
+ (prefixp->base_reg_number << 12)
+ index_reg_number);
if (**cPP == '+')
{
(*cPP)++;
prefixp->opcode |= AUTOINCR_BIT << 8;
}
if (**cPP != ']')
return 0;
(*cPP)++;
if (! get_bwd_size_modifier (cPP, &size_bits))
return 0;
prefixp->opcode |= size_bits << 4;
}
else if (get_gen_reg (cPP, &index_reg_number))
{
int size_bits;
prefixp->kind = PREFIX_BIAP;
prefixp->opcode
= (BIAP_OPCODE
| prefixp->base_reg_number
| (index_reg_number << 12));
if (! get_bwd_size_modifier (cPP, &size_bits))
return 0;
else
prefixp->opcode |= size_bits << 4;
}
else if (**cPP != ']' && cris_get_expression (cPP, &prefixp->expr))
{
prefixp->kind = PREFIX_BDAP_IMM;
prefixp->opcode
= (BDAP_INDIR_OPCODE
| (prefixp->base_reg_number << 12)
| (AUTOINCR_BIT << 8)
| (2 << 4)
| REG_PC );
if (pic && **cPP == PIC_SUFFIX_CHAR)
{
unsigned int relocsize;
cris_get_pic_suffix (cPP, &prefixp->reloc, &prefixp->expr);
relocsize = cris_get_pic_reloc_size (prefixp->reloc);
if (relocsize != 4)
prefixp->opcode
= ((prefixp->opcode & ~(3 << 4))
| ((relocsize >> 1) << 4));
}
}
else
return 0;
}
else if (**cPP == '-')
{
if (! cris_get_expression (cPP, &prefixp->expr))
return 0;
else
{
prefixp->kind = PREFIX_BDAP_IMM;
}
}
else
{
prefixp->expr.X_op = O_constant;
prefixp->expr.X_add_number = 0;
prefixp->expr.X_add_symbol = NULL;
prefixp->expr.X_op_symbol = NULL;
prefixp->kind = PREFIX_BDAP_IMM;
}
}
else if (cris_get_expression (cPP, &prefixp->expr))
{
prefixp->kind = PREFIX_DIP;
prefixp->opcode = DIP_OPCODE | (AUTOINCR_BIT << 8) | REG_PC;
prefixp->reloc = BFD_RELOC_32;
}
else
return 0;
if (**cPP != ']')
return 0;
(*cPP)++;
return 1;
}
static int
cris_get_expression (cPP, exprP)
char **cPP;
expressionS *exprP;
{
char *saved_input_line_pointer;
segT exp;
saved_input_line_pointer = input_line_pointer;
input_line_pointer = *cPP;
if (*input_line_pointer == '[')
{
input_line_pointer = saved_input_line_pointer;
return 0;
}
exp = expression (exprP);
if (exprP->X_op == O_illegal || exprP->X_op == O_absent)
{
input_line_pointer = saved_input_line_pointer;
return 0;
}
*cPP = input_line_pointer;
input_line_pointer = saved_input_line_pointer;
return 1;
}
static int
get_flags (cPP, flagsp)
char **cPP;
int *flagsp;
{
for (;;)
{
switch (**cPP)
{
case 'd':
case 'D':
if (! cris_insn_ver_valid_for_arch (cris_ver_v0_3,
cris_arch))
return 0;
*flagsp |= 0x80;
break;
case 'm':
case 'M':
if (! cris_insn_ver_valid_for_arch (cris_ver_v8_10,
cris_arch))
return 0;
*flagsp |= 0x80;
break;
case 'e':
case 'E':
if (! cris_insn_ver_valid_for_arch (cris_ver_v0_3,
cris_arch))
return 0;
*flagsp |= 0x40;
break;
case 'b':
case 'B':
if (! cris_insn_ver_valid_for_arch (cris_ver_v8_10,
cris_arch))
return 0;
*flagsp |= 0x40;
break;
case 'p':
case 'P':
if (! cris_insn_ver_valid_for_arch (cris_ver_v32p,
cris_arch))
return 0;
*flagsp |= 0x80;
break;
case 'u':
case 'U':
if (! cris_insn_ver_valid_for_arch (cris_ver_v32p,
cris_arch))
return 0;
*flagsp |= 0x40;
break;
case 'i':
case 'I':
*flagsp |= 0x20;
break;
case 'x':
case 'X':
*flagsp |= 0x10;
break;
case 'n':
case 'N':
*flagsp |= 0x8;
break;
case 'z':
case 'Z':
*flagsp |= 0x4;
break;
case 'v':
case 'V':
*flagsp |= 0x2;
break;
case 'c':
case 'C':
*flagsp |= 1;
break;
default:
if (**cPP != ','
&& **cPP != 0
&& ! ISSPACE (**cPP))
return 0;
else
return 1;
}
(*cPP)++;
}
}
static void
gen_bdap (base_regno, exprP)
int base_regno;
expressionS *exprP;
{
unsigned int opcode;
char *opcodep;
opcode = BDAP_QUICK_OPCODE | (base_regno << 12);
opcodep = cris_insn_first_word_frag ();
md_number_to_chars (opcodep, opcode, 2);
if (exprP->X_op == O_constant)
{
long int value;
int size;
value = exprP->X_add_number;
if (value < -32768 || value > 32767)
size = 2;
else
size = 1;
if (value >= -128 && value <= 127)
opcodep[0] = value;
else
{
char *p;
opcodep[0] = BDAP_PC_LOW + (size << 4);
opcodep[1] &= 0xF0;
opcodep[1] |= BDAP_INCR_HIGH;
p = frag_more (1 << size);
md_number_to_chars (p, value, 1 << size);
}
}
else
{
valueT addvalue
= SIMPLE_EXPR (exprP) ? exprP->X_add_number : 0;
symbolS *sym
= (SIMPLE_EXPR (exprP)
? exprP->X_add_symbol : make_expr_symbol (exprP));
frag_var (rs_machine_dependent, 4, 0,
ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_UNDF),
sym, addvalue, opcodep);
}
}
static int
branch_disp (offset)
int offset;
{
int disp;
if (cris_arch == arch_crisv32 || cris_arch == arch_cris_common_v10_v32)
offset += 2;
disp = offset & 0xFE;
if (offset < 0)
disp |= 1;
return disp;
}
static void
gen_cond_branch_32 (opcodep, writep, fragP, add_symP, sub_symP, add_num)
char *opcodep;
char *writep;
fragS *fragP;
symbolS *add_symP;
symbolS *sub_symP;
long int add_num;
{
int nop_opcode;
int opc_offset;
int branch_offset;
if (cris_arch == arch_crisv32)
{
nop_opcode = NOP_OPCODE_V32;
opc_offset = 10;
branch_offset = -2 - 8;
}
else
{
nop_opcode = NOP_OPCODE;
opc_offset = 8;
branch_offset = -2 - 6;
}
if (cris_arch == arch_cris_common_v10_v32)
as_fatal (_("Calling gen_cond_branch_32 for .arch common_v10_v32\n"));
if (warn_for_branch_expansion)
as_warn_where (fragP->fr_file, fragP->fr_line,
_("32-bit conditional branch generated"));
writep[opc_offset] = branch_disp (branch_offset);
writep[opc_offset + 1] = opcodep[1];
md_number_to_chars (opcodep, BA_QUICK_OPCODE
+ (cris_arch == arch_crisv32 ? 12 : 8), 2);
md_number_to_chars (writep, nop_opcode, 2);
md_number_to_chars (writep + 2,
cris_arch == arch_crisv32
? BA_DWORD_OPCODE
: (pic ? ADD_PC_INCR_OPCODE : JUMP_PC_INCR_OPCODE), 2);
if (add_symP == NULL && sub_symP == NULL)
{
if (pic || cris_arch == arch_crisv32)
fix_new (fragP, writep + 4 - fragP->fr_literal, 4,
section_symbol (absolute_section),
add_num
+ (cris_arch == arch_crisv32 ? 6 : 0),
1, BFD_RELOC_32_PCREL);
else
md_number_to_chars (writep + 4, add_num, 4);
}
else
{
if (sub_symP != NULL)
as_bad_where (fragP->fr_file, fragP->fr_line,
_("Complex expression not supported"));
fix_new (fragP, writep + 4 - fragP->fr_literal, 4, add_symP,
add_num + (cris_arch == arch_crisv32 ? 6 : 0),
pic || cris_arch == arch_crisv32 ? 1 : 0,
pic || cris_arch == arch_crisv32
? BFD_RELOC_32_PCREL : BFD_RELOC_32);
}
if (cris_arch == arch_crisv32)
md_number_to_chars (writep + 8, NOP_OPCODE_V32, 2);
}
static unsigned int
cris_get_pic_reloc_size (reloc)
bfd_reloc_code_real_type reloc;
{
return reloc == BFD_RELOC_CRIS_16_GOTPLT || reloc == BFD_RELOC_CRIS_16_GOT
? 2 : 4;
}
static void
cris_get_pic_suffix (cPP, relocp, exprP)
char **cPP;
bfd_reloc_code_real_type *relocp;
expressionS *exprP;
{
char *s = *cPP;
unsigned int i;
expressionS const_expr;
const struct pic_suffixes_struct
{
const char *const suffix;
unsigned int len;
bfd_reloc_code_real_type reloc;
} pic_suffixes[] =
{
#undef PICMAP
#define PICMAP(s, r) {s, sizeof (s) - 1, r}
PICMAP ("GOTPLT16", BFD_RELOC_CRIS_16_GOTPLT),
PICMAP ("GOTPLT", BFD_RELOC_CRIS_32_GOTPLT),
PICMAP ("PLTG", BFD_RELOC_CRIS_32_PLT_GOTREL),
PICMAP ("PLT", BFD_RELOC_CRIS_32_PLT_PCREL),
PICMAP ("GOTOFF", BFD_RELOC_CRIS_32_GOTREL),
PICMAP ("GOT16", BFD_RELOC_CRIS_16_GOT),
PICMAP ("GOT", BFD_RELOC_CRIS_32_GOT)
};
s++;
for (i = 0; i < sizeof (pic_suffixes)/sizeof (pic_suffixes[0]); i++)
{
if (strncmp (s, pic_suffixes[i].suffix, pic_suffixes[i].len) == 0
&& ! is_part_of_name (s[pic_suffixes[i].len]))
{
s += pic_suffixes[i].len;
if (*s == '+' || *s == '-')
{
if (! cris_get_expression (&s, &const_expr))
break;
exprP->X_add_symbol = make_expr_symbol (exprP);
exprP->X_op = O_add;
exprP->X_add_number = 0;
exprP->X_op_symbol = make_expr_symbol (&const_expr);
}
*relocp = pic_suffixes[i].reloc;
*cPP = s;
return;
}
}
}
char *
md_atof (type, litp, sizep)
char type ATTRIBUTE_UNUSED;
char *litp ATTRIBUTE_UNUSED;
int *sizep ATTRIBUTE_UNUSED;
{
return _("Bad call to md_atof () - floating point formats are not supported");
}
static void
cris_number_to_imm (bufp, val, n, fixP, seg)
char *bufp;
long val;
int n;
fixS *fixP;
segT seg;
{
segT sym_seg;
know (n <= 4);
know (fixP);
if (fixP->fx_addsy
&& (sym_seg = S_GET_SEGMENT (fixP->fx_addsy)) != seg)
val += sym_seg->vma;
if (fixP->fx_addsy != NULL || fixP->fx_pcrel)
switch (fixP->fx_r_type)
{
case BFD_RELOC_16_PCREL:
case BFD_RELOC_8_PCREL:
as_bad_where (fixP->fx_file, fixP->fx_line,
_("PC-relative relocation must be trivially resolved"));
default:
;
}
if (cris_arch != arch_cris_any_v0_v10
&& (fixP->fx_addsy != NULL || fixP->fx_pcrel))
return;
switch (fixP->fx_r_type)
{
case BFD_RELOC_CRIS_16_GOT:
case BFD_RELOC_CRIS_32_GOT:
case BFD_RELOC_CRIS_32_GOTREL:
case BFD_RELOC_CRIS_16_GOTPLT:
case BFD_RELOC_CRIS_32_GOTPLT:
case BFD_RELOC_CRIS_32_PLT_GOTREL:
case BFD_RELOC_CRIS_32_PLT_PCREL:
break;
case BFD_RELOC_32_PCREL:
if (fixP->fx_addsy != NULL || fixP->fx_pcrel)
break;
case BFD_RELOC_32:
bufp[3] = (val >> 24) & 0xFF;
bufp[2] = (val >> 16) & 0xFF;
bufp[1] = (val >> 8) & 0xFF;
bufp[0] = val & 0xFF;
break;
case BFD_RELOC_16:
case BFD_RELOC_16_PCREL:
if (val > 0xffff || val < -32768)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("Value not in 16 bit range: %ld"), val);
if (! fixP->fx_addsy)
{
bufp[1] = (val >> 8) & 0xFF;
bufp[0] = val & 0xFF;
}
break;
case BFD_RELOC_CRIS_SIGNED_16:
if (val > 32767 || val < -32768)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("Value not in 16 bit signed range: %ld"), val);
if (! fixP->fx_addsy)
{
bufp[1] = (val >> 8) & 0xFF;
bufp[0] = val & 0xFF;
}
break;
case BFD_RELOC_8:
case BFD_RELOC_8_PCREL:
if (val > 255 || val < -128)
as_bad_where (fixP->fx_file, fixP->fx_line, _("Value not in 8 bit range: %ld"), val);
if (! fixP->fx_addsy)
bufp[0] = val & 0xFF;
break;
case BFD_RELOC_CRIS_SIGNED_8:
if (val > 127 || val < -128)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("Value not in 8 bit signed range: %ld"), val);
if (! fixP->fx_addsy)
bufp[0] = val & 0xFF;
break;
case BFD_RELOC_CRIS_LAPCQ_OFFSET:
case BFD_RELOC_CRIS_UNSIGNED_4:
if (val > 15 || val < 0)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("Value not in 4 bit unsigned range: %ld"), val);
if (! fixP->fx_addsy)
bufp[0] |= val & 0x0F;
break;
case BFD_RELOC_CRIS_UNSIGNED_5:
if (val > 31 || val < 0)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("Value not in 5 bit unsigned range: %ld"), val);
if (! fixP->fx_addsy)
bufp[0] |= val & 0x1F;
break;
case BFD_RELOC_CRIS_SIGNED_6:
if (val > 31 || val < -32)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("Value not in 6 bit range: %ld"), val);
if (! fixP->fx_addsy)
bufp[0] |= val & 0x3F;
break;
case BFD_RELOC_CRIS_UNSIGNED_6:
if (val > 63 || val < 0)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("Value not in 6 bit unsigned range: %ld"), val);
if (! fixP->fx_addsy)
bufp[0] |= val & 0x3F;
break;
case BFD_RELOC_CRIS_BDISP8:
if (! fixP->fx_addsy)
bufp[0] = branch_disp (val);
break;
case BFD_RELOC_NONE:
if (! fixP->fx_addsy)
md_number_to_chars (bufp, val, n);
break;
case BFD_RELOC_VTABLE_INHERIT:
if (fixP->fx_addsy
&& !S_IS_DEFINED (fixP->fx_addsy)
&& !S_IS_WEAK (fixP->fx_addsy))
S_SET_WEAK (fixP->fx_addsy);
case BFD_RELOC_VTABLE_ENTRY:
fixP->fx_done = 0;
break;
default:
BAD_CASE (fixP->fx_r_type);
}
}
int
md_parse_option (arg, argp)
int arg;
char *argp ATTRIBUTE_UNUSED;
{
switch (arg)
{
case 'H':
case 'h':
printf (_("Please use --help to see usage and options for this assembler.\n"));
md_show_usage (stdout);
exit (EXIT_SUCCESS);
case 'N':
warn_for_branch_expansion = 1;
break;
case OPTION_NO_US:
demand_register_prefix = TRUE;
if (OUTPUT_FLAVOR == bfd_target_aout_flavour)
as_bad (_("--no-underscore is invalid with a.out format"));
else
symbols_have_leading_underscore = FALSE;
break;
case OPTION_US:
demand_register_prefix = FALSE;
symbols_have_leading_underscore = TRUE;
break;
case OPTION_PIC:
pic = TRUE;
break;
case OPTION_ARCH:
{
char *str = argp;
enum cris_archs argarch = cris_arch_from_string (&str);
if (argarch == arch_cris_unknown)
as_bad (_("invalid <arch> in --march=<arch>: %s"), argp);
else
cris_arch = argarch;
if (argarch == arch_crisv32)
{
err_for_dangerous_mul_placement = 0;
md_long_jump_size = crisv32_long_jump_size;
}
else
md_long_jump_size = cris_any_v0_v10_long_jump_size;
}
break;
case OPTION_MULBUG_ABORT_OFF:
err_for_dangerous_mul_placement = 0;
break;
case OPTION_MULBUG_ABORT_ON:
err_for_dangerous_mul_placement = 1;
break;
default:
return 0;
}
return 1;
}
valueT
md_section_align (segment, size)
segT segment;
valueT size;
{
if (OUTPUT_FLAVOR == bfd_target_aout_flavour)
{
if (segment == bss_section)
return (size + 1) & ~1;
return (size + 3) & ~3;
}
else
{
if (segment == text_section)
return (size + 3) & ~3;
}
return size;
}
arelent *
tc_gen_reloc (section, fixP)
asection *section ATTRIBUTE_UNUSED;
fixS *fixP;
{
arelent *relP;
bfd_reloc_code_real_type code;
switch (fixP->fx_r_type)
{
case BFD_RELOC_CRIS_SIGNED_8:
code = BFD_RELOC_8;
break;
case BFD_RELOC_CRIS_SIGNED_16:
code = BFD_RELOC_16;
break;
case BFD_RELOC_CRIS_16_GOT:
case BFD_RELOC_CRIS_32_GOT:
case BFD_RELOC_CRIS_16_GOTPLT:
case BFD_RELOC_CRIS_32_GOTPLT:
case BFD_RELOC_CRIS_32_GOTREL:
case BFD_RELOC_CRIS_32_PLT_GOTREL:
case BFD_RELOC_CRIS_32_PLT_PCREL:
case BFD_RELOC_32:
case BFD_RELOC_32_PCREL:
case BFD_RELOC_16:
case BFD_RELOC_8:
case BFD_RELOC_VTABLE_INHERIT:
case BFD_RELOC_VTABLE_ENTRY:
case BFD_RELOC_CRIS_UNSIGNED_8:
case BFD_RELOC_CRIS_UNSIGNED_16:
case BFD_RELOC_CRIS_LAPCQ_OFFSET:
code = fixP->fx_r_type;
break;
default:
as_bad_where (fixP->fx_file, fixP->fx_line,
_("Semantics error. This type of operand can not be relocated, it must be an assembly-time constant"));
return 0;
}
relP = (arelent *) xmalloc (sizeof (arelent));
assert (relP != 0);
relP->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
*relP->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
relP->address = fixP->fx_frag->fr_address + fixP->fx_where;
relP->addend = fixP->fx_offset;
if (OUTPUT_FLAVOR == bfd_target_aout_flavour
&& fixP->fx_addsy && S_IS_WEAK (fixP->fx_addsy)
&& ! bfd_is_und_section (S_GET_SEGMENT (fixP->fx_addsy)))
{
relP->addend -= S_GET_VALUE (fixP->fx_addsy);
}
relP->howto = bfd_reloc_type_lookup (stdoutput, code);
if (! relP->howto)
{
const char *name;
name = S_GET_NAME (fixP->fx_addsy);
if (name == NULL)
name = _("<unknown>");
as_fatal (_("Cannot generate relocation type for symbol %s, code %s"),
name, bfd_get_reloc_code_name (code));
}
return relP;
}
void
md_show_usage (stream)
FILE *stream;
{
fprintf (stream, _("CRIS-specific options:\n"));
fprintf (stream, "%s",
_(" -h, -H Don't execute, print this help text. Deprecated.\n"));
fprintf (stream, "%s",
_(" -N Warn when branches are expanded to jumps.\n"));
fprintf (stream, "%s",
_(" --underscore User symbols are normally prepended with underscore.\n"));
fprintf (stream, "%s",
_(" Registers will not need any prefix.\n"));
fprintf (stream, "%s",
_(" --no-underscore User symbols do not have any prefix.\n"));
fprintf (stream, "%s",
_(" Registers will require a `$'-prefix.\n"));
fprintf (stream, "%s",
_(" --pic Enable generation of position-independent code.\n"));
fprintf (stream, "%s",
_(" --march=<arch> Generate code for <arch>. Valid choices for <arch>\n\
are v0_v10, v10, v32 and common_v10_v32.\n"));
}
void
md_apply_fix3 (fixP, valP, seg)
fixS *fixP;
valueT *valP;
segT seg;
{
long val = (long) *valP;
char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
fixP->fx_done = 1;
if (fixP->fx_bit_fixP || fixP->fx_im_disp != 0)
{
as_bad_where (fixP->fx_file, fixP->fx_line, _("Invalid relocation"));
fixP->fx_done = 1;
}
else
{
if (fixP->fx_subsy != (symbolS *) NULL)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("expression too complex"));
if (fixP->fx_r_type == BFD_RELOC_CRIS_LAPCQ_OFFSET)
val /= 2;
cris_number_to_imm (buf, val, fixP->fx_size, fixP, seg);
}
}
long
md_pcrel_from (fixP)
fixS *fixP;
{
valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
if (OUTPUT_FLAVOR != bfd_target_elf_flavour
|| (fixP->fx_r_type != BFD_RELOC_8_PCREL
&& fixP->fx_r_type != BFD_RELOC_16_PCREL
&& fixP->fx_r_type != BFD_RELOC_32_PCREL
&& fixP->fx_r_type != BFD_RELOC_CRIS_LAPCQ_OFFSET))
as_bad_where (fixP->fx_file, fixP->fx_line,
_("Invalid pc-relative relocation"));
return fixP->fx_size + addr;
}
symbolS *
md_undefined_symbol (name)
char *name ATTRIBUTE_UNUSED;
{
return 0;
}
int
md_cris_force_relocation (fixp)
struct fix *fixp;
{
switch (fixp->fx_r_type)
{
case BFD_RELOC_CRIS_16_GOT:
case BFD_RELOC_CRIS_32_GOT:
case BFD_RELOC_CRIS_16_GOTPLT:
case BFD_RELOC_CRIS_32_GOTPLT:
case BFD_RELOC_CRIS_32_GOTREL:
case BFD_RELOC_CRIS_32_PLT_GOTREL:
case BFD_RELOC_CRIS_32_PLT_PCREL:
return 1;
default:
;
}
return generic_force_reloc (fixp);
}
void
tc_cris_check_adjusted_broken_word (new_offset, brokwP)
offsetT new_offset;
struct broken_word *brokwP;
{
if (new_offset > 32767 || new_offset < -32768)
as_bad_where (brokwP->frag->fr_file, brokwP->frag->fr_line,
_("Adjusted signed .word (%ld) overflows: `switch'-statement too large."),
(long) new_offset);
}
static void cris_force_reg_prefix ()
{
demand_register_prefix = TRUE;
}
static void cris_relax_reg_prefix ()
{
demand_register_prefix = FALSE;
}
static void cris_sym_leading_underscore ()
{
if (!symbols_have_leading_underscore)
as_bad (_(".syntax %s requires command-line option `--underscore'"),
SYNTAX_USER_SYM_LEADING_UNDERSCORE);
}
static void cris_sym_no_leading_underscore ()
{
if (symbols_have_leading_underscore)
as_bad (_(".syntax %s requires command-line option `--no-underscore'"),
SYNTAX_USER_SYM_NO_LEADING_UNDERSCORE);
}
static void
s_syntax (ignore)
int ignore ATTRIBUTE_UNUSED;
{
static const struct syntaxes
{
const char *const operand;
void (*fn) PARAMS ((void));
} syntax_table[] =
{{SYNTAX_ENFORCE_REG_PREFIX, cris_force_reg_prefix},
{SYNTAX_RELAX_REG_PREFIX, cris_relax_reg_prefix},
{SYNTAX_USER_SYM_LEADING_UNDERSCORE, cris_sym_leading_underscore},
{SYNTAX_USER_SYM_NO_LEADING_UNDERSCORE, cris_sym_no_leading_underscore}};
const struct syntaxes *sp;
for (sp = syntax_table;
sp < syntax_table + sizeof (syntax_table) / sizeof (syntax_table[0]);
sp++)
{
if (strncmp (input_line_pointer, sp->operand,
strlen (sp->operand)) == 0)
{
(sp->fn) ();
input_line_pointer += strlen (sp->operand);
demand_empty_rest_of_line ();
return;
}
}
as_bad (_("Unknown .syntax operand"));
}
static void
s_cris_file (dummy)
int dummy;
{
if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
as_bad (_("Pseudodirective .file is only valid when generating ELF"));
else
dwarf2_directive_file (dummy);
}
static void
s_cris_loc (dummy)
int dummy;
{
if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
as_bad (_("Pseudodirective .loc is only valid when generating ELF"));
else
dwarf2_directive_loc (dummy);
}
static enum cris_archs
cris_arch_from_string (str)
char **str;
{
static const struct cris_arch_struct
{
const char *const name;
enum cris_archs arch;
} arch_table[] =
{{"v0_v10", arch_cris_any_v0_v10},
{"v10", arch_crisv10},
{"v32", arch_crisv32},
{"common_v10_v32", arch_cris_common_v10_v32}};
const struct cris_arch_struct *ap;
for (ap = arch_table;
ap < arch_table + sizeof (arch_table) / sizeof (arch_table[0]);
ap++)
{
int len = strlen (ap->name);
if (strncmp (*str, ap->name, len) == 0
&& (str[0][len] == 0 || ISSPACE (str[0][len])))
{
*str += strlen (ap->name);
return ap->arch;
}
}
return arch_cris_unknown;
}
static int
cris_insn_ver_valid_for_arch (iver, arch)
enum cris_insn_version_usage iver;
enum cris_archs arch;
{
switch (arch)
{
case arch_cris_any_v0_v10:
return
(iver == cris_ver_version_all
|| iver == cris_ver_warning
|| iver == cris_ver_v0_3
|| iver == cris_ver_v3p
|| iver == cris_ver_v0_10
|| iver == cris_ver_sim_v0_10
|| iver == cris_ver_v3_10
|| iver == cris_ver_v8
|| iver == cris_ver_v8p
|| iver == cris_ver_v8_10
|| iver == cris_ver_v10
|| iver == cris_ver_v10p);
case arch_crisv32:
return
(iver == cris_ver_version_all
|| iver == cris_ver_v3p
|| iver == cris_ver_v8p
|| iver == cris_ver_v10p
|| iver == cris_ver_v32p);
case arch_cris_common_v10_v32:
return
(iver == cris_ver_version_all
|| iver == cris_ver_v3p
|| iver == cris_ver_v8p
|| iver == cris_ver_v10p);
case arch_crisv0:
return
(iver == cris_ver_version_all
|| iver == cris_ver_v0_3
|| iver == cris_ver_v0_10
|| iver == cris_ver_sim_v0_10);
case arch_crisv3:
return
(iver == cris_ver_version_all
|| iver == cris_ver_v0_3
|| iver == cris_ver_v3p
|| iver == cris_ver_v0_10
|| iver == cris_ver_sim_v0_10
|| iver == cris_ver_v3_10);
case arch_crisv8:
return
(iver == cris_ver_version_all
|| iver == cris_ver_v3p
|| iver == cris_ver_v0_10
|| iver == cris_ver_sim_v0_10
|| iver == cris_ver_v3_10
|| iver == cris_ver_v8
|| iver == cris_ver_v8p
|| iver == cris_ver_v8_10);
case arch_crisv10:
return
(iver == cris_ver_version_all
|| iver == cris_ver_v3p
|| iver == cris_ver_v0_10
|| iver == cris_ver_sim_v0_10
|| iver == cris_ver_v3_10
|| iver == cris_ver_v8p
|| iver == cris_ver_v8_10
|| iver == cris_ver_v10
|| iver == cris_ver_v10p);
default:
BAD_CASE (arch);
}
}
static void
s_cris_arch (dummy)
int dummy ATTRIBUTE_UNUSED;
{
char *str = input_line_pointer;
enum cris_archs arch = cris_arch_from_string (&str);
if (arch == arch_cris_unknown)
{
as_bad (_("unknown operand to .arch"));
while (is_part_of_name (*str))
str++;
}
else if (arch != cris_arch)
as_bad (_(".arch <arch> requires a matching --march=... option"));
input_line_pointer = str;
demand_empty_rest_of_line ();
return;
}