/* tc-m32c.c -- Assembler for the Renesas M32C.
- Copyright (C) 2005 Free Software Foundation.
+ Copyright (C) 2005, 2006 Free Software Foundation.
Contributed by RedHat.
This file is part of GAS, the GNU Assembler.
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 "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
}
m32c_insn;
+#define rl_for(_insn) (CGEN_ATTR_CGEN_INSN_RL_TYPE_VALUE (&((_insn).insn->base->attrs)))
+#define relaxable(_insn) (CGEN_ATTR_CGEN_INSN_RELAXABLE_VALUE (&((_insn).insn->base->attrs)))
+
const char comment_chars[] = ";";
const char line_comment_chars[] = "#";
const char line_separator_chars[] = "|";
/* assembler options */
#define OPTION_CPU_M16C (OPTION_MD_BASE)
#define OPTION_CPU_M32C (OPTION_MD_BASE + 1)
+#define OPTION_LINKRELAX (OPTION_MD_BASE + 2)
struct option md_longopts[] =
{
{ "m16c", no_argument, NULL, OPTION_CPU_M16C },
{ "m32c", no_argument, NULL, OPTION_CPU_M32C },
+ { "relax", no_argument, NULL, OPTION_LINKRELAX },
{NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof (md_longopts);
static unsigned long m32c_mach = bfd_mach_m16c;
static int cpu_mach = (1 << MACH_M16C);
static int insn_size;
+static int m32c_relax = 0;
/* Flags to set in the elf header */
static flagword m32c_flags = DEFAULT_FLAGS;
-static unsigned int m32c_isa = (1 << ISA_M16C);
+static char default_isa = 1 << (7 - ISA_M16C);
+static CGEN_BITSET m32c_isa = {1, & default_isa};
static void
set_isa (enum isa_attr isa_num)
{
- m32c_isa = (1 << isa_num);
+ cgen_bitset_set (& m32c_isa, isa_num);
}
static void s_bss (int);
set_isa (ISA_M32C);
break;
+ case OPTION_LINKRELAX:
+ m32c_relax = 1;
+ break;
+
default:
return 0;
}
const pseudo_typeS md_pseudo_table[] =
{
{ "bss", s_bss, 0},
+ { "3byte", cons, 3 },
{ "word", cons, 4 },
{ NULL, NULL, 0 }
};
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_ISAS, & m32c_isa,
CGEN_CPU_OPEN_END);
m32c_cgen_init_asm (gas_cgen_cpu_desc);
{
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");
+ if (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE)
+ {
+ /* Pad with nops for objdump. */
+ n_nops = (32 - ((insn_size) % 32)) / 8;
+ for (i = 1; i <= n_nops; i++)
+ md_assemble ("nop");
+ }
}
void
static int last_insn_had_delay_slot = 0;
m32c_insn insn;
char * errmsg;
+ finished_insnS results;
+ int rl_type;
if (m32c_mach == bfd_mach_m32c && m32c_indirect_operand (str))
return;
return;
}
+ results.num_fixups = 0;
/* 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);
+ CGEN_FIELDS_BITSIZE (& insn.fields), 1, &results);
last_insn_had_delay_slot
= CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_DELAY_SLOT);
insn_size = CGEN_INSN_BITSIZE(insn.insn);
+
+ rl_type = rl_for (insn);
+
+ /* We have to mark all the jumps, because we need to adjust them
+ when we delete bytes, but we only need to mark the displacements
+ if they're symbolic - if they're not, we've already picked the
+ shortest opcode by now. The linker, however, will still have to
+ check any operands to see if they're the displacement type, since
+ we don't know (nor record) *which* operands are relaxable. */
+ if (m32c_relax
+ && rl_type != RL_TYPE_NONE
+ && (rl_type == RL_TYPE_JUMP || results.num_fixups)
+ && !relaxable (insn))
+ {
+ int reloc = 0;
+ int addend = results.num_fixups + 16 * insn_size/8;
+
+ switch (rl_for (insn))
+ {
+ case RL_TYPE_JUMP: reloc = BFD_RELOC_M32C_RL_JUMP; break;
+ case RL_TYPE_1ADDR: reloc = BFD_RELOC_M32C_RL_1ADDR; break;
+ case RL_TYPE_2ADDR: reloc = BFD_RELOC_M32C_RL_2ADDR; break;
+ }
+ if (insn.insn->base->num == M32C_INSN_JMP16_S
+ || insn.insn->base->num == M32C_INSN_JMP32_S)
+ addend = 0x10;
+
+ fix_new (results.frag,
+ results.addr - results.frag->fr_literal,
+ 0, abs_section_sym, addend, 0,
+ reloc);
+ }
}
/* The syntax in the manual says constants begin with '#'.
/* 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 */
+ /* 18 */ { 0, 0, 4, 0 }, /* jmp32.a */
+
+ /* 19 */ { 32767, -32768, 3, 20 }, /* jsr16.w */
+ /* 20 */ { 0, 0, 4, 0 }, /* jsr16.a */
+ /* 21 */ { 32767, -32768, 3, 11 }, /* jsr32.w */
+ /* 22 */ { 0, 0, 4, 0 }, /* jsr32.a */
+
+ /* 23 */ { 0, 0, 3, 0 }, /* adjnz pc8 */
+ /* 24 */ { 0, 0, 4, 0 }, /* adjnz disp8 pc8 */
+ /* 25 */ { 0, 0, 5, 0 }, /* adjnz disp16 pc8 */
+ /* 26 */ { 0, 0, 6, 0 } /* adjnz disp24 pc8 */
};
enum {
M32C_MACRO_JCND16_A,
M32C_MACRO_JCND32_W,
M32C_MACRO_JCND32_A,
+ /* the digit is the array index of the pcrel byte */
+ M32C_MACRO_ADJNZ_2,
+ M32C_MACRO_ADJNZ_3,
+ M32C_MACRO_ADJNZ_4,
+ M32C_MACRO_ADJNZ_5,
} M32C_Macros;
static struct {
/* 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 }
+ /* 18 */ { M32C_INSN_JMP32_A, 4, M32C_INSN_JMP32_A, 0 },
+
+ /* 19 */ { M32C_INSN_JSR16_W, 3, M32C_INSN_JSR16_A, 2 },
+ /* 20 */ { M32C_INSN_JSR16_A, 4, M32C_INSN_JSR16_A, 0 },
+ /* 21 */ { M32C_INSN_JSR32_W, 3, M32C_INSN_JSR32_A, 2 },
+ /* 22 */ { M32C_INSN_JSR32_A, 4, M32C_INSN_JSR32_A, 0 },
+
+ /* 23 */ { -M32C_MACRO_ADJNZ_2, 3, -M32C_MACRO_ADJNZ_2, 0 },
+ /* 24 */ { -M32C_MACRO_ADJNZ_3, 4, -M32C_MACRO_ADJNZ_3, 0 },
+ /* 25 */ { -M32C_MACRO_ADJNZ_4, 5, -M32C_MACRO_ADJNZ_4, 0 },
+ /* 26 */ { -M32C_MACRO_ADJNZ_5, 6, -M32C_MACRO_ADJNZ_5, 0 }
};
#define NUM_MAPPINGS (sizeof (subtype_mappings) / sizeof (subtype_mappings[0]))
}
static int
-insn_to_subtype (int insn)
+insn_to_subtype (int inum, const CGEN_INSN *insn)
{
unsigned int i;
+
+ if (insn
+ && (strncmp (insn->base->mnemonic, "adjnz", 5) == 0
+ || strncmp (insn->base->mnemonic, "sbjnz", 5) == 0))
+ {
+ i = 23 + insn->base->bitsize/8 - 3;
+ /*printf("mapping %d used for %s\n", i, insn->base->mnemonic);*/
+ return i;
+ }
+
for (i=0; i<NUM_MAPPINGS; i++)
- if (insn == subtype_mappings[i].insn)
+ if (inum == subtype_mappings[i].insn)
{
/*printf("mapping %d used\n", i);*/
return i;
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);
+ fragP->fr_subtype = insn_to_subtype (fragP->fr_cgen.insn->base->num, fragP->fr_cgen.insn);
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);
+ fragP->fr_subtype = insn_to_subtype (new_insn, 0);
}
if (fragP->fr_cgen.insn->base
int operand;
int new_insn;
int where = fragP->fr_opcode - fragP->fr_literal;
+ int rl_where = fragP->fr_opcode - fragP->fr_literal;
unsigned char *op = (unsigned char *)fragP->fr_opcode;
+ int op_base = 0;
+ int op_op = 0;
+ int rl_addend = 0;
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;
+ op_base = 0;
+
switch (subtype_mappings[fragP->fr_subtype].insn)
{
case M32C_INSN_JCND16_5:
op[1] = addend - 1;
operand = M32C_OPERAND_LAB_8_8;
+ op_op = 1;
+ rl_addend = 0x21;
break;
case -M32C_MACRO_JCND16_5_W:
operand = M32C_OPERAND_LAB_8_16;
where += 2;
new_insn = M32C_INSN_JMP16_W;
+ op_base = 2;
+ op_op = 3;
+ rl_addend = 0x51;
break;
case -M32C_MACRO_JCND16_5_A:
operand = M32C_OPERAND_LAB_8_24;
where += 2;
new_insn = M32C_INSN_JMP16_A;
+ op_base = 2;
+ op_op = 3;
+ rl_addend = 0x61;
break;
case M32C_INSN_JCND16:
op[2] = addend - 2;
operand = M32C_OPERAND_LAB_16_8;
+ op_base = 0;
+ op_op = 2;
+ rl_addend = 0x31;
break;
case -M32C_MACRO_JCND16_W:
operand = M32C_OPERAND_LAB_8_16;
where += 3;
new_insn = M32C_INSN_JMP16_W;
+ op_base = 3;
+ op_op = 4;
+ rl_addend = 0x61;
break;
case -M32C_MACRO_JCND16_A:
operand = M32C_OPERAND_LAB_8_24;
where += 3;
new_insn = M32C_INSN_JMP16_A;
+ op_base = 3;
+ op_op = 4;
+ rl_addend = 0x71;
break;
case M32C_INSN_JMP16_S:
op[0] = 0x60 | ((addend-2) & 0x07);
operand = M32C_OPERAND_LAB_5_3;
+ op_base = 0;
+ op_op = 0;
+ rl_addend = 0x10;
break;
case M32C_INSN_JMP16_B:
op[0] = 0xfe;
op[1] = addend - 1;
operand = M32C_OPERAND_LAB_8_8;
+ op_base = 0;
+ op_op = 1;
+ rl_addend = 0x21;
break;
case M32C_INSN_JMP16_W:
op[1] = addend - 1;
op[2] = (addend - 1) >> 8;
operand = M32C_OPERAND_LAB_8_16;
+ op_base = 0;
+ op_op = 1;
+ rl_addend = 0x31;
break;
case M32C_INSN_JMP16_A:
op[2] = 0;
op[3] = 0;
operand = M32C_OPERAND_LAB_8_24;
+ op_base = 0;
+ op_op = 1;
+ rl_addend = 0x41;
break;
case M32C_INSN_JCND32:
op[1] = addend - 1;
operand = M32C_OPERAND_LAB_8_8;
+ op_base = 0;
+ op_op = 1;
+ rl_addend = 0x21;
break;
case -M32C_MACRO_JCND32_W:
operand = M32C_OPERAND_LAB_8_16;
where += 2;
new_insn = M32C_INSN_JMP32_W;
+ op_base = 2;
+ op_op = 3;
+ rl_addend = 0x51;
break;
case -M32C_MACRO_JCND32_A:
operand = M32C_OPERAND_LAB_8_24;
where += 2;
new_insn = M32C_INSN_JMP32_A;
+ op_base = 2;
+ op_op = 3;
+ rl_addend = 0x61;
break;
addend = ((addend-2) & 0x07);
op[0] = 0x4a | (addend & 0x01) | ((addend << 3) & 0x30);
operand = M32C_OPERAND_LAB32_JMP_S;
+ op_base = 0;
+ op_op = 0;
+ rl_addend = 0x10;
break;
case M32C_INSN_JMP32_B:
op[0] = 0xbb;
op[1] = addend - 1;
operand = M32C_OPERAND_LAB_8_8;
+ op_base = 0;
+ op_op = 1;
+ rl_addend = 0x21;
break;
case M32C_INSN_JMP32_W:
op[1] = addend - 1;
op[2] = (addend - 1) >> 8;
operand = M32C_OPERAND_LAB_8_16;
+ op_base = 0;
+ op_op = 1;
+ rl_addend = 0x31;
break;
case M32C_INSN_JMP32_A:
op[2] = 0;
op[3] = 0;
operand = M32C_OPERAND_LAB_8_24;
+ op_base = 0;
+ op_op = 1;
+ rl_addend = 0x41;
+ break;
+
+
+ case M32C_INSN_JSR16_W:
+ op[0] = 0xf5;
+ op[1] = addend - 1;
+ op[2] = (addend - 1) >> 8;
+ operand = M32C_OPERAND_LAB_8_16;
+ op_base = 0;
+ op_op = 1;
+ rl_addend = 0x31;
+ break;
+
+ case M32C_INSN_JSR16_A:
+ op[0] = 0xfd;
+ op[1] = 0;
+ op[2] = 0;
+ op[3] = 0;
+ operand = M32C_OPERAND_LAB_8_24;
+ op_base = 0;
+ op_op = 1;
+ rl_addend = 0x41;
+ break;
+
+ case M32C_INSN_JSR32_W:
+ op[0] = 0xcf;
+ op[1] = addend - 1;
+ op[2] = (addend - 1) >> 8;
+ operand = M32C_OPERAND_LAB_8_16;
+ op_base = 0;
+ op_op = 1;
+ rl_addend = 0x31;
+ break;
+
+ case M32C_INSN_JSR32_A:
+ op[0] = 0xcd;
+ op[1] = 0;
+ op[2] = 0;
+ op[3] = 0;
+ operand = M32C_OPERAND_LAB_8_24;
+ op_base = 0;
+ op_op = 1;
+ rl_addend = 0x41;
+ break;
+
+ case -M32C_MACRO_ADJNZ_2:
+ rl_addend = 0x31;
+ op[2] = addend;
+ operand = M32C_OPERAND_LAB_16_8;
+ break;
+ case -M32C_MACRO_ADJNZ_3:
+ rl_addend = 0x41;
+ op[3] = addend;
+ operand = M32C_OPERAND_LAB_24_8;
+ break;
+ case -M32C_MACRO_ADJNZ_4:
+ rl_addend = 0x51;
+ op[4] = addend;
+ operand = M32C_OPERAND_LAB_32_8;
+ break;
+ case -M32C_MACRO_ADJNZ_5:
+ rl_addend = 0x61;
+ op[5] = addend;
+ operand = M32C_OPERAND_LAB_40_8;
break;
abort();
}
+ if (m32c_relax)
+ {
+ if (operand != M32C_OPERAND_LAB_8_24)
+ fragP->fr_offset = (fragP->fr_address + where);
+
+ fix_new (fragP,
+ rl_where,
+ 0, abs_section_sym, rl_addend, 0,
+ BFD_RELOC_M32C_RL_JUMP);
+ }
+
if (S_GET_SEGMENT (fragP->fr_symbol) != sec
- || operand == M32C_OPERAND_LAB_8_24)
+ || operand == M32C_OPERAND_LAB_8_24
+ || (m32c_relax && (operand != M32C_OPERAND_LAB_5_3
+ && operand != M32C_OPERAND_LAB32_JMP_S)))
{
+ fixS *fixP;
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);
+ fixP = 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);
}
}
\f
} op_reloc_table[] = {
/* PC-REL relocs for 8-bit fields. */
+ { M32C_OPERAND_LAB_8_8, BFD_RELOC_8_PCREL, 1 },
{ M32C_OPERAND_LAB_16_8, BFD_RELOC_8_PCREL, 2 },
{ M32C_OPERAND_LAB_24_8, BFD_RELOC_8_PCREL, 3 },
{ M32C_OPERAND_LAB_32_8, BFD_RELOC_8_PCREL, 4 },
{ M32C_OPERAND_LAB_40_8, BFD_RELOC_8_PCREL, 5 },
+ /* PC-REL relocs for 16-bit fields. */
+ { M32C_OPERAND_LAB_8_16, BFD_RELOC_16_PCREL, 1 },
+
/* Absolute relocs for 8-bit fields. */
{ M32C_OPERAND_IMM_8_QI, BFD_RELOC_8, 1 },
{ M32C_OPERAND_IMM_16_QI, BFD_RELOC_8, 2 },
return BFD_RELOC_NONE;
}
+void
+m32c_cons_fix_new (fragS * frag,
+ int where,
+ int size,
+ expressionS *exp)
+{
+ bfd_reloc_code_real_type type;
+
+ switch (size)
+ {
+ case 1:
+ type = BFD_RELOC_8;
+ break;
+ case 2:
+ type = BFD_RELOC_16;
+ break;
+ case 3:
+ type = BFD_RELOC_24;
+ break;
+ case 4:
+ default:
+ type = BFD_RELOC_32;
+ break;
+ case 8:
+ type = BFD_RELOC_64;
+ break;
+ }
+
+ fix_new_exp (frag, where, (int) size, exp, 0, type);
+}
+
+void
+m32c_apply_fix (struct fix *f, valueT *t, segT s)
+{
+ if (f->fx_r_type == BFD_RELOC_M32C_RL_JUMP
+ || f->fx_r_type == BFD_RELOC_M32C_RL_1ADDR
+ || f->fx_r_type == BFD_RELOC_M32C_RL_2ADDR)
+ return;
+ gas_cgen_md_apply_fix (f, t, s);
+}
+
+arelent *
+tc_gen_reloc (asection *sec, fixS *fx)
+{
+ if (fx->fx_r_type == BFD_RELOC_M32C_RL_JUMP
+ || fx->fx_r_type == BFD_RELOC_M32C_RL_1ADDR
+ || fx->fx_r_type == BFD_RELOC_M32C_RL_2ADDR)
+ {
+ arelent * reloc;
+
+ reloc = xmalloc (sizeof (* reloc));
+
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fx->fx_addsy);
+ reloc->address = fx->fx_frag->fr_address + fx->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fx->fx_r_type);
+ reloc->addend = fx->fx_offset;
+ return reloc;
+
+ }
+ return gas_cgen_tc_gen_reloc (sec, fx);
+}
+
/* 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. */
case M32C_OPERAND_DSP_24_U16:
case M32C_OPERAND_IMM_24_HI:
return 1;
+
+ /* If we're doing linker relaxing, we need to keep all the
+ pc-relative jumps in case we need to fix them due to
+ deleted bytes between the jump and its destination. */
+ case M32C_OPERAND_LAB_8_8:
+ case M32C_OPERAND_LAB_8_16:
+ case M32C_OPERAND_LAB_8_24:
+ case M32C_OPERAND_LAB_16_8:
+ case M32C_OPERAND_LAB_24_8:
+ case M32C_OPERAND_LAB_32_8:
+ case M32C_OPERAND_LAB_40_8:
+ if (m32c_relax)
+ return 1;
+ default:
+ break;
}
}
else
{
- if (fixp->fx_r_type == BFD_RELOC_16)
- return 1;
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_16:
+ return 1;
+
+ case BFD_RELOC_M32C_RL_JUMP:
+ case BFD_RELOC_M32C_RL_1ADDR:
+ case BFD_RELOC_M32C_RL_2ADDR:
+ case BFD_RELOC_8_PCREL:
+ case BFD_RELOC_16_PCREL:
+ if (m32c_relax)
+ return 1;
+ default:
+ break;
+ }
}
return generic_force_reloc (fixp);
if (S_GET_SEGMENT (fixP->fx_addsy)->flags & SEC_MERGE)
return 0;
+ if (m32c_relax)
+ return 0;
+
return 1;
}