diff options
Diffstat (limited to 'gas/config/tc-m32c.c')
-rw-r--r-- | gas/config/tc-m32c.c | 1017 |
1 files changed, 1017 insertions, 0 deletions
diff --git a/gas/config/tc-m32c.c b/gas/config/tc-m32c.c new file mode 100644 index 0000000..8ee44f8 --- /dev/null +++ b/gas/config/tc-m32c.c @@ -0,0 +1,1017 @@ +/* tc-m32c.c -- Assembler for the Renesas M32C. + Copyright (C) 2005 Free Software Foundation. + Contributed by RedHat. + + 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 2, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GAS; see the file COPYING. If not, write to + the Free Software Foundation, 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <stdio.h> +#include "as.h" +#include "subsegs.h" +#include "symcat.h" +#include "opcodes/m32c-desc.h" +#include "opcodes/m32c-opc.h" +#include "cgen.h" +#include "elf/common.h" +#include "elf/m32c.h" +#include "libbfd.h" +#include "libiberty.h" +#include "safe-ctype.h" + +/* Structure to hold all of the different components + describing an individual instruction. */ +typedef struct +{ + const CGEN_INSN * insn; + const CGEN_INSN * orig_insn; + CGEN_FIELDS fields; +#if CGEN_INT_INSN_P + CGEN_INSN_INT buffer [1]; +#define INSN_VALUE(buf) (*(buf)) +#else + unsigned char buffer [CGEN_MAX_INSN_SIZE]; +#define INSN_VALUE(buf) (buf) +#endif + char * addr; + fragS * frag; + int num_fixups; + fixS * fixups [GAS_CGEN_MAX_FIXUPS]; + int indices [MAX_OPERAND_INSTANCES]; +} +m32c_insn; + +const char comment_chars[] = ";"; +const char line_comment_chars[] = "#"; +const char line_separator_chars[] = "|"; +const char EXP_CHARS[] = "eE"; +const char FLT_CHARS[] = "dD"; + +#define M32C_SHORTOPTS "" +const char * md_shortopts = M32C_SHORTOPTS; + +/* assembler options */ +#define OPTION_CPU_M16C (OPTION_MD_BASE) +#define OPTION_CPU_M32C (OPTION_MD_BASE + 1) + +struct option md_longopts[] = +{ + { "m16c", no_argument, NULL, OPTION_CPU_M16C }, + { "m32c", no_argument, NULL, OPTION_CPU_M32C }, + {NULL, no_argument, NULL, 0} +}; +size_t md_longopts_size = sizeof (md_longopts); + +/* Default machine */ + +#define DEFAULT_MACHINE bfd_mach_m16c +#define DEFAULT_FLAGS EF_M32C_CPU_M16C + +static unsigned long m32c_mach = bfd_mach_m16c; +static int cpu_mach = (1 << MACH_M16C); +static int insn_size; + +/* Flags to set in the elf header */ +static flagword m32c_flags = DEFAULT_FLAGS; + +static unsigned int m32c_isa = (1 << ISA_M16C); + +static void +set_isa (enum isa_attr isa_num) +{ + m32c_isa = (1 << isa_num); +} + +static void s_bss (int); + +int +md_parse_option (int c, char * arg ATTRIBUTE_UNUSED) +{ + switch (c) + { + case OPTION_CPU_M16C: + m32c_flags = (m32c_flags & ~EF_M32C_CPU_MASK) | EF_M32C_CPU_M16C; + m32c_mach = bfd_mach_m16c; + cpu_mach = (1 << MACH_M16C); + set_isa (ISA_M16C); + break; + + case OPTION_CPU_M32C: + m32c_flags = (m32c_flags & ~EF_M32C_CPU_MASK) | EF_M32C_CPU_M32C; + m32c_mach = bfd_mach_m32c; + cpu_mach = (1 << MACH_M32C); + set_isa (ISA_M32C); + break; + + default: + return 0; + } + return 1; +} + +void +md_show_usage (FILE * stream) +{ + fprintf (stream, _(" M32C specific command line options:\n")); +} + +static void +s_bss (int ignore ATTRIBUTE_UNUSED) +{ + int temp; + + temp = get_absolute_expression (); + subseg_set (bss_section, (subsegT) temp); + demand_empty_rest_of_line (); +} + +/* The target specific pseudo-ops which we support. */ +const pseudo_typeS md_pseudo_table[] = +{ + { "bss", s_bss, 0}, + { "word", cons, 4 }, + { NULL, NULL, 0 } +}; + + +void +md_begin (void) +{ + /* Initialize the `cgen' interface. */ + + /* Set the machine number and endian. */ + gas_cgen_cpu_desc = m32c_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, cpu_mach, + CGEN_CPU_OPEN_ENDIAN, + CGEN_ENDIAN_BIG, + CGEN_CPU_OPEN_ISAS, & m32c_isa, + CGEN_CPU_OPEN_END); + + m32c_cgen_init_asm (gas_cgen_cpu_desc); + + /* This is a callback from cgen to gas to parse operands. */ + cgen_set_parse_operand_fn (gas_cgen_cpu_desc, gas_cgen_parse_operand); + + /* Set the ELF flags if desired. */ + if (m32c_flags) + bfd_set_private_flags (stdoutput, m32c_flags); + + /* Set the machine type */ + bfd_default_set_arch_mach (stdoutput, bfd_arch_m32c, m32c_mach); + + insn_size = 0; +} + +void +m32c_md_end (void) +{ + int i, n_nops; + + /* Pad with nops for objdump. */ + n_nops = (32 - ((insn_size) % 32)) / 8; + for (i = 1; i <= n_nops; i++) + md_assemble ("nop"); +} + +void +m32c_start_line_hook (void) +{ +#if 0 /* not necessary....handled in the .cpu file */ + char *s = input_line_pointer; + char *sg; + + for (s = input_line_pointer ; s && s[0] != '\n'; s++) + { + if (s[0] == ':') + { + /* Remove :g suffix. Squeeze out blanks. */ + if (s[1] == 'g') + { + for (sg = s - 1; sg && sg >= input_line_pointer; sg--) + { + sg[2] = sg[0]; + } + sg[1] = ' '; + sg[2] = ' '; + input_line_pointer += 2; + } + } + } +#endif +} + +/* Process [[indirect-operands]] in instruction str. */ + +static bfd_boolean +m32c_indirect_operand (char *str) +{ + char *new_str; + char *s; + char *ns; + int ns_len; + char *ns_end; + enum indirect_type {none, relative, absolute} ; + enum indirect_type indirection [3] = { none, none, none }; + int brace_n [3] = { 0, 0, 0 }; + int operand; + + s = str; + operand = 1; + for (s = str; *s; s++) + { + if (s[0] == ',') + operand = 2; + /* [abs] where abs is not a0 or a1 */ + if (s[1] == '[' && ! (s[2] == 'a' && (s[3] == '0' || s[3] == '1')) + && (ISBLANK (s[0]) || s[0] == ',')) + indirection[operand] = absolute; + if (s[0] == ']' && s[1] == ']') + indirection[operand] = relative; + if (s[0] == '[' && s[1] == '[') + indirection[operand] = relative; + } + + if (indirection[1] == none && indirection[2] == none) + return FALSE; + + operand = 1; + ns_len = strlen (str); + new_str = (char*) xmalloc (ns_len); + ns = new_str; + ns_end = ns + ns_len; + + for (s = str; *s; s++) + { + if (s[0] == ',') + operand = 2; + + if (s[0] == '[' && ! brace_n[operand]) + { + brace_n[operand] += 1; + /* Squeeze [[ to [ if this is an indirect operand. */ + if (indirection[operand] != none) + continue; + } + + else if (s[0] == '[' && brace_n[operand]) + { + brace_n[operand] += 1; + } + else if (s[0] == ']' && s[1] == ']' && indirection[operand] == relative) + { + s += 1; /* skip one ]. */ + brace_n[operand] -= 2; /* allow for 2 [. */ + } + else if (s[0] == ']' && indirection[operand] == absolute) + { + brace_n[operand] -= 1; + continue; /* skip closing ]. */ + } + else if (s[0] == ']') + { + brace_n[operand] -= 1; + } + *ns = s[0]; + ns += 1; + if (ns >= ns_end) + return FALSE; + if (s[0] == 0) + break; + } + *ns = '\0'; + for (operand = 1; operand <= 2; operand++) + if (brace_n[operand]) + { + fprintf (stderr, "Unmatched [[operand-%d]] %d\n", operand, brace_n[operand]); + } + + if (indirection[1] != none && indirection[2] != none) + md_assemble ("src-dest-indirect"); + else if (indirection[1] != none) + md_assemble ("src-indirect"); + else if (indirection[2] != none) + md_assemble ("dest-indirect"); + + md_assemble (new_str); + free (new_str); + return TRUE; +} + +void +md_assemble (char * str) +{ + static int last_insn_had_delay_slot = 0; + m32c_insn insn; + char * errmsg; + + if (m32c_mach == bfd_mach_m32c && m32c_indirect_operand (str)) + return; + + /* Initialize GAS's cgen interface for a new instruction. */ + gas_cgen_init_parse (); + + insn.insn = m32c_cgen_assemble_insn + (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg); + + if (!insn.insn) + { + as_bad (errmsg); + return; + } + + /* Doesn't really matter what we pass for RELAX_P here. */ + gas_cgen_finish_insn (insn.insn, insn.buffer, + CGEN_FIELDS_BITSIZE (& insn.fields), 1, NULL); + + last_insn_had_delay_slot + = CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_DELAY_SLOT); + insn_size = CGEN_INSN_BITSIZE(insn.insn); +} + +/* The syntax in the manual says constants begin with '#'. + We just ignore it. */ + +void +md_operand (expressionS * exp) +{ + /* In case of a syntax error, escape back to try next syntax combo. */ + if (exp->X_op == O_absent) + gas_cgen_md_operand (exp); +} + +valueT +md_section_align (segT segment, valueT size) +{ + int align = bfd_get_section_alignment (stdoutput, segment); + return ((size + (1 << align) - 1) & (-1 << align)); +} + +symbolS * +md_undefined_symbol (char * name ATTRIBUTE_UNUSED) +{ + return 0; +} + +const relax_typeS md_relax_table[] = +{ + /* The fields are: + 1) most positive reach of this state, + 2) most negative reach of this state, + 3) how many bytes this mode will have in the variable part of the frag + 4) which index into the table to try if we can't fit into this one. */ + + /* 0 */ { 0, 0, 0, 0 }, /* unused */ + /* 1 */ { 0, 0, 0, 0 }, /* marker for "don't know yet" */ + + /* 2 */ { 127, -128, 2, 3 }, /* jcnd16_5.b */ + /* 3 */ { 32767, -32768, 5, 4 }, /* jcnd16_5.w */ + /* 4 */ { 0, 0, 6, 0 }, /* jcnd16_5.a */ + + /* 5 */ { 127, -128, 2, 6 }, /* jcnd16.b */ + /* 6 */ { 32767, -32768, 5, 7 }, /* jcnd16.w */ + /* 7 */ { 0, 0, 6, 0 }, /* jcnd16.a */ + + /* 8 */ { 8, 1, 1, 9 }, /* jmp16.s */ + /* 9 */ { 127, -128, 2, 10 }, /* jmp16.b */ + /* 10 */ { 32767, -32768, 3, 11 }, /* jmp16.w */ + /* 11 */ { 0, 0, 4, 0 }, /* jmp16.a */ + + /* 12 */ { 127, -128, 2, 13 }, /* jcnd32.b */ + /* 13 */ { 32767, -32768, 5, 14 }, /* jcnd32.w */ + /* 14 */ { 0, 0, 6, 0 }, /* jcnd32.a */ + + /* 15 */ { 8, 1, 1, 16 }, /* jmp32.s */ + /* 16 */ { 127, -128, 2, 17 }, /* jmp32.b */ + /* 17 */ { 32767, -32768, 3, 18 }, /* jmp32.w */ + /* 18 */ { 0, 0, 4, 0 } /* jmp32.a */ +}; + +enum { + M32C_MACRO_JCND16_5_W, + M32C_MACRO_JCND16_5_A, + M32C_MACRO_JCND16_W, + M32C_MACRO_JCND16_A, + M32C_MACRO_JCND32_W, + M32C_MACRO_JCND32_A, +} M32C_Macros; + +static struct { + int insn; + int bytes; + int insn_for_extern; + int pcrel_aim_offset; +} subtype_mappings[] = { + /* 0 */ { 0, 0, 0, 0 }, + /* 1 */ { 0, 0, 0, 0 }, + + /* 2 */ { M32C_INSN_JCND16_5, 2, -M32C_MACRO_JCND16_5_A, 1 }, + /* 3 */ { -M32C_MACRO_JCND16_5_W, 5, -M32C_MACRO_JCND16_5_A, 4 }, + /* 4 */ { -M32C_MACRO_JCND16_5_A, 6, -M32C_MACRO_JCND16_5_A, 0 }, + + /* 5 */ { M32C_INSN_JCND16, 3, -M32C_MACRO_JCND16_A, 1 }, + /* 6 */ { -M32C_MACRO_JCND16_W, 6, -M32C_MACRO_JCND16_A, 4 }, + /* 7 */ { -M32C_MACRO_JCND16_A, 7, -M32C_MACRO_JCND16_A, 0 }, + + /* 8 */ { M32C_INSN_JMP16_S, 1, M32C_INSN_JMP16_A, 0 }, + /* 9 */ { M32C_INSN_JMP16_B, 2, M32C_INSN_JMP16_A, 1 }, + /* 10 */ { M32C_INSN_JMP16_W, 3, M32C_INSN_JMP16_A, 2 }, + /* 11 */ { M32C_INSN_JMP16_A, 4, M32C_INSN_JMP16_A, 0 }, + + /* 12 */ { M32C_INSN_JCND32, 2, -M32C_MACRO_JCND32_A, 1 }, + /* 13 */ { -M32C_MACRO_JCND32_W, 5, -M32C_MACRO_JCND32_A, 4 }, + /* 14 */ { -M32C_MACRO_JCND32_A, 6, -M32C_MACRO_JCND32_A, 0 }, + + /* 15 */ { M32C_INSN_JMP32_S, 1, M32C_INSN_JMP32_A, 0 }, + /* 16 */ { M32C_INSN_JMP32_B, 2, M32C_INSN_JMP32_A, 1 }, + /* 17 */ { M32C_INSN_JMP32_W, 3, M32C_INSN_JMP32_A, 2 }, + /* 18 */ { M32C_INSN_JMP32_A, 4, M32C_INSN_JMP32_A, 0 } +}; +#define NUM_MAPPINGS (sizeof (subtype_mappings) / sizeof (subtype_mappings[0])) + +void +m32c_prepare_relax_scan (fragS *fragP, offsetT *aim, relax_substateT this_state) +{ + symbolS *symbolP = fragP->fr_symbol; + if (symbolP && !S_IS_DEFINED (symbolP)) + *aim = 0; + /* Adjust for m32c pcrel not being relative to the next opcode. */ + *aim += subtype_mappings[this_state].pcrel_aim_offset; +} + +static int +insn_to_subtype (int insn) +{ + unsigned int i; + for (i=0; i<NUM_MAPPINGS; i++) + if (insn == subtype_mappings[i].insn) + { + /*printf("mapping %d used\n", i);*/ + return i; + } + abort (); +} + +/* Return an initial guess of the length by which a fragment must grow to + hold a branch to reach its destination. + Also updates fr_type/fr_subtype as necessary. + + Called just before doing relaxation. + Any symbol that is now undefined will not become defined. + The guess for fr_var is ACTUALLY the growth beyond fr_fix. + Whatever we do to grow fr_fix or fr_var contributes to our returned value. + Although it may not be explicit in the frag, pretend fr_var starts with a + 0 value. */ + +int +md_estimate_size_before_relax (fragS * fragP, segT segment ATTRIBUTE_UNUSED) +{ + int where = fragP->fr_opcode - fragP->fr_literal; + + if (fragP->fr_subtype == 1) + fragP->fr_subtype = insn_to_subtype (fragP->fr_cgen.insn->base->num); + + if (S_GET_SEGMENT (fragP->fr_symbol) != segment) + { + int new_insn; + + new_insn = subtype_mappings[fragP->fr_subtype].insn_for_extern; + fragP->fr_subtype = insn_to_subtype (new_insn); + } + + if (fragP->fr_cgen.insn->base + && fragP->fr_cgen.insn->base->num + != subtype_mappings[fragP->fr_subtype].insn + && subtype_mappings[fragP->fr_subtype].insn > 0) + { + int new_insn= subtype_mappings[fragP->fr_subtype].insn; + if (new_insn >= 0) + { + fragP->fr_cgen.insn = (fragP->fr_cgen.insn + - fragP->fr_cgen.insn->base->num + + new_insn); + } + } + + return subtype_mappings[fragP->fr_subtype].bytes - (fragP->fr_fix - where); +} + +/* *fragP has been relaxed to its final size, and now needs to have + the bytes inside it modified to conform to the new size. + + Called after relaxation is finished. + fragP->fr_type == rs_machine_dependent. + fragP->fr_subtype is the subtype of what the address relaxed to. */ + +static int +target_address_for (fragS *frag) +{ + int rv = frag->fr_offset; + symbolS *sym = frag->fr_symbol; + + if (sym) + rv += S_GET_VALUE (sym); + + /*printf("target_address_for returns %d\n", rv);*/ + return rv; +} + +void +md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED, + segT sec ATTRIBUTE_UNUSED, + fragS * fragP ATTRIBUTE_UNUSED) +{ + int addend; + int operand; + int new_insn; + int where = fragP->fr_opcode - fragP->fr_literal; + unsigned char *op = (unsigned char *)fragP->fr_opcode; + + addend = target_address_for (fragP) - (fragP->fr_address + where); + new_insn = subtype_mappings[fragP->fr_subtype].insn; + + fragP->fr_fix = where + subtype_mappings[fragP->fr_subtype].bytes; + + switch (subtype_mappings[fragP->fr_subtype].insn) + { + case M32C_INSN_JCND16_5: + op[1] = addend - 1; + operand = M32C_OPERAND_LAB_8_8; + break; + + case -M32C_MACRO_JCND16_5_W: + op[0] ^= 0x04; + op[1] = 4; + op[2] = 0xf4; + op[3] = addend - 3; + op[4] = (addend - 3) >> 8; + operand = M32C_OPERAND_LAB_8_16; + where += 2; + new_insn = M32C_INSN_JMP16_W; + break; + + case -M32C_MACRO_JCND16_5_A: + op[0] ^= 0x04; + op[1] = 5; + op[2] = 0xfc; + operand = M32C_OPERAND_LAB_8_24; + where += 2; + new_insn = M32C_INSN_JMP16_A; + break; + + + case M32C_INSN_JCND16: + op[2] = addend - 2; + operand = M32C_OPERAND_LAB_16_8; + break; + + case -M32C_MACRO_JCND16_W: + op[1] ^= 0x04; + op[2] = 4; + op[3] = 0xf4; + op[4] = addend - 4; + op[5] = (addend - 4) >> 8; + operand = M32C_OPERAND_LAB_8_16; + where += 3; + new_insn = M32C_INSN_JMP16_W; + break; + + case -M32C_MACRO_JCND16_A: + op[1] ^= 0x04; + op[2] = 5; + op[3] = 0xfc; + operand = M32C_OPERAND_LAB_8_24; + where += 3; + new_insn = M32C_INSN_JMP16_A; + break; + + case M32C_INSN_JMP16_S: + op[0] = 0x60 | ((addend-2) & 0x07); + operand = M32C_OPERAND_LAB_5_3; + break; + + case M32C_INSN_JMP16_B: + op[0] = 0xfe; + op[1] = addend - 1; + operand = M32C_OPERAND_LAB_8_8; + break; + + case M32C_INSN_JMP16_W: + op[0] = 0xf4; + op[1] = addend - 1; + op[2] = (addend - 1) >> 8; + operand = M32C_OPERAND_LAB_8_16; + break; + + case M32C_INSN_JMP16_A: + op[0] = 0xfc; + op[1] = 0; + op[2] = 0; + op[3] = 0; + operand = M32C_OPERAND_LAB_8_24; + break; + + case M32C_INSN_JCND32: + op[1] = addend - 1; + operand = M32C_OPERAND_LAB_8_8; + break; + + case -M32C_MACRO_JCND32_W: + op[0] ^= 0x40; + op[1] = 4; + op[2] = 0xce; + op[3] = addend - 3; + op[4] = (addend - 3) >> 8; + operand = M32C_OPERAND_LAB_8_16; + where += 2; + new_insn = M32C_INSN_JMP32_W; + break; + + case -M32C_MACRO_JCND32_A: + op[0] ^= 0x40; + op[1] = 5; + op[2] = 0xcc; + operand = M32C_OPERAND_LAB_8_24; + where += 2; + new_insn = M32C_INSN_JMP32_A; + break; + + + + case M32C_INSN_JMP32_S: + addend = ((addend-2) & 0x07); + op[0] = 0x4a | (addend & 0x01) | ((addend << 3) & 0x30); + operand = M32C_OPERAND_LAB32_JMP_S; + break; + + case M32C_INSN_JMP32_B: + op[0] = 0xbb; + op[1] = addend - 1; + operand = M32C_OPERAND_LAB_8_8; + break; + + case M32C_INSN_JMP32_W: + op[0] = 0xce; + op[1] = addend - 1; + op[2] = (addend - 1) >> 8; + operand = M32C_OPERAND_LAB_8_16; + break; + + case M32C_INSN_JMP32_A: + op[0] = 0xcc; + op[1] = 0; + op[2] = 0; + op[3] = 0; + operand = M32C_OPERAND_LAB_8_24; + break; + + + default: + printf("\nHey! Need more opcode converters! missing: %d %s\n\n", + fragP->fr_subtype, + fragP->fr_cgen.insn->base->name); + abort(); + } + + if (S_GET_SEGMENT (fragP->fr_symbol) != sec + || operand == M32C_OPERAND_LAB_8_24) + { + assert (fragP->fr_cgen.insn != 0); + gas_cgen_record_fixup (fragP, + where, + fragP->fr_cgen.insn, + (fragP->fr_fix - where) * 8, + cgen_operand_lookup_by_num (gas_cgen_cpu_desc, + operand), + fragP->fr_cgen.opinfo, + fragP->fr_symbol, fragP->fr_offset); + } +} + +/* Functions concerning relocs. */ + +/* The location from which a PC relative jump should be calculated, + given a PC relative reloc. */ + +long +md_pcrel_from_section (fixS * fixP, segT sec) +{ + if (fixP->fx_addsy != (symbolS *) NULL + && (! S_IS_DEFINED (fixP->fx_addsy) + || S_GET_SEGMENT (fixP->fx_addsy) != sec)) + /* The symbol is undefined (or is defined but not in this section). + Let the linker figure it out. */ + return 0; + + return (fixP->fx_frag->fr_address + fixP->fx_where); +} + +/* Return the bfd reloc type for OPERAND of INSN at fixup FIXP. + Returns BFD_RELOC_NONE if no reloc type can be found. + *FIXP may be modified if desired. */ + +bfd_reloc_code_real_type +md_cgen_lookup_reloc (const CGEN_INSN * insn ATTRIBUTE_UNUSED, + const CGEN_OPERAND * operand, + fixS * fixP ATTRIBUTE_UNUSED) +{ + static const struct op_reloc { + /* A CGEN operand type that can be a relocatable expression. */ + CGEN_OPERAND_TYPE operand; + + /* The appropriate BFD reloc type to use for that. */ + bfd_reloc_code_real_type reloc; + + /* The offset from the start of the instruction to the field to be + relocated, in bytes. */ + int offset; + } op_reloc_table[] = { + + /* Absolute relocs for 16-bit fields. */ + { M32C_OPERAND_IMM_16_HI, BFD_RELOC_16, 2 }, + { M32C_OPERAND_IMM_24_HI, BFD_RELOC_16, 3 }, + { M32C_OPERAND_IMM_32_HI, BFD_RELOC_16, 4 }, + { M32C_OPERAND_DSP_16_S16, BFD_RELOC_16, 2 }, + { M32C_OPERAND_DSP_24_S16, BFD_RELOC_16, 3 }, + { M32C_OPERAND_DSP_32_S16, BFD_RELOC_16, 4 }, + { M32C_OPERAND_DSP_40_S16, BFD_RELOC_16, 5 }, + { M32C_OPERAND_DSP_8_U16, BFD_RELOC_16, 1 }, + { M32C_OPERAND_DSP_16_U16, BFD_RELOC_16, 2 }, + { M32C_OPERAND_DSP_24_U16, BFD_RELOC_16, 3 }, + { M32C_OPERAND_DSP_32_U16, BFD_RELOC_16, 4 }, + + /* Absolute relocs for 24-bit fields. */ + { M32C_OPERAND_LAB_8_24, BFD_RELOC_24, 1 }, + { M32C_OPERAND_DSP_16_U24, BFD_RELOC_24, 2 }, + { M32C_OPERAND_DSP_24_U24, BFD_RELOC_24, 3 }, + { M32C_OPERAND_DSP_32_U24, BFD_RELOC_24, 4 }, + { M32C_OPERAND_DSP_40_U24, BFD_RELOC_24, 5 }, + + /* Absolute relocs for 32-bit fields. */ + { M32C_OPERAND_IMM_16_SI, BFD_RELOC_32, 2 }, + { M32C_OPERAND_IMM_24_SI, BFD_RELOC_32, 3 }, + { M32C_OPERAND_IMM_32_SI, BFD_RELOC_32, 4 }, + { M32C_OPERAND_IMM_40_SI, BFD_RELOC_32, 5 }, + + }; + + int i; + + for (i = ARRAY_SIZE (op_reloc_table); --i >= 0; ) + { + const struct op_reloc *or = &op_reloc_table[i]; + + if (or->operand == operand->type) + { + fixP->fx_where += or->offset; + fixP->fx_size -= or->offset; + return or->reloc; + } + } + + fprintf + (stderr, + "Error: tc-m32c.c:md_cgen_lookup_reloc Unimplemented relocation %d\n", + operand->type); + + return BFD_RELOC_NONE; +} + +/* See whether we need to force a relocation into the output file. + This is used to force out switch and PC relative relocations when + relaxing. */ + +int +m32c_force_relocation (fixS * fixp) +{ + int reloc = fixp->fx_r_type; + + if (reloc > (int)BFD_RELOC_UNUSED) + { + reloc -= (int)BFD_RELOC_UNUSED; + switch (reloc) + { + case M32C_OPERAND_DSP_32_S16: + case M32C_OPERAND_DSP_32_U16: + case M32C_OPERAND_IMM_32_HI: + case M32C_OPERAND_DSP_16_S16: + case M32C_OPERAND_DSP_16_U16: + case M32C_OPERAND_IMM_16_HI: + case M32C_OPERAND_DSP_24_S16: + case M32C_OPERAND_DSP_24_U16: + case M32C_OPERAND_IMM_24_HI: + return 1; + } + } + else + { + if (fixp->fx_r_type == BFD_RELOC_16) + return 1; + } + + return generic_force_reloc (fixp); +} + +/* Write a value out to the object file, using the appropriate endianness. */ + +void +md_number_to_chars (char * buf, valueT val, int n) +{ + number_to_chars_littleendian (buf, val, n); +} + +/* Turn a string in input_line_pointer into a floating point constant of type + type, and store the appropriate bytes in *litP. The number of LITTLENUMS + emitted is stored in *sizeP . An error message is returned, or NULL on OK. */ + +/* Equal to MAX_PRECISION in atof-ieee.c. */ +#define MAX_LITTLENUMS 6 + +char * +md_atof (int type, char * litP, int * sizeP) +{ + int i; + int prec; + LITTLENUM_TYPE words [MAX_LITTLENUMS]; + char * t; + + switch (type) + { + case 'f': + case 'F': + case 's': + case 'S': + prec = 2; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + prec = 4; + break; + + /* FIXME: Some targets allow other format chars for bigger sizes here. */ + + default: + * sizeP = 0; + return _("Bad call to md_atof()"); + } + + t = atof_ieee (input_line_pointer, type, words); + if (t) + input_line_pointer = t; + * sizeP = prec * sizeof (LITTLENUM_TYPE); + + for (i = 0; i < prec; i++) + { + md_number_to_chars (litP, (valueT) words[i], + sizeof (LITTLENUM_TYPE)); + litP += sizeof (LITTLENUM_TYPE); + } + + return 0; +} + +bfd_boolean +m32c_fix_adjustable (fixS * fixP) +{ + int reloc; + if (fixP->fx_addsy == NULL) + return 1; + + /* We need the symbol name for the VTABLE entries. */ + reloc = fixP->fx_r_type; + if (reloc > (int)BFD_RELOC_UNUSED) + { + reloc -= (int)BFD_RELOC_UNUSED; + switch (reloc) + { + case M32C_OPERAND_DSP_32_S16: + case M32C_OPERAND_DSP_32_U16: + case M32C_OPERAND_IMM_32_HI: + case M32C_OPERAND_DSP_16_S16: + case M32C_OPERAND_DSP_16_U16: + case M32C_OPERAND_IMM_16_HI: + case M32C_OPERAND_DSP_24_S16: + case M32C_OPERAND_DSP_24_U16: + case M32C_OPERAND_IMM_24_HI: + return 0; + } + } + else + { + if (fixP->fx_r_type == BFD_RELOC_16) + return 0; + } + + /* Do not adjust relocations involving symbols in merged sections. + + A reloc patching in the value of some symbol S plus some addend A + can be produced in different ways: + + 1) It might simply be a reference to the data at S + A. Clearly, + if linker merging shift that data around, the value patched in + by the reloc needs to be adjusted accordingly. + + 2) Or, it might be a reference to S, with A added in as a constant + bias. For example, given code like this: + + static int S[100]; + + ... S[i - 8] ... + + it would be reasonable for the compiler to rearrange the array + reference to something like: + + ... (S-8)[i] ... + + and emit assembly code that refers to S - (8 * sizeof (int)), + so the subtraction is done entirely at compile-time. In this + case, the reloc's addend A would be -(8 * sizeof (int)), and + shifting around code or data at S + A should not affect the + reloc: the reloc isn't referring to that code or data at all. + + The linker has no way of knowing which case it has in hand. So, + to disambiguate, we have the linker always treat reloc addends as + in case 2): they're constants that should be simply added to the + symbol value, just like the reloc says. And we express case 1) + in different way: we have the compiler place a label at the real + target, and reference that label with an addend of zero. (The + compiler is unlikely to reference code using a label plus an + offset anyway, since it doesn't know the sizes of the + instructions.) + + The simplification being done by gas/write.c:adjust_reloc_syms, + however, turns the explicit-label usage into the label-plus- + offset usage, re-introducing the ambiguity the compiler avoided. + So we need to disable that simplification for symbols referring + to merged data. + + This only affects object size a little bit. */ + if (S_GET_SEGMENT (fixP->fx_addsy)->flags & SEC_MERGE) + return 0; + + return 1; +} + +/* Worker function for m32c_is_colon_insn(). */ +static char restore_colon PARAMS ((int)); + +static char +restore_colon (int advance_i_l_p_by) +{ + char c; + + /* Restore the colon, and advance input_line_pointer to + the end of the new symbol. */ + * input_line_pointer = ':'; + input_line_pointer += advance_i_l_p_by; + c = * input_line_pointer; + * input_line_pointer = 0; + + return c; +} + +/* Determines if the symbol starting at START and ending in + a colon that was at the location pointed to by INPUT_LINE_POINTER + (but which has now been replaced bu a NUL) is in fact an + :Z, :S, :Q, or :G suffix. + If it is, then it restores the colon, advances INPUT_LINE_POINTER + to the real end of the instruction/symbol, and returns the character + that really terminated the symbol. Otherwise it returns 0. */ +char +m32c_is_colon_insn (char *start ATTRIBUTE_UNUSED) +{ + char * i_l_p = input_line_pointer; + + /* Check to see if the text following the colon is 'G' */ + if (TOLOWER (i_l_p[1]) == 'g' && (i_l_p[2] == ' ' || i_l_p[2] == '\t')) + return restore_colon (2); + + /* Check to see if the text following the colon is 'Q' */ + if (TOLOWER (i_l_p[1]) == 'q' && (i_l_p[2] == ' ' || i_l_p[2] == '\t')) + return restore_colon (2); + + /* Check to see if the text following the colon is 'S' */ + if (TOLOWER (i_l_p[1]) == 's' && (i_l_p[2] == ' ' || i_l_p[2] == '\t')) + return restore_colon (2); + + /* Check to see if the text following the colon is 'Z' */ + if (TOLOWER (i_l_p[1]) == 'z' && (i_l_p[2] == ' ' || i_l_p[2] == '\t')) + return restore_colon (2); + + return 0; +} + |