/* tc-msp430.c -- Assembler code for the Texas Instruments MSP430
- Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007
+ Free Software Foundation, Inc.
Contributed by Dmitry Diky <diwil@mail.ru>
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)
+ the Free Software Foundation; either version 3, or (at your option)
any later version.
GAS is distributed in the hope that it will be useful,
the Free Software Foundation, 51 Franklin Street - Fifth Floor,
Boston, MA 02110-1301, USA. */
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
#include <limits.h>
#define PUSH_1X_WORKAROUND
#include "subsegs.h"
#include "opcode/msp430.h"
#include "safe-ctype.h"
+#include "dwarf2dbg.h"
+
+/*
+ We will disable polymorphs by default because it is dangerous.
+ The potential problem here is the following: assume we got the
+ following code:
+
+ jump .l1
+ nop
+ jump subroutine ; external symbol
+ .l1:
+ nop
+ ret
+
+ In case of assembly time relaxation we'll get:
+ 0: jmp .l1 <.text +0x08> (reloc deleted)
+ 2: nop
+ 4: br subroutine
+ .l1:
+ 8: nop
+ 10: ret
+
+ If the 'subroutine' wiys thin +-1024 bytes range then linker
+ will produce
+ 0: jmp .text +0x08
+ 2: nop
+ 4: jmp subroutine
+ .l1:
+ 6: nop
+ 8: ret ; 'jmp .text +0x08' will land here. WRONG!!!
+
+
+ The workaround is the following:
+ 1. Declare global var enable_polymorphs which set to 1 via option -mP.
+ 2. Declare global var enable_relax which set to 1 via option -mQ.
+
+ If polymorphs are enabled, and relax isn't, treat all jumps as long jumps,
+ do not delete any relocs and leave them for linker.
+
+ If relax is enabled, relax at assembly time and kill relocs as necessary.
+ */
+
+int msp430_enable_relax;
+int msp430_enable_polys;
/* GCC uses the some condition codes which we'll
implement as new polymorph instructions.
const char comment_chars[] = ";";
const char line_comment_chars[] = "#";
-const char line_separator_chars[] = "";
+const char line_separator_chars[] = "{";
const char EXP_CHARS[] = "eE";
const char FLT_CHARS[] = "dD";
return s;
}
-/* Extract one word from FROM and copy it to TO. Delimeters are ",;\n" */
+/* Extract one word from FROM and copy it to TO. Delimiters are ",;\n" */
static char *
extract_operand (char * from, char * to, int limit)
}
#define OPTION_MMCU 'm'
+#define OPTION_RELAX 'Q'
+#define OPTION_POLYMORPHS 'P'
static void
msp430_set_arch (int dummy ATTRIBUTE_UNUSED)
as_fatal (_("redefinition of mcu type %s' to %s'"),
msp430_mcu->name, mcu_types[i].name);
return 1;
+ break;
+
+ case OPTION_RELAX:
+ msp430_enable_relax = 1;
+ return 1;
+ break;
+
+ case OPTION_POLYMORPHS:
+ msp430_enable_polys = 1;
+ return 1;
+ break;
}
return 0;
struct option md_longopts[] =
{
{"mmcu", required_argument, NULL, OPTION_MMCU},
+ {"mP", no_argument, NULL, OPTION_POLYMORPHS},
+ {"mQ", no_argument, NULL, OPTION_RELAX},
{NULL, no_argument, NULL, 0}
};
" msp430xG437 msp430xG438 msp430G439\n"
" msp430x435 msp430x436 msp430x437\n"
" msp430x447 msp430x448 msp430x449\n"));
+ fprintf (stream,
+ _(" -mQ - enable relaxation at assembly time. DANGEROUS!\n"
+ " -mP - enable polymorph instructions\n"));
show_mcu_list (stream);
}
msp430_operands (struct msp430_opcode_s * opcode, char * line)
{
int bin = opcode->bin_opcode; /* Opcode mask. */
- int __is;
+ int __is = 0;
char l1[MAX_OP_LEN], l2[MAX_OP_LEN];
char *frag;
int where;
__is = 2;
frag = frag_more (__is);
bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (__is);
break;
case 1:
/* Something which works with destination operand. */
frag = frag_more (2 * __is);
where = frag - frag_now->fr_literal;
bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (2 * __is);
if (op1.mode == OP_EXP)
{
frag = frag_more (2 * __is);
where = frag - frag_now->fr_literal;
bfd_putl16 ((bfd_vma) bin, frag);
-
+ dwarf2_emit_insn (2 * __is);
+
if (op1.mode == OP_EXP)
{
where += 2; /* Advance 'where' as we do not know _where_. */
frag = frag_more (2 * __is);
where = frag - frag_now->fr_literal;
bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (2 * __is);
if (op1.mode == OP_EXP)
{
frag = frag_more (2 * __is);
where = frag - frag_now->fr_literal;
bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (2 * __is);
if (op1.mode == OP_EXP)
{
/* reti instruction. */
frag = frag_more (2);
bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (2);
break;
}
frag = frag_more (2 * __is);
where = frag - frag_now->fr_literal;
bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (2 * __is);
if (op1.mode == OP_EXP)
{
else if (*l1 == '$')
{
as_bad (_("instruction requires label sans '$'"));
- break;
}
else
{
as_bad (_
("instruction requires label or value in range -511:512"));
- break;
}
+ dwarf2_emit_insn (2 * __is);
+ break;
}
else
{
break;
case 4: /* Extended jumps. */
+ if (!msp430_enable_polys)
+ {
+ as_bad(_("polymorphs are not enabled. Use -mP option to enable."));
+ break;
+ }
+
line = extract_operand (line, l1, sizeof (l1));
if (l1[0])
{
/* Relaxation required. */
struct rcodes_s rc = msp430_rcodes[opcode->insn_opnumb];
+ /* The parameter to dwarf2_emit_insn is actually the offset to the start
+ of the insn from the fix piece of instruction that was emitted.
+ Since next fragments may have variable size we tie debug info
+ to the beginning of the instruction. */
frag = frag_more (8);
+ dwarf2_emit_insn (0);
bfd_putl16 ((bfd_vma) rc.sop, frag);
frag = frag_variant (rs_machine_dependent, 8, 2,
ENCODE_RELAX (rc.lpos, STATE_BITS10), /* Wild guess. */
break;
case 5: /* Emulated extended branches. */
+ if (!msp430_enable_polys)
+ {
+ as_bad(_("polymorphs are not enabled. Use -mP option to enable."));
+ break;
+ }
line = extract_operand (line, l1, sizeof (l1));
if (l1[0])
{
struct hcodes_s hc = msp430_hcodes[opcode->insn_opnumb];
frag = frag_more (8);
+ dwarf2_emit_insn (0);
bfd_putl16 ((bfd_vma) hc.op0, frag);
bfd_putl16 ((bfd_vma) hc.op1, frag+2);
+
frag = frag_variant (rs_machine_dependent, 8, 2,
ENCODE_RELAX (STATE_EMUL_BRANCH, STATE_BITS10), /* Wild guess. */
exp.X_add_symbol,
return fixp->fx_frag->fr_address + fixp->fx_where;
}
+/* Replaces standard TC_FORCE_RELOCATION_LOCAL.
+ Now it handles the situation when relocations
+ have to be passed to linker. */
+int
+msp430_force_relocation_local(fixS *fixp)
+{
+ if (msp430_enable_polys
+ && !msp430_enable_relax)
+ return 1;
+ else
+ return (!fixp->fx_pcrel
+ || generic_force_reloc(fixp));
+}
+
+
/* GAS will call this for each fixup. It should store the correct
value in the object file. */
-
void
md_apply_fix (fixS * fixp, valueT * valuep, segT seg)
{
}
}
- switch (fixp->fx_r_type)
+ fixp->fx_no_overflow = 1;
+
+ /* if polymorphs are enabled and relax disabled.
+ do not kill any relocs and pass them to linker. */
+ if (msp430_enable_polys
+ && !msp430_enable_relax)
{
- default:
- fixp->fx_no_overflow = 1;
- break;
- case BFD_RELOC_MSP430_10_PCREL:
- break;
+ if (!fixp->fx_addsy || (fixp->fx_addsy
+ && S_GET_SEGMENT (fixp->fx_addsy) == absolute_section))
+ fixp->fx_done = 1; /* it is ok to kill 'abs' reloc */
+ else
+ fixp->fx_done = 0;
}
if (fixp->fx_done)
}
}
-/* A `BFD_ASSEMBLER' GAS will call this to generate a reloc. GAS
- will pass the resulting reloc to `bfd_install_relocation'. This
- currently works poorly, as `bfd_install_relocation' often does the
- wrong thing, and instances of `tc_gen_reloc' have been written to
- work around the problems, which in turns makes it difficult to fix
- `bfd_install_relocation'. */
+/* GAS will call this to generate a reloc, passing the resulting reloc
+ to `bfd_install_relocation'. This currently works poorly, as
+ `bfd_install_relocation' often does the wrong thing, and instances of
+ `tc_gen_reloc' have been written to work around the problems, which
+ in turns makes it difficult to fix `bfd_install_relocation'. */
/* If while processing a fixup, a reloc really needs to be created
then it is done here. */
aim = S_GET_VALUE (symbolP) - fragP->fr_address - fragP->fr_fix;
}
+ if (!msp430_enable_relax)
+ {
+ /* Relaxation is not enabled. So, make all jump as long ones
+ by setting 'aim' to quite high value. */
+ aim = 0x7fff;
+ }
+
this_state = fragP->fr_subtype;
start_type = this_type = table + this_state;
{
/* Look backwards. */
for (next_state = this_type->rlx_more; next_state;)
- if (aim >= this_type->rlx_backward)
+ if (aim >= this_type->rlx_backward || !this_type->rlx_backward)
next_state = 0;
else
{
{
/* Look forwards. */
for (next_state = this_type->rlx_more; next_state;)
- if (aim <= this_type->rlx_forward)
+ if (aim <= this_type->rlx_forward || !this_type->rlx_forward)
next_state = 0;
else
{